Merge Android R
Bug: 168057903 Merged-In: Ice3e441cc9c0df8d0a6acc016bb74375e081bd67 Change-Id: I1d85742f594be2007c99841b290e502b6ede624e
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
/.idea
|
||||
.idea
|
||||
*.iml
|
||||
*.sw*
|
||||
gen/
|
||||
@@ -0,0 +1,12 @@
|
||||
drops {
|
||||
android_build_drop {
|
||||
build_id: "6508977"
|
||||
target: "CtsShim"
|
||||
source_file: "aosp_arm64/CtsShimPriv.apk"
|
||||
}
|
||||
dest_file: "packages/CtsShim/apk//arm/CtsShimPriv.apk"
|
||||
version: ""
|
||||
version_group: ""
|
||||
git_project: "platform/frameworks/base"
|
||||
git_branch: "rvc-dev"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
drops {
|
||||
android_build_drop {
|
||||
build_id: "6508977"
|
||||
target: "CtsShim"
|
||||
source_file: "aosp_arm64/CtsShim.apk"
|
||||
}
|
||||
dest_file: "packages/CtsShim/apk//arm/CtsShim.apk"
|
||||
version: ""
|
||||
version_group: ""
|
||||
git_project: "platform/frameworks/base"
|
||||
git_branch: "rvc-dev"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
drops {
|
||||
android_build_drop {
|
||||
build_id: "6508977"
|
||||
target: "CtsShim"
|
||||
source_file: "aosp_x86_64/CtsShimPriv.apk"
|
||||
}
|
||||
dest_file: "packages/CtsShim/apk//x86/CtsShimPriv.apk"
|
||||
version: ""
|
||||
version_group: ""
|
||||
git_project: "platform/frameworks/base"
|
||||
git_branch: "rvc-dev"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
drops {
|
||||
android_build_drop {
|
||||
build_id: "6508977"
|
||||
target: "CtsShim"
|
||||
source_file: "aosp_x86_64/CtsShim.apk"
|
||||
}
|
||||
dest_file: "packages/CtsShim/apk//x86/CtsShim.apk"
|
||||
version: ""
|
||||
version_group: ""
|
||||
git_project: "platform/frameworks/base"
|
||||
git_branch: "rvc-dev"
|
||||
}
|
||||
571
Android.bp
571
Android.bp
@@ -265,12 +265,20 @@ filegroup {
|
||||
path: "telephony/java",
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "statslog-telephony-common-java-gen",
|
||||
tools: ["stats-log-api-gen"],
|
||||
cmd: "$(location stats-log-api-gen) --java $(out) --module telephony_common"
|
||||
+ " --javaPackage com.android.internal.telephony --javaClass TelephonyCommonStatsLog",
|
||||
out: ["com/android/internal/telephony/TelephonyCommonStatsLog.java"],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-telephony-common-sources",
|
||||
srcs: [
|
||||
"telephony/common/**/*.java",
|
||||
":statslog-telephony-common-java-gen",
|
||||
],
|
||||
path: "telephony/common",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
@@ -282,38 +290,23 @@ filegroup {
|
||||
path: "mms/java",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-wifi-sources",
|
||||
srcs: [
|
||||
"wifi/java/**/*.java",
|
||||
"wifi/java/**/*.aidl",
|
||||
],
|
||||
path: "wifi/java",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-wifi-updatable-exported-aidl-sources",
|
||||
srcs: [
|
||||
"wifi/aidl-export/**/*.aidl",
|
||||
],
|
||||
path: "wifi/aidl-export",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-non-updatable-sources",
|
||||
srcs: [
|
||||
// Java/AIDL sources under frameworks/base
|
||||
":framework-blobstore-sources",
|
||||
":framework-core-sources",
|
||||
":framework-drm-sources",
|
||||
":framework-graphics-sources",
|
||||
":framework-jobscheduler-sources", // jobscheduler is not a module for R
|
||||
":framework-keystore-sources",
|
||||
":framework-identity-sources",
|
||||
":framework-location-sources",
|
||||
":framework-lowpan-sources",
|
||||
":framework-media-sources",
|
||||
":framework-mca-effect-sources",
|
||||
":framework-mca-filterfw-sources",
|
||||
":framework-mca-filterpacks-sources",
|
||||
":framework-media-sources",
|
||||
":framework-mms-sources",
|
||||
":framework-opengl-sources",
|
||||
":framework-rs-sources",
|
||||
@@ -321,7 +314,8 @@ filegroup {
|
||||
":framework-telecomm-sources",
|
||||
":framework-telephony-common-sources",
|
||||
":framework-telephony-sources",
|
||||
":framework-wifi-sources",
|
||||
":framework-wifi-annotations",
|
||||
":framework-wifi-non-updatable-sources",
|
||||
":PacProcessor-aidl-sources",
|
||||
":ProxyHandler-aidl-sources",
|
||||
":net-utils-framework-common-srcs",
|
||||
@@ -344,6 +338,7 @@ filegroup {
|
||||
":libcamera_client_aidl",
|
||||
":libcamera_client_framework_aidl",
|
||||
":libupdate_engine_aidl",
|
||||
":resourcemanager_aidl",
|
||||
":storaged_aidl",
|
||||
":vold_aidl",
|
||||
|
||||
@@ -352,7 +347,6 @@ filegroup {
|
||||
|
||||
// etc.
|
||||
":framework-javastream-protos",
|
||||
":framework-statslog-gen", // StatsLogInternal.java
|
||||
":statslog-framework-java-gen", // FrameworkStatsLog.java
|
||||
],
|
||||
}
|
||||
@@ -360,23 +354,52 @@ filegroup {
|
||||
filegroup {
|
||||
name: "framework-updatable-sources",
|
||||
srcs: [
|
||||
":framework-mediaprovider-sources",
|
||||
":framework-permission-sources",
|
||||
":framework-sdkextensions-sources",
|
||||
":framework-statsd-sources",
|
||||
":framework-tethering-srcs",
|
||||
":framework-wifi-updatable-exported-aidl-sources",
|
||||
":framework-wifi-updatable-sources",
|
||||
":updatable-media-srcs",
|
||||
":ike-srcs",
|
||||
]
|
||||
],
|
||||
visibility: ["//visibility:private"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "framework-updatable-stubs-module_libs_api",
|
||||
static_libs: [
|
||||
"framework-media.stubs.module_lib",
|
||||
"framework-mediaprovider.stubs.module_lib",
|
||||
"framework-permission.stubs.module_lib",
|
||||
"framework-sdkextensions.stubs.module_lib",
|
||||
"framework-statsd.stubs.module_lib",
|
||||
"framework-tethering.stubs.module_lib",
|
||||
"updatable_media_stubs",
|
||||
"framework-wifi.stubs.module_lib",
|
||||
],
|
||||
sdk_version: "module_current",
|
||||
visibility: [":__pkg__"],
|
||||
visibility: ["//visibility:private"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "framework-all",
|
||||
installable: false,
|
||||
static_libs: [
|
||||
"framework-minus-apex",
|
||||
"framework-mediaprovider.impl",
|
||||
"framework-permission.impl",
|
||||
"framework-sdkextensions.impl",
|
||||
"framework-statsd.impl",
|
||||
"framework-tethering.impl",
|
||||
"framework-wifi.impl",
|
||||
"updatable-media",
|
||||
],
|
||||
apex_available: ["//apex_available:platform"],
|
||||
sdk_version: "core_platform",
|
||||
visibility: [
|
||||
// DO NOT ADD ANY MORE ENTRIES TO THIS LIST
|
||||
"//external/robolectric-shadows:__subpackages__",
|
||||
"//frameworks/layoutlib:__subpackages__",
|
||||
],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
@@ -388,6 +411,8 @@ filegroup {
|
||||
],
|
||||
}
|
||||
|
||||
// AIDL files under these paths are mixture of public and private ones.
|
||||
// They shouldn't be exported across module boundaries.
|
||||
java_defaults {
|
||||
name: "framework-aidl-export-defaults",
|
||||
aidl: {
|
||||
@@ -400,7 +425,6 @@ java_defaults {
|
||||
"location/java",
|
||||
"lowpan/java",
|
||||
"media/java",
|
||||
"media/apex/java",
|
||||
"media/mca/effect/java",
|
||||
"media/mca/filterfw/java",
|
||||
"media/mca/filterpacks/java",
|
||||
@@ -409,9 +433,8 @@ java_defaults {
|
||||
"rs/java",
|
||||
"sax/java",
|
||||
"telecomm/java",
|
||||
"wifi/java",
|
||||
"wifi/aidl-export",
|
||||
|
||||
"apex/media/aidl/stable",
|
||||
// TODO(b/147699819): remove this
|
||||
"telephony/java",
|
||||
],
|
||||
@@ -428,9 +451,13 @@ java_library {
|
||||
"framework-protos",
|
||||
"game-driver-protos",
|
||||
"android.hidl.base-V1.0-java",
|
||||
"android.hardware.cas-V1.1-java",
|
||||
"android.hardware.cas-V1.0-java",
|
||||
"android.hardware.cas-V1.1-java",
|
||||
"android.hardware.cas-V1.2-java",
|
||||
"android.hardware.contexthub-V1.0-java",
|
||||
"android.hardware.contexthub-V1.1-java",
|
||||
"android.hardware.gnss-V1.0-java",
|
||||
"android.hardware.gnss-V2.1-java",
|
||||
"android.hardware.health-V1.0-java-constants",
|
||||
"android.hardware.radio-V1.0-java",
|
||||
"android.hardware.radio-V1.1-java",
|
||||
@@ -443,6 +470,7 @@ java_library {
|
||||
"android.hardware.thermal-V1.1-java",
|
||||
"android.hardware.thermal-V2.0-java",
|
||||
"android.hardware.tv.input-V1.0-java-constants",
|
||||
"android.hardware.tv.tuner-V1.0-java-constants",
|
||||
"android.hardware.usb-V1.0-java-constants",
|
||||
"android.hardware.usb-V1.1-java-constants",
|
||||
"android.hardware.usb-V1.2-java-constants",
|
||||
@@ -451,7 +479,6 @@ java_library {
|
||||
"android.hardware.vibrator-V1.1-java",
|
||||
"android.hardware.vibrator-V1.2-java",
|
||||
"android.hardware.vibrator-V1.3-java",
|
||||
"android.hardware.wifi-V1.0-java-constants",
|
||||
"devicepolicyprotosnano",
|
||||
|
||||
"com.android.sysprop.apex",
|
||||
@@ -463,72 +490,21 @@ java_library {
|
||||
installable: false,
|
||||
}
|
||||
|
||||
java_defaults {
|
||||
name: "framework-defaults",
|
||||
defaults: ["framework-aidl-export-defaults"],
|
||||
installable: true,
|
||||
|
||||
aidl: {
|
||||
generate_get_transaction_name: true,
|
||||
},
|
||||
|
||||
srcs: ["core/java/**/*.logtags"],
|
||||
|
||||
exclude_srcs: [
|
||||
// See comment on framework-atb-backward-compatibility module below
|
||||
"core/java/android/content/pm/AndroidTestBaseUpdater.java",
|
||||
],
|
||||
|
||||
sdk_version: "core_platform",
|
||||
libs: [
|
||||
"app-compat-annotations",
|
||||
"ext",
|
||||
"unsupportedappusage",
|
||||
],
|
||||
|
||||
jarjar_rules: ":framework-jarjar-rules",
|
||||
|
||||
static_libs: [
|
||||
"framework-internal-utils",
|
||||
],
|
||||
|
||||
required: [
|
||||
// TODO: remove gps_debug when the build system propagates "required" properly.
|
||||
"gps_debug.conf",
|
||||
],
|
||||
|
||||
dxflags: [
|
||||
"--core-library",
|
||||
"--multi-dex",
|
||||
],
|
||||
|
||||
plugins: ["view-inspector-annotation-processor"],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-jarjar-rules",
|
||||
srcs: ["jarjar_rules_hidl.txt"],
|
||||
srcs: ["framework-jarjar-rules.txt"],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "libincident_aidl",
|
||||
srcs: [
|
||||
"core/java/android/os/IIncidentDumpCallback.aidl",
|
||||
"core/java/android/os/IIncidentManager.aidl",
|
||||
"core/java/android/os/IIncidentReportStatusListener.aidl",
|
||||
],
|
||||
path: "core/java",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "statsd_aidl",
|
||||
srcs: [
|
||||
"core/java/android/os/IStatsCompanionService.aidl",
|
||||
"core/java/android/os/IStatsManager.aidl",
|
||||
"core/java/android/os/IStatsPullerCallback.aidl",
|
||||
],
|
||||
path: "core/java",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "libvibrator_aidl",
|
||||
srcs: [
|
||||
@@ -538,19 +514,59 @@ filegroup {
|
||||
path: "core/java",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "libpowermanager_aidl",
|
||||
srcs: [
|
||||
"core/java/android/os/Temperature.aidl",
|
||||
"core/java/android/os/CoolingDevice.aidl",
|
||||
"core/java/android/os/IThermalEventListener.aidl",
|
||||
"core/java/android/os/IThermalStatusListener.aidl",
|
||||
"core/java/android/os/IThermalService.aidl",
|
||||
],
|
||||
path: "core/java",
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "framework-minus-apex",
|
||||
defaults: ["framework-defaults"],
|
||||
srcs: [":framework-non-updatable-sources"],
|
||||
defaults: ["framework-aidl-export-defaults"],
|
||||
srcs: [
|
||||
":framework-non-updatable-sources",
|
||||
"core/java/**/*.logtags",
|
||||
],
|
||||
// See comment on framework-atb-backward-compatibility module below
|
||||
exclude_srcs: ["core/java/android/content/pm/AndroidTestBaseUpdater.java"],
|
||||
aidl: {
|
||||
generate_get_transaction_name: true,
|
||||
},
|
||||
dxflags: [
|
||||
"--core-library",
|
||||
"--multi-dex",
|
||||
],
|
||||
installable: true,
|
||||
jarjar_rules: ":framework-jarjar-rules",
|
||||
javac_shard_size: 150,
|
||||
plugins: [
|
||||
"view-inspector-annotation-processor",
|
||||
"staledataclass-annotation-processor",
|
||||
],
|
||||
required: [
|
||||
"framework-platform-compat-config",
|
||||
// TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
|
||||
"gps_debug.conf",
|
||||
"libcore-platform-compat-config",
|
||||
"protolog.conf.json.gz",
|
||||
"services-platform-compat-config",
|
||||
"documents-ui-compat-config",
|
||||
],
|
||||
libs: ["framework-updatable-stubs-module_libs_api"],
|
||||
libs: [
|
||||
"app-compat-annotations",
|
||||
"ext",
|
||||
"framework-updatable-stubs-module_libs_api",
|
||||
"unsupportedappusage",
|
||||
],
|
||||
sdk_version: "core_platform",
|
||||
static_libs: [
|
||||
"framework-internal-utils",
|
||||
// If MimeMap ever becomes its own APEX, then this dependency would need to be removed
|
||||
// in favor of an API stubs dependency in java_library "framework" below.
|
||||
"mimemap",
|
||||
@@ -559,7 +575,10 @@ java_library {
|
||||
stem: "framework",
|
||||
apex_available: ["//apex_available:platform"],
|
||||
visibility: [
|
||||
"//frameworks/base",
|
||||
// TODO(b/147128803) remove the below lines
|
||||
"//frameworks/base/apex/blobstore/framework",
|
||||
"//frameworks/base/apex/jobscheduler/framework",
|
||||
"//frameworks/base/packages/Tethering/tests/unit",
|
||||
],
|
||||
}
|
||||
@@ -578,6 +597,7 @@ java_library {
|
||||
defaults: ["framework-aidl-export-defaults"],
|
||||
installable: false, // this lib is a build-only library
|
||||
static_libs: [
|
||||
"app-compat-annotations",
|
||||
"framework-minus-apex",
|
||||
"framework-updatable-stubs-module_libs_api",
|
||||
],
|
||||
@@ -585,27 +605,9 @@ java_library {
|
||||
apex_available: ["//apex_available:platform"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "framework-all",
|
||||
defaults: ["framework-defaults"],
|
||||
srcs: [":framework-all-sources"],
|
||||
installable: false,
|
||||
static_libs: [
|
||||
// Additional dependencies needed to build the ike API classes.
|
||||
"ike-internals",
|
||||
],
|
||||
apex_available: ["//apex_available:platform"],
|
||||
visibility: [
|
||||
// DO NOT ADD ANY MORE ENTRIES TO THIS LIST
|
||||
"//external/robolectric-shadows:__subpackages__",
|
||||
"//frameworks/base",
|
||||
"//frameworks/layoutlib:__subpackages__",
|
||||
],
|
||||
}
|
||||
|
||||
platform_compat_config {
|
||||
name: "framework-platform-compat-config",
|
||||
src: ":framework-minus-apex",
|
||||
name: "framework-platform-compat-config",
|
||||
src: ":framework-minus-apex",
|
||||
}
|
||||
|
||||
// A temporary build target that is conditionally included on the bootclasspath if
|
||||
@@ -622,13 +624,6 @@ java_library {
|
||||
],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "framework-statslog-gen",
|
||||
tools: ["stats-log-api-gen"],
|
||||
cmd: "$(location stats-log-api-gen) --java $(out) --worksource",
|
||||
out: ["android/util/StatsLogInternal.java"],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "statslog-framework-java-gen",
|
||||
tools: ["stats-log-api-gen"],
|
||||
@@ -637,6 +632,18 @@ genrule {
|
||||
out: ["com/android/internal/util/FrameworkStatsLog.java"],
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "uieventloggerlib",
|
||||
srcs: [
|
||||
"core/java/com/android/internal/logging/UiEvent.java",
|
||||
"core/java/com/android/internal/logging/UiEventLogger.java",
|
||||
"core/java/com/android/internal/logging/UiEventLoggerImpl.java",
|
||||
"core/java/com/android/internal/logging/InstanceId.java",
|
||||
"core/java/com/android/internal/logging/InstanceIdSequence.java",
|
||||
":statslog-framework-java-gen",
|
||||
],
|
||||
}
|
||||
|
||||
gensrcs {
|
||||
name: "framework-javastream-protos",
|
||||
depfile: true,
|
||||
@@ -658,25 +665,62 @@ gensrcs {
|
||||
"&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
|
||||
|
||||
srcs: [
|
||||
":ipconnectivity-proto-src",
|
||||
"core/proto/**/*.proto",
|
||||
"libs/incident/**/*.proto",
|
||||
],
|
||||
output_extension: "srcjar",
|
||||
}
|
||||
|
||||
gensrcs {
|
||||
name: "framework-cppstream-protos",
|
||||
depfile: true,
|
||||
|
||||
tools: [
|
||||
"aprotoc",
|
||||
"protoc-gen-cppstream",
|
||||
],
|
||||
|
||||
cmd: "mkdir -p $(genDir) " +
|
||||
"&& $(location aprotoc) " +
|
||||
" --plugin=$(location protoc-gen-cppstream) " +
|
||||
" --dependency_out=$(depfile) " +
|
||||
" --cppstream_out=$(genDir) " +
|
||||
" -Iexternal/protobuf/src " +
|
||||
" -I . " +
|
||||
" $(in)",
|
||||
|
||||
srcs: [
|
||||
":ipconnectivity-proto-src",
|
||||
"core/proto/**/*.proto",
|
||||
"libs/incident/**/*.proto",
|
||||
],
|
||||
|
||||
output_extension: "proto.h",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-annotations",
|
||||
srcs: [
|
||||
"core/java/android/annotation/CallbackExecutor.java",
|
||||
"core/java/android/annotation/CheckResult.java",
|
||||
"core/java/android/annotation/CurrentTimeMillisLong.java",
|
||||
"core/java/android/annotation/Hide.java",
|
||||
"core/java/android/annotation/NonNull.java",
|
||||
"core/java/android/annotation/Nullable.java",
|
||||
"core/java/android/annotation/IntDef.java",
|
||||
"core/java/android/annotation/IntRange.java",
|
||||
"core/java/android/annotation/LongDef.java",
|
||||
"core/java/android/annotation/NonNull.java",
|
||||
"core/java/android/annotation/Nullable.java",
|
||||
"core/java/android/annotation/RequiresPermission.java",
|
||||
"core/java/android/annotation/SdkConstant.java",
|
||||
"core/java/android/annotation/StringDef.java",
|
||||
"core/java/android/annotation/SystemApi.java",
|
||||
"core/java/android/annotation/SystemService.java",
|
||||
"core/java/android/annotation/TestApi.java",
|
||||
"core/java/android/annotation/WorkerThread.java",
|
||||
"core/java/com/android/internal/annotations/GuardedBy.java",
|
||||
"core/java/com/android/internal/annotations/VisibleForTesting.java",
|
||||
"core/java/com/android/internal/annotations/Immutable.java",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -721,24 +765,6 @@ filegroup {
|
||||
],
|
||||
}
|
||||
|
||||
// utility classes statically linked into framework-wifi and dynamically linked
|
||||
// into wifi-service
|
||||
java_library {
|
||||
name: "framework-wifi-util-lib",
|
||||
sdk_version: "module_current",
|
||||
srcs: [
|
||||
"core/java/com/android/internal/util/Preconditions.java",
|
||||
],
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
"unsupportedappusage",
|
||||
],
|
||||
visibility: [
|
||||
"//frameworks/base/wifi",
|
||||
"//frameworks/base/services/net",
|
||||
],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-services-net-module-wifi-shared-srcs",
|
||||
srcs: [
|
||||
@@ -772,10 +798,6 @@ java_library {
|
||||
"libphonenumber-platform",
|
||||
"tagsoup",
|
||||
"rappor",
|
||||
"libtextclassifier-java",
|
||||
],
|
||||
required: [
|
||||
"libtextclassifier",
|
||||
],
|
||||
dxflags: ["--core-library"],
|
||||
}
|
||||
@@ -784,6 +806,7 @@ java_library {
|
||||
java_library_host {
|
||||
name: "platformprotos",
|
||||
srcs: [
|
||||
":ipconnectivity-proto-src",
|
||||
"cmds/am/proto/instrumentation_data.proto",
|
||||
"cmds/statsd/src/**/*.proto",
|
||||
"core/proto/**/*.proto",
|
||||
@@ -812,6 +835,7 @@ java_library {
|
||||
],
|
||||
sdk_version: "9",
|
||||
srcs: [
|
||||
":ipconnectivity-proto-src",
|
||||
"core/proto/**/*.proto",
|
||||
"libs/incident/proto/android/os/**/*.proto",
|
||||
],
|
||||
@@ -826,6 +850,7 @@ java_library {
|
||||
},
|
||||
|
||||
srcs: [
|
||||
":ipconnectivity-proto-src",
|
||||
"core/proto/**/*.proto",
|
||||
"libs/incident/proto/android/os/**/*.proto",
|
||||
],
|
||||
@@ -856,6 +881,7 @@ cc_defaults {
|
||||
],
|
||||
|
||||
srcs: [
|
||||
":ipconnectivity-proto-src",
|
||||
"core/proto/**/*.proto",
|
||||
],
|
||||
}
|
||||
@@ -936,11 +962,39 @@ cc_library {
|
||||
filegroup {
|
||||
name: "incremental_aidl",
|
||||
srcs: [
|
||||
"core/java/android/os/incremental/IIncrementalServiceConnector.aidl",
|
||||
"core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl",
|
||||
],
|
||||
path: "core/java",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "dataloader_aidl",
|
||||
srcs: [
|
||||
"core/java/android/content/pm/DataLoaderParamsParcel.aidl",
|
||||
"core/java/android/content/pm/DataLoaderType.aidl",
|
||||
"core/java/android/content/pm/FileSystemControlParcel.aidl",
|
||||
"core/java/android/content/pm/IDataLoader.aidl",
|
||||
"core/java/android/content/pm/IDataLoaderManager.aidl",
|
||||
"core/java/android/content/pm/InstallationFileParcel.aidl",
|
||||
"core/java/android/content/pm/InstallationFileLocation.aidl",
|
||||
"core/java/android/content/pm/IDataLoaderStatusListener.aidl",
|
||||
"core/java/android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl",
|
||||
],
|
||||
path: "core/java",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "incremental_manager_aidl",
|
||||
srcs: [
|
||||
"core/java/android/os/incremental/IIncrementalService.aidl",
|
||||
"core/java/android/os/incremental/IncrementalNewFileParams.aidl",
|
||||
"core/java/android/os/incremental/IStorageHealthListener.aidl",
|
||||
"core/java/android/os/incremental/StorageHealthCheckParams.aidl",
|
||||
],
|
||||
path: "core/java",
|
||||
}
|
||||
|
||||
aidl_interface {
|
||||
name: "libincremental_aidl",
|
||||
unstable: true,
|
||||
@@ -960,41 +1014,49 @@ aidl_interface {
|
||||
},
|
||||
}
|
||||
|
||||
gensrcs {
|
||||
name: "gen-platform-proto-constants",
|
||||
depfile: true,
|
||||
|
||||
tools: [
|
||||
"aprotoc",
|
||||
"protoc-gen-cppstream",
|
||||
],
|
||||
|
||||
aidl_interface {
|
||||
name: "libdataloader_aidl",
|
||||
unstable: true,
|
||||
srcs: [
|
||||
"core/proto/android/os/backtrace.proto",
|
||||
"core/proto/android/os/batterytype.proto",
|
||||
"core/proto/android/os/cpufreq.proto",
|
||||
"core/proto/android/os/cpuinfo.proto",
|
||||
"core/proto/android/os/data.proto",
|
||||
"core/proto/android/os/kernelwake.proto",
|
||||
"core/proto/android/os/pagetypeinfo.proto",
|
||||
"core/proto/android/os/procrank.proto",
|
||||
"core/proto/android/os/ps.proto",
|
||||
"core/proto/android/os/system_properties.proto",
|
||||
"core/proto/android/util/event_log_tags.proto",
|
||||
"core/proto/android/util/log.proto",
|
||||
":dataloader_aidl",
|
||||
],
|
||||
imports: [
|
||||
"libincremental_aidl",
|
||||
],
|
||||
backend: {
|
||||
java: {
|
||||
sdk_version: "28",
|
||||
},
|
||||
cpp: {
|
||||
enabled: true,
|
||||
},
|
||||
ndk: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
|
||||
cmd: "mkdir -p $(genDir) " +
|
||||
"&& $(location aprotoc) " +
|
||||
" --plugin=$(location protoc-gen-cppstream) " +
|
||||
" --dependency_out=$(depfile) " +
|
||||
" --cppstream_out=$(genDir) " +
|
||||
" -Iexternal/protobuf/src " +
|
||||
" -I . " +
|
||||
" $(in)",
|
||||
|
||||
output_extension: "proto.h",
|
||||
aidl_interface {
|
||||
name: "libincremental_manager_aidl",
|
||||
unstable: true,
|
||||
srcs: [
|
||||
":incremental_manager_aidl",
|
||||
],
|
||||
imports: [
|
||||
"libincremental_aidl",
|
||||
"libdataloader_aidl",
|
||||
],
|
||||
backend: {
|
||||
java: {
|
||||
sdk_version: "28",
|
||||
},
|
||||
cpp: {
|
||||
enabled: true,
|
||||
},
|
||||
ndk: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// TODO(b/77285514): remove this once the last few hidl interfaces have been
|
||||
@@ -1058,19 +1120,22 @@ python_binary_host {
|
||||
filegroup {
|
||||
name: "framework-media-annotation-srcs",
|
||||
srcs: [
|
||||
":framework-annotations",
|
||||
"core/java/android/annotation/CallbackExecutor.java",
|
||||
"core/java/android/annotation/CallSuper.java",
|
||||
"core/java/android/annotation/DrawableRes.java",
|
||||
"core/java/android/annotation/IntDef.java",
|
||||
"core/java/android/annotation/LongDef.java",
|
||||
"core/java/android/annotation/NonNull.java",
|
||||
"core/java/android/annotation/Nullable.java",
|
||||
"core/java/android/annotation/RequiresPermission.java",
|
||||
"core/java/android/annotation/SdkConstant.java",
|
||||
"core/java/android/annotation/StringDef.java",
|
||||
"core/java/android/annotation/SystemApi.java",
|
||||
"core/java/android/annotation/TestApi.java",
|
||||
"core/java/com/android/internal/annotations/GuardedBy.java",
|
||||
],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-mediaprovider-annotation-sources",
|
||||
srcs: [
|
||||
":framework-annotations",
|
||||
"core/java/android/annotation/BytesLong.java",
|
||||
"core/java/android/annotation/CurrentTimeSecondsLong.java",
|
||||
"core/java/android/annotation/DurationMillisLong.java",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1082,24 +1147,13 @@ aidl_mapping {
|
||||
output: "framework-aidl-mappings.txt",
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-cellbroadcast-shared-srcs",
|
||||
srcs: [
|
||||
"core/java/android/os/HandlerExecutor.java",
|
||||
"core/java/android/util/LocalLog.java",
|
||||
"core/java/android/util/Slog.java",
|
||||
"core/java/com/android/internal/util/IState.java",
|
||||
"core/java/com/android/internal/util/Preconditions.java",
|
||||
"core/java/com/android/internal/util/State.java",
|
||||
"core/java/com/android/internal/util/StateMachine.java",
|
||||
],
|
||||
}
|
||||
|
||||
// Avoid including Parcelable classes as we don't want to have two copies of
|
||||
// Parcelable cross the process.
|
||||
// Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony)
|
||||
// and TeleService app (packages/services/Telephony).
|
||||
filegroup {
|
||||
name: "framework-telephony-stack-shared-srcs",
|
||||
name: "framework-telephony-common-shared-srcs",
|
||||
srcs: [
|
||||
"core/java/android/os/BasicShellCommandHandler.java",
|
||||
"core/java/android/os/RegistrantList.java",
|
||||
"core/java/android/os/Registrant.java",
|
||||
"core/java/android/util/LocalLog.java",
|
||||
@@ -1119,6 +1173,97 @@ filegroup {
|
||||
],
|
||||
}
|
||||
|
||||
// Avoid including Parcelable classes as we don't want to have two copies of
|
||||
// Parcelable cross the process. This is used by framework-telephony (frameworks/base/telephony).
|
||||
filegroup {
|
||||
name: "framework-telephony-shared-srcs",
|
||||
srcs: [
|
||||
"core/java/android/util/RecurrenceRule.java",
|
||||
"core/java/com/android/internal/os/SomeArgs.java",
|
||||
"core/java/com/android/internal/util/BitwiseInputStream.java",
|
||||
"core/java/com/android/internal/util/BitwiseOutputStream.java",
|
||||
"core/java/com/android/internal/util/FunctionalUtils.java",
|
||||
"core/java/com/android/internal/util/HexDump.java",
|
||||
"core/java/com/android/internal/util/IndentingPrintWriter.java",
|
||||
"core/java/com/android/internal/util/Preconditions.java",
|
||||
],
|
||||
}
|
||||
|
||||
// Avoid including Parcelable classes as we don't want to have two copies of
|
||||
// Parcelable cross the process.
|
||||
filegroup {
|
||||
name: "framework-cellbroadcast-shared-srcs",
|
||||
srcs: [
|
||||
"core/java/android/os/HandlerExecutor.java",
|
||||
"core/java/android/util/LocalLog.java",
|
||||
"core/java/com/android/internal/util/IState.java",
|
||||
"core/java/com/android/internal/util/Preconditions.java",
|
||||
"core/java/com/android/internal/util/State.java",
|
||||
"core/java/com/android/internal/util/StateMachine.java",
|
||||
],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-ims-common-shared-srcs",
|
||||
srcs: [
|
||||
"core/java/android/os/RegistrantList.java",
|
||||
"core/java/android/os/Registrant.java",
|
||||
"core/java/com/android/internal/os/SomeArgs.java",
|
||||
"core/java/com/android/internal/util/Preconditions.java",
|
||||
],
|
||||
}
|
||||
|
||||
// utility classes statically linked into framework-wifi and dynamically linked
|
||||
// into wifi-service
|
||||
java_library {
|
||||
name: "framework-wifi-util-lib",
|
||||
sdk_version: "module_current",
|
||||
srcs: [
|
||||
"core/java/android/content/pm/BaseParceledListSlice.java",
|
||||
"core/java/android/content/pm/ParceledListSlice.java",
|
||||
"core/java/android/os/HandlerExecutor.java",
|
||||
"core/java/com/android/internal/util/AsyncChannel.java",
|
||||
"core/java/com/android/internal/util/AsyncService.java",
|
||||
"core/java/com/android/internal/util/Protocol.java",
|
||||
"core/java/com/android/internal/util/Preconditions.java",
|
||||
"telephony/java/android/telephony/Annotation.java",
|
||||
":net-utils-framework-wifi-common-srcs",
|
||||
],
|
||||
libs: [
|
||||
"framework-annotations-lib",
|
||||
"unsupportedappusage",
|
||||
],
|
||||
visibility: [
|
||||
"//frameworks/base/wifi",
|
||||
"//frameworks/base/services/net",
|
||||
],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-wifi-util-lib-aidls",
|
||||
srcs: ["core/java/android/content/pm/ParceledListSlice.aidl"],
|
||||
path: "core/java",
|
||||
}
|
||||
|
||||
// utility classes statically linked into wifi-service
|
||||
filegroup {
|
||||
name: "framework-wifi-service-shared-srcs",
|
||||
srcs: [
|
||||
"core/java/android/net/InterfaceConfiguration.java",
|
||||
"core/java/android/os/BasicShellCommandHandler.java",
|
||||
"core/java/android/util/BackupUtils.java",
|
||||
"core/java/android/util/Rational.java",
|
||||
"core/java/com/android/internal/util/FastXmlSerializer.java",
|
||||
"core/java/com/android/internal/util/HexDump.java",
|
||||
"core/java/com/android/internal/util/IState.java",
|
||||
"core/java/com/android/internal/util/MessageUtils.java",
|
||||
"core/java/com/android/internal/util/State.java",
|
||||
"core/java/com/android/internal/util/StateMachine.java",
|
||||
"core/java/com/android/internal/util/WakeupMessage.java",
|
||||
],
|
||||
visibility: ["//frameworks/opt/net/wifi/service"],
|
||||
}
|
||||
|
||||
// TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
|
||||
metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " +
|
||||
"--ignore-classes-on-classpath " +
|
||||
@@ -1140,3 +1285,43 @@ build = [
|
||||
"StubLibraries.bp",
|
||||
"ApiDocs.bp",
|
||||
]
|
||||
|
||||
java_library {
|
||||
name: "framework-telephony",
|
||||
srcs: [
|
||||
//":framework-telephony-sources",
|
||||
//":framework-telephony-shared-srcs",
|
||||
],
|
||||
// TODO: change to framework-system-stub to build against system APIs.
|
||||
libs: [
|
||||
"framework-minus-apex",
|
||||
"unsupportedappusage",
|
||||
],
|
||||
static_libs: [
|
||||
"libphonenumber-platform",
|
||||
"app-compat-annotations",
|
||||
],
|
||||
sdk_version: "core_platform",
|
||||
aidl: {
|
||||
export_include_dirs: ["telephony/java"],
|
||||
include_dirs: [
|
||||
"frameworks/native/aidl/binder",
|
||||
"frameworks/native/aidl/gui",
|
||||
]
|
||||
},
|
||||
jarjar_rules: ":framework-telephony-jarjar-rules",
|
||||
dxflags: [
|
||||
"--core-library",
|
||||
"--multi-dex",
|
||||
],
|
||||
// This is to break the dependency from boot jars.
|
||||
dex_preopt: {
|
||||
enabled: false,
|
||||
},
|
||||
installable: true,
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "framework-telephony-jarjar-rules",
|
||||
srcs: ["telephony/framework-telephony-jarjar-rules.txt"],
|
||||
}
|
||||
|
||||
@@ -255,7 +255,13 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/DefaultContainerSer
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/CaptivePortalLogin)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/ext.jar)
|
||||
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/google/android/mms)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*-service.jar)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/service-statsd.jar)
|
||||
$(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/.intermediates/frameworks/base/libincremental_aidl-cpp-source/)
|
||||
$(call add-clean-step, rm -rf $(SOONG_OUT_DIR)/.intermediates/frameworks/base/libincremental_manager_aidl-cpp-source/)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/InProcessTethering)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/OsuLogin)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other/system/app/OsuLogin)
|
||||
# ******************************************************************
|
||||
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
|
||||
# ******************************************************************
|
||||
|
||||
@@ -9,6 +9,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
|
||||
core/jni/
|
||||
libs/input/
|
||||
services/core/jni/
|
||||
services/incremental/
|
||||
|
||||
[Hook Scripts]
|
||||
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
|
||||
|
||||
@@ -51,6 +51,10 @@ stubs_defaults {
|
||||
":android_icu4j_public_api_files",
|
||||
"**/package.html",
|
||||
],
|
||||
// TODO(b/147699819): remove below aidl includes.
|
||||
aidl: {
|
||||
local_include_dirs: ["telephony/java"],
|
||||
},
|
||||
libs: ["framework-internal-utils"],
|
||||
installable: false,
|
||||
annotations_enabled: true,
|
||||
@@ -81,12 +85,9 @@ stubs_defaults {
|
||||
// finalized stub library to resolve them. If a new class gets added,
|
||||
// this may be need to be revisited to use a manually maintained stub
|
||||
// library with empty classes in order to resolve those references.
|
||||
libs: ["sdk_system_29_android"],
|
||||
libs: ["sdk_system_30_android"],
|
||||
aidl: {
|
||||
local_include_dirs: [
|
||||
"media/apex/java",
|
||||
"wifi/aidl-export",
|
||||
],
|
||||
local_include_dirs: ["apex/media/aidl/stable"],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -112,7 +113,7 @@ droidstubs {
|
||||
},
|
||||
last_released: {
|
||||
api_file: ":android.api.public.latest",
|
||||
removed_api_file: "api/removed.txt",
|
||||
removed_api_file: ":removed.api.public.latest",
|
||||
baseline_file: ":public-api-incompatibilities-with-last-released",
|
||||
},
|
||||
api_lint: {
|
||||
@@ -139,6 +140,10 @@ droidstubs {
|
||||
api_file: "non-updatable-api/current.txt",
|
||||
removed_api_file: "non-updatable-api/removed.txt",
|
||||
},
|
||||
api_lint: {
|
||||
enabled: true,
|
||||
new_since: ":android-non-updatable.api.public.latest",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -170,7 +175,7 @@ droidstubs {
|
||||
},
|
||||
last_released: {
|
||||
api_file: ":android.api.system.latest",
|
||||
removed_api_file: "api/system-removed.txt",
|
||||
removed_api_file: ":removed.api.system.latest",
|
||||
baseline_file: ":system-api-incompatibilities-with-last-released"
|
||||
},
|
||||
api_lint: {
|
||||
@@ -197,6 +202,11 @@ droidstubs {
|
||||
api_file: "non-updatable-api/system-current.txt",
|
||||
removed_api_file: "non-updatable-api/system-removed.txt",
|
||||
},
|
||||
api_lint: {
|
||||
enabled: true,
|
||||
new_since: ":android-non-updatable.api.system.latest",
|
||||
baseline_file: "non-updatable-api/system-lint-baseline.txt",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -245,7 +255,7 @@ droidstubs {
|
||||
},
|
||||
last_released: {
|
||||
api_file: ":android.api.module-lib.latest",
|
||||
removed_api_file: "api/module-lib-removed.txt",
|
||||
removed_api_file: ":removed.api.module-lib.latest",
|
||||
baseline_file: ":module-lib-api-incompatibilities-with-last-released"
|
||||
},
|
||||
api_lint: {
|
||||
@@ -271,6 +281,10 @@ droidstubs {
|
||||
api_file: "non-updatable-api/module-lib-current.txt",
|
||||
removed_api_file: "non-updatable-api/module-lib-removed.txt",
|
||||
},
|
||||
api_lint: {
|
||||
enabled: true,
|
||||
new_since: ":android-non-updatable.api.module-lib.latest",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -283,8 +297,6 @@ java_defaults {
|
||||
name: "android_defaults_stubs_current",
|
||||
libs: [ "stub-annotations" ],
|
||||
static_libs: [
|
||||
"private-stub-annotations-jar",
|
||||
|
||||
// License notices from art module
|
||||
"art-notices-for-framework-stubs-jar",
|
||||
"framework-res-package-jar", // Export package of framework-res
|
||||
@@ -312,6 +324,7 @@ java_defaults {
|
||||
java_library_static {
|
||||
name: "android_monolith_stubs_current",
|
||||
srcs: [ ":api-stubs-docs" ],
|
||||
static_libs: [ "private-stub-annotations-jar" ],
|
||||
defaults: ["android_defaults_stubs_current"],
|
||||
}
|
||||
|
||||
@@ -320,9 +333,14 @@ java_library_static {
|
||||
srcs: [ ":api-stubs-docs-non-updatable" ],
|
||||
static_libs: [
|
||||
"conscrypt.module.public.api.stubs",
|
||||
"framework-media.stubs",
|
||||
"framework-mediaprovider.stubs",
|
||||
"framework-permission.stubs",
|
||||
"framework-sdkextensions.stubs",
|
||||
"framework-statsd.stubs",
|
||||
"framework-tethering.stubs",
|
||||
"updatable_media_stubs.public",
|
||||
"framework-wifi.stubs",
|
||||
"private-stub-annotations-jar",
|
||||
],
|
||||
defaults: ["android_defaults_stubs_current"],
|
||||
}
|
||||
@@ -336,6 +354,7 @@ java_library_static {
|
||||
java_library_static {
|
||||
name: "android_system_monolith_stubs_current",
|
||||
srcs: [ ":system-api-stubs-docs" ],
|
||||
static_libs: [ "private-stub-annotations-jar" ],
|
||||
defaults: [
|
||||
"android_defaults_stubs_current",
|
||||
"android_stubs_dists_default",
|
||||
@@ -358,9 +377,14 @@ java_library_static {
|
||||
srcs: [ ":system-api-stubs-docs-non-updatable" ],
|
||||
static_libs: [
|
||||
"conscrypt.module.public.api.stubs",
|
||||
"framework-media.stubs.system",
|
||||
"framework-mediaprovider.stubs.system",
|
||||
"framework-permission.stubs.system",
|
||||
"framework-sdkextensions.stubs.system",
|
||||
"framework-statsd.stubs.system",
|
||||
"framework-tethering.stubs.system",
|
||||
"updatable_media_stubs",
|
||||
"framework-wifi.stubs.system",
|
||||
"private-stub-annotations-jar",
|
||||
],
|
||||
defaults: ["android_defaults_stubs_current"],
|
||||
}
|
||||
@@ -374,6 +398,7 @@ java_library_static {
|
||||
java_library_static {
|
||||
name: "android_test_stubs_current",
|
||||
srcs: [ ":test-api-stubs-docs" ],
|
||||
static_libs: [ "private-stub-annotations-jar" ],
|
||||
defaults: [
|
||||
"android_defaults_stubs_current",
|
||||
"android_stubs_dists_default",
|
||||
|
||||
@@ -52,5 +52,10 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"postsubmit-managedprofile-stress": [
|
||||
{
|
||||
"name": "ManagedProfileLifecycleStressTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
<activity android:name="android.perftests.utils.StubActivity">
|
||||
<activity android:name="android.perftests.utils.PerfTestActivity">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.perftests.core.PERFTEST" />
|
||||
</intent-filter>
|
||||
|
||||
@@ -20,9 +20,9 @@ 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.perftests.utils.StubActivity;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
@@ -46,8 +46,8 @@ public abstract class AbstractAutofillPerfTestCase {
|
||||
Settings.Secure.AUTOFILL_SERVICE);
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<StubActivity> mActivityRule =
|
||||
new ActivityTestRule<StubActivity>(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule<>(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
@@ -68,7 +68,7 @@ public abstract class AbstractAutofillPerfTestCase {
|
||||
Looper.getMainLooper().getThread() == Thread.currentThread());
|
||||
assertTrue("We should be running on the main thread",
|
||||
Looper.myLooper() == Looper.getMainLooper());
|
||||
StubActivity activity = mActivityRule.getActivity();
|
||||
PerfTestActivity activity = mActivityRule.getActivity();
|
||||
activity.setContentView(mLayoutId);
|
||||
onCreate(activity);
|
||||
});
|
||||
@@ -89,9 +89,9 @@ public abstract class AbstractAutofillPerfTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the {@link StubActivity} after it was launched.
|
||||
* Initializes the {@link PerfTestActivity} after it was launched.
|
||||
*/
|
||||
protected abstract void onCreate(StubActivity activity);
|
||||
protected abstract void onCreate(PerfTestActivity activity);
|
||||
|
||||
/**
|
||||
* Uses the {@code settings} binary to set the autofill service.
|
||||
|
||||
@@ -20,7 +20,7 @@ import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT
|
||||
import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
@@ -39,7 +39,7 @@ public class LoginTest extends AbstractAutofillPerfTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(StubActivity activity) {
|
||||
protected void onCreate(PerfTestActivity activity) {
|
||||
View root = activity.getWindow().getDecorView();
|
||||
mUsername = root.findViewById(R.id.username);
|
||||
mPassword = root.findViewById(R.id.password);
|
||||
|
||||
28
apct-tests/perftests/blobstore/Android.bp
Normal file
28
apct-tests/perftests/blobstore/Android.bp
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2020 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
android_test {
|
||||
name: "BlobStorePerfTests",
|
||||
srcs: ["src/**/*.java"],
|
||||
static_libs: [
|
||||
"BlobStoreTestUtils",
|
||||
"androidx.test.rules",
|
||||
"androidx.annotation_annotation",
|
||||
"apct-perftests-utils",
|
||||
"ub-uiautomator",
|
||||
],
|
||||
platform_apis: true,
|
||||
test_suites: ["device-tests"],
|
||||
certificate: "platform",
|
||||
}
|
||||
27
apct-tests/perftests/blobstore/AndroidManifest.xml
Normal file
27
apct-tests/perftests/blobstore/AndroidManifest.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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.blob">
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.perftests.blob"/>
|
||||
|
||||
</manifest>
|
||||
28
apct-tests/perftests/blobstore/AndroidTest.xml
Normal file
28
apct-tests/perftests/blobstore/AndroidTest.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?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 BlobStorePerfTests 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="BlobStorePerfTests.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||
<option name="package" value="com.android.perftests.blob" />
|
||||
<option name="hidden-api-checks" value="false"/>
|
||||
</test>
|
||||
</configuration>
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.perftests.blob;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.app.UiAutomation;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.perftests.utils.TraceMarkParser;
|
||||
import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
// Copy of com.android.frameworks.perftests.am.util.AtraceUtils. TODO: avoid this duplication.
|
||||
public class AtraceUtils {
|
||||
private static final String TAG = "AtraceUtils";
|
||||
private static final boolean VERBOSE = true;
|
||||
|
||||
private static final String ATRACE_START = "atrace --async_start -b %d -c %s";
|
||||
private static final String ATRACE_DUMP = "atrace --async_dump";
|
||||
private static final String ATRACE_STOP = "atrace --async_stop";
|
||||
private static final int DEFAULT_ATRACE_BUF_SIZE = 1024;
|
||||
|
||||
private UiAutomation mAutomation;
|
||||
private static AtraceUtils sUtils = null;
|
||||
private boolean mStarted = false;
|
||||
|
||||
private AtraceUtils(Instrumentation instrumentation) {
|
||||
mAutomation = instrumentation.getUiAutomation();
|
||||
}
|
||||
|
||||
public static AtraceUtils getInstance(Instrumentation instrumentation) {
|
||||
if (sUtils == null) {
|
||||
sUtils = new AtraceUtils(instrumentation);
|
||||
}
|
||||
return sUtils;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param categories The list of the categories to trace, separated with space.
|
||||
*/
|
||||
public void startTrace(String categories) {
|
||||
synchronized (this) {
|
||||
if (mStarted) {
|
||||
throw new IllegalStateException("atrace already started");
|
||||
}
|
||||
runShellCommand(String.format(
|
||||
ATRACE_START, DEFAULT_ATRACE_BUF_SIZE, categories));
|
||||
mStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void stopTrace() {
|
||||
synchronized (this) {
|
||||
mStarted = false;
|
||||
runShellCommand(ATRACE_STOP);
|
||||
}
|
||||
}
|
||||
|
||||
private String runShellCommand(String cmd) {
|
||||
try {
|
||||
return UiDevice.getInstance(
|
||||
InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parser The function that can accept the buffer of atrace dump and parse it.
|
||||
* @param handler The parse result handler
|
||||
*/
|
||||
public void performDump(TraceMarkParser parser,
|
||||
BiConsumer<String, List<TraceMarkSlice>> handler) {
|
||||
parser.reset();
|
||||
try {
|
||||
if (VERBOSE) {
|
||||
Log.i(TAG, "Collecting atrace dump...");
|
||||
}
|
||||
writeDataToBuf(mAutomation.executeShellCommand(ATRACE_DUMP), parser);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error in reading dump", e);
|
||||
}
|
||||
parser.forAllSlices(handler);
|
||||
}
|
||||
|
||||
// The given file descriptor here will be closed by this function
|
||||
private void writeDataToBuf(ParcelFileDescriptor pfDescriptor,
|
||||
TraceMarkParser parser) throws IOException {
|
||||
InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfDescriptor);
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
parser.visit(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.perftests.blob;
|
||||
|
||||
import android.app.blob.BlobHandle;
|
||||
import android.app.blob.BlobStoreManager;
|
||||
import android.content.Context;
|
||||
import android.perftests.utils.ManualBenchmarkState;
|
||||
import android.perftests.utils.PerfManualStatusReporter;
|
||||
import android.perftests.utils.TraceMarkParser;
|
||||
import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.utils.blob.DummyBlobData;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
public class BlobStorePerfTests {
|
||||
// From frameworks/native/cmds/atrace/atrace.cpp
|
||||
private static final String ATRACE_CATEGORY_SYSTEM_SERVER = "ss";
|
||||
// From f/b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
|
||||
private static final String ATRACE_COMPUTE_DIGEST_PREFIX = "computeBlobDigest-";
|
||||
|
||||
private Context mContext;
|
||||
private BlobStoreManager mBlobStoreManager;
|
||||
private AtraceUtils mAtraceUtils;
|
||||
private ManualBenchmarkState mState;
|
||||
|
||||
@Rule
|
||||
public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter();
|
||||
|
||||
@Parameterized.Parameter(0)
|
||||
public int fileSizeInMb;
|
||||
|
||||
@Parameterized.Parameters(name = "{0}MB")
|
||||
public static Collection<Object[]> getParameters() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ 25 },
|
||||
{ 50 },
|
||||
{ 100 },
|
||||
{ 200 },
|
||||
});
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
mBlobStoreManager = (BlobStoreManager) mContext.getSystemService(
|
||||
Context.BLOB_STORE_SERVICE);
|
||||
mAtraceUtils = AtraceUtils.getInstance(InstrumentationRegistry.getInstrumentation());
|
||||
mState = mPerfManualStatusReporter.getBenchmarkState();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
// TODO: Add a blob_store shell command to trigger idle maintenance to avoid hardcoding
|
||||
// job id like this.
|
||||
// From BlobStoreConfig.IDLE_JOB_ID = 191934935.
|
||||
runShellCommand("cmd jobscheduler run -f android 191934935");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComputeDigest() throws Exception {
|
||||
mAtraceUtils.startTrace(ATRACE_CATEGORY_SYSTEM_SERVER);
|
||||
try {
|
||||
final List<Long> durations = new ArrayList<>();
|
||||
final DummyBlobData blobData = prepareDataBlob(fileSizeInMb);
|
||||
final TraceMarkParser parser = new TraceMarkParser(
|
||||
line -> line.name.startsWith(ATRACE_COMPUTE_DIGEST_PREFIX));
|
||||
while (mState.keepRunning(durations)) {
|
||||
commitBlob(blobData);
|
||||
|
||||
durations.clear();
|
||||
collectDigestDurationsFromTrace(parser, durations);
|
||||
|
||||
deleteBlob(blobData.getBlobHandle());
|
||||
}
|
||||
} finally {
|
||||
mAtraceUtils.stopTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void collectDigestDurationsFromTrace(TraceMarkParser parser, List<Long> durations) {
|
||||
mAtraceUtils.performDump(parser, (key, slices) -> {
|
||||
for (TraceMarkSlice slice : slices) {
|
||||
durations.add(TimeUnit.MICROSECONDS.toNanos(slice.getDurationInMicroseconds()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DummyBlobData prepareDataBlob(int fileSizeInMb) throws Exception {
|
||||
final DummyBlobData blobData = new DummyBlobData.Builder(mContext)
|
||||
.setFileSize(fileSizeInMb * 1024 * 1024 /* bytes */)
|
||||
.build();
|
||||
blobData.prepare();
|
||||
return blobData;
|
||||
}
|
||||
|
||||
private void commitBlob(DummyBlobData blobData) throws Exception {
|
||||
final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle());
|
||||
try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) {
|
||||
blobData.writeToSession(session);
|
||||
final CompletableFuture<Integer> callback = new CompletableFuture<>();
|
||||
session.commit(mContext.getMainExecutor(), callback::complete);
|
||||
// Ignore commit callback result.
|
||||
callback.get();
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteBlob(BlobHandle blobHandle) throws Exception {
|
||||
runShellCommand(String.format(
|
||||
"cmd blob_store delete-blob --algo %s --digest %s --label %s --expiry %d --tag %s",
|
||||
blobHandle.algorithm,
|
||||
Base64.getEncoder().encode(blobHandle.digest),
|
||||
blobHandle.label,
|
||||
blobHandle.expiryTimeMillis,
|
||||
blobHandle.tag));
|
||||
}
|
||||
|
||||
private String runShellCommand(String cmd) {
|
||||
try {
|
||||
return UiDevice.getInstance(
|
||||
InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
apct-tests/perftests/core/Android.bp
Normal file
34
apct-tests/perftests/core/Android.bp
Normal file
@@ -0,0 +1,34 @@
|
||||
android_test {
|
||||
name: "CorePerfTests",
|
||||
|
||||
resource_dirs: ["res"],
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
"src/**/*.kt",
|
||||
"src/android/os/ISomeService.aidl",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"androidx.appcompat_appcompat",
|
||||
"androidx.test.rules",
|
||||
"androidx.annotation_annotation",
|
||||
"apct-perftests-overlay-apps",
|
||||
"apct-perftests-resources-manager-apps",
|
||||
"apct-perftests-utils",
|
||||
"guava",
|
||||
],
|
||||
|
||||
libs: ["android.test.base"],
|
||||
|
||||
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",
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
||||
LOCAL_SRC_FILES := \
|
||||
$(call all-java-files-under, src) \
|
||||
src/android/os/ISomeService.aidl
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
androidx.test.rules \
|
||||
androidx.annotation_annotation \
|
||||
apct-perftests-utils \
|
||||
guava
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := android.test.base
|
||||
|
||||
LOCAL_PACKAGE_NAME := CorePerfTests
|
||||
LOCAL_PRIVATE_PLATFORM_APIS := true
|
||||
|
||||
LOCAL_JNI_SHARED_LIBRARIES := libperftestscore_jni
|
||||
|
||||
# Use google-fonts/dancing-script for the performance metrics
|
||||
LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script
|
||||
|
||||
LOCAL_COMPATIBILITY_SUITE += device-tests
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
@@ -5,12 +5,15 @@
|
||||
<permission android:name="com.android.perftests.core.TestPermission" />
|
||||
<uses-permission android:name="com.android.perftests.core.TestPermission" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
|
||||
<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" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
<activity android:name="android.perftests.utils.StubActivity">
|
||||
<activity android:name="android.perftests.utils.PerfTestActivity">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.perftests.core.PERFTEST" />
|
||||
</intent-filter>
|
||||
|
||||
188
apct-tests/perftests/core/apps/overlay/Android.bp
Normal file
188
apct-tests/perftests/core/apps/overlay/Android.bp
Normal file
@@ -0,0 +1,188 @@
|
||||
// Copyright (C) 2019 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
android_test_helper_app {
|
||||
name: "Overlay0",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay0",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "Overlay1",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay1",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "Overlay2",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay2",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "Overlay3",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay3",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "Overlay4",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay4",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "Overlay5",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay5",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "Overlay6",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay6",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "Overlay7",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay7",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "Overlay8",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay8",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "Overlay9",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay9",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeOverlay0",
|
||||
resource_dirs : [ "res_large" ],
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay.large0",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeOverlay1",
|
||||
resource_dirs : [ "res_large" ],
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay.large1",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeOverlay2",
|
||||
resource_dirs : [ "res_large" ],
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay.large2",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeOverlay3",
|
||||
resource_dirs : [ "res_large" ],
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay.large3",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeOverlay4",
|
||||
resource_dirs : [ "res_large" ],
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay.large4",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeOverlay5",
|
||||
resource_dirs : [ "res_large" ],
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay.large5",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeOverlay6",
|
||||
resource_dirs : [ "res_large" ],
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay.large6",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeOverlay7",
|
||||
resource_dirs : [ "res_large" ],
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay.large7",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeOverlay8",
|
||||
resource_dirs : [ "res_large" ],
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay.large8",
|
||||
]
|
||||
}
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeOverlay9",
|
||||
resource_dirs : [ "res_large" ],
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.overlay.large9",
|
||||
]
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "apct-perftests-overlay-apps",
|
||||
java_resources: [
|
||||
":Overlay0",
|
||||
":Overlay1",
|
||||
":Overlay2",
|
||||
":Overlay3",
|
||||
":Overlay4",
|
||||
":Overlay5",
|
||||
":Overlay6",
|
||||
":Overlay7",
|
||||
":Overlay8",
|
||||
":Overlay9",
|
||||
":LargeOverlay0",
|
||||
":LargeOverlay1",
|
||||
":LargeOverlay2",
|
||||
":LargeOverlay3",
|
||||
":LargeOverlay4",
|
||||
":LargeOverlay5",
|
||||
":LargeOverlay6",
|
||||
":LargeOverlay7",
|
||||
":LargeOverlay8",
|
||||
":LargeOverlay9",
|
||||
],
|
||||
}
|
||||
21
apct-tests/perftests/core/apps/overlay/AndroidManifest.xml
Normal file
21
apct-tests/perftests/core/apps/overlay/AndroidManifest.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.perftests.overlay">
|
||||
<application android:hasCode="false" />
|
||||
<uses-sdk android:targetSdkVersion="29" />
|
||||
<overlay android:targetPackage="com.android.perftests.core" android:targetName="TestResources"/>
|
||||
</manifest>
|
||||
19
apct-tests/perftests/core/apps/overlay/res/values/values.xml
Normal file
19
apct-tests/perftests/core/apps/overlay/res/values/values.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="short_text">B</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,274 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="short_text000">B</string>
|
||||
<string name="short_text001">B</string>
|
||||
<string name="short_text002">B</string>
|
||||
<string name="short_text003">B</string>
|
||||
<string name="short_text004">B</string>
|
||||
<string name="short_text005">B</string>
|
||||
<string name="short_text006">B</string>
|
||||
<string name="short_text007">B</string>
|
||||
<string name="short_text008">B</string>
|
||||
<string name="short_text009">B</string>
|
||||
<string name="short_text010">B</string>
|
||||
<string name="short_text011">B</string>
|
||||
<string name="short_text012">B</string>
|
||||
<string name="short_text013">B</string>
|
||||
<string name="short_text014">B</string>
|
||||
<string name="short_text015">B</string>
|
||||
<string name="short_text016">B</string>
|
||||
<string name="short_text017">B</string>
|
||||
<string name="short_text018">B</string>
|
||||
<string name="short_text019">B</string>
|
||||
<string name="short_text020">B</string>
|
||||
<string name="short_text021">B</string>
|
||||
<string name="short_text022">B</string>
|
||||
<string name="short_text023">B</string>
|
||||
<string name="short_text024">B</string>
|
||||
<string name="short_text025">B</string>
|
||||
<string name="short_text026">B</string>
|
||||
<string name="short_text027">B</string>
|
||||
<string name="short_text028">B</string>
|
||||
<string name="short_text029">B</string>
|
||||
<string name="short_text030">B</string>
|
||||
<string name="short_text031">B</string>
|
||||
<string name="short_text032">B</string>
|
||||
<string name="short_text033">B</string>
|
||||
<string name="short_text034">B</string>
|
||||
<string name="short_text035">B</string>
|
||||
<string name="short_text036">B</string>
|
||||
<string name="short_text037">B</string>
|
||||
<string name="short_text038">B</string>
|
||||
<string name="short_text039">B</string>
|
||||
<string name="short_text040">B</string>
|
||||
<string name="short_text041">B</string>
|
||||
<string name="short_text042">B</string>
|
||||
<string name="short_text043">B</string>
|
||||
<string name="short_text044">B</string>
|
||||
<string name="short_text045">B</string>
|
||||
<string name="short_text046">B</string>
|
||||
<string name="short_text047">B</string>
|
||||
<string name="short_text048">B</string>
|
||||
<string name="short_text049">B</string>
|
||||
<string name="short_text050">B</string>
|
||||
<string name="short_text051">B</string>
|
||||
<string name="short_text052">B</string>
|
||||
<string name="short_text053">B</string>
|
||||
<string name="short_text054">B</string>
|
||||
<string name="short_text055">B</string>
|
||||
<string name="short_text056">B</string>
|
||||
<string name="short_text057">B</string>
|
||||
<string name="short_text058">B</string>
|
||||
<string name="short_text059">B</string>
|
||||
<string name="short_text060">B</string>
|
||||
<string name="short_text061">B</string>
|
||||
<string name="short_text062">B</string>
|
||||
<string name="short_text063">B</string>
|
||||
<string name="short_text064">B</string>
|
||||
<string name="short_text065">B</string>
|
||||
<string name="short_text066">B</string>
|
||||
<string name="short_text067">B</string>
|
||||
<string name="short_text068">B</string>
|
||||
<string name="short_text069">B</string>
|
||||
<string name="short_text070">B</string>
|
||||
<string name="short_text071">B</string>
|
||||
<string name="short_text072">B</string>
|
||||
<string name="short_text073">B</string>
|
||||
<string name="short_text074">B</string>
|
||||
<string name="short_text075">B</string>
|
||||
<string name="short_text076">B</string>
|
||||
<string name="short_text077">B</string>
|
||||
<string name="short_text078">B</string>
|
||||
<string name="short_text079">B</string>
|
||||
<string name="short_text080">B</string>
|
||||
<string name="short_text081">B</string>
|
||||
<string name="short_text082">B</string>
|
||||
<string name="short_text083">B</string>
|
||||
<string name="short_text084">B</string>
|
||||
<string name="short_text085">B</string>
|
||||
<string name="short_text086">B</string>
|
||||
<string name="short_text087">B</string>
|
||||
<string name="short_text088">B</string>
|
||||
<string name="short_text089">B</string>
|
||||
<string name="short_text090">B</string>
|
||||
<string name="short_text091">B</string>
|
||||
<string name="short_text092">B</string>
|
||||
<string name="short_text093">B</string>
|
||||
<string name="short_text094">B</string>
|
||||
<string name="short_text095">B</string>
|
||||
<string name="short_text096">B</string>
|
||||
<string name="short_text097">B</string>
|
||||
<string name="short_text098">B</string>
|
||||
<string name="short_text099">B</string>
|
||||
<string name="short_text100">B</string>
|
||||
<string name="short_text101">B</string>
|
||||
<string name="short_text102">B</string>
|
||||
<string name="short_text103">B</string>
|
||||
<string name="short_text104">B</string>
|
||||
<string name="short_text105">B</string>
|
||||
<string name="short_text106">B</string>
|
||||
<string name="short_text107">B</string>
|
||||
<string name="short_text108">B</string>
|
||||
<string name="short_text109">B</string>
|
||||
<string name="short_text110">B</string>
|
||||
<string name="short_text111">B</string>
|
||||
<string name="short_text112">B</string>
|
||||
<string name="short_text113">B</string>
|
||||
<string name="short_text114">B</string>
|
||||
<string name="short_text115">B</string>
|
||||
<string name="short_text116">B</string>
|
||||
<string name="short_text117">B</string>
|
||||
<string name="short_text118">B</string>
|
||||
<string name="short_text119">B</string>
|
||||
<string name="short_text120">B</string>
|
||||
<string name="short_text121">B</string>
|
||||
<string name="short_text122">B</string>
|
||||
<string name="short_text123">B</string>
|
||||
<string name="short_text124">B</string>
|
||||
<string name="short_text125">B</string>
|
||||
<string name="short_text126">B</string>
|
||||
<string name="short_text127">B</string>
|
||||
<string name="short_text128">B</string>
|
||||
<string name="short_text129">B</string>
|
||||
<string name="short_text130">B</string>
|
||||
<string name="short_text131">B</string>
|
||||
<string name="short_text132">B</string>
|
||||
<string name="short_text133">B</string>
|
||||
<string name="short_text134">B</string>
|
||||
<string name="short_text135">B</string>
|
||||
<string name="short_text136">B</string>
|
||||
<string name="short_text137">B</string>
|
||||
<string name="short_text138">B</string>
|
||||
<string name="short_text139">B</string>
|
||||
<string name="short_text140">B</string>
|
||||
<string name="short_text141">B</string>
|
||||
<string name="short_text142">B</string>
|
||||
<string name="short_text143">B</string>
|
||||
<string name="short_text144">B</string>
|
||||
<string name="short_text145">B</string>
|
||||
<string name="short_text146">B</string>
|
||||
<string name="short_text147">B</string>
|
||||
<string name="short_text148">B</string>
|
||||
<string name="short_text149">B</string>
|
||||
<string name="short_text150">B</string>
|
||||
<string name="short_text151">B</string>
|
||||
<string name="short_text152">B</string>
|
||||
<string name="short_text153">B</string>
|
||||
<string name="short_text154">B</string>
|
||||
<string name="short_text155">B</string>
|
||||
<string name="short_text156">B</string>
|
||||
<string name="short_text157">B</string>
|
||||
<string name="short_text158">B</string>
|
||||
<string name="short_text159">B</string>
|
||||
<string name="short_text160">B</string>
|
||||
<string name="short_text161">B</string>
|
||||
<string name="short_text162">B</string>
|
||||
<string name="short_text163">B</string>
|
||||
<string name="short_text164">B</string>
|
||||
<string name="short_text165">B</string>
|
||||
<string name="short_text166">B</string>
|
||||
<string name="short_text167">B</string>
|
||||
<string name="short_text168">B</string>
|
||||
<string name="short_text169">B</string>
|
||||
<string name="short_text170">B</string>
|
||||
<string name="short_text171">B</string>
|
||||
<string name="short_text172">B</string>
|
||||
<string name="short_text173">B</string>
|
||||
<string name="short_text174">B</string>
|
||||
<string name="short_text175">B</string>
|
||||
<string name="short_text176">B</string>
|
||||
<string name="short_text177">B</string>
|
||||
<string name="short_text178">B</string>
|
||||
<string name="short_text179">B</string>
|
||||
<string name="short_text180">B</string>
|
||||
<string name="short_text181">B</string>
|
||||
<string name="short_text182">B</string>
|
||||
<string name="short_text183">B</string>
|
||||
<string name="short_text184">B</string>
|
||||
<string name="short_text185">B</string>
|
||||
<string name="short_text186">B</string>
|
||||
<string name="short_text187">B</string>
|
||||
<string name="short_text188">B</string>
|
||||
<string name="short_text189">B</string>
|
||||
<string name="short_text190">B</string>
|
||||
<string name="short_text191">B</string>
|
||||
<string name="short_text192">B</string>
|
||||
<string name="short_text193">B</string>
|
||||
<string name="short_text194">B</string>
|
||||
<string name="short_text195">B</string>
|
||||
<string name="short_text196">B</string>
|
||||
<string name="short_text197">B</string>
|
||||
<string name="short_text198">B</string>
|
||||
<string name="short_text199">B</string>
|
||||
<string name="short_text200">B</string>
|
||||
<string name="short_text201">B</string>
|
||||
<string name="short_text202">B</string>
|
||||
<string name="short_text203">B</string>
|
||||
<string name="short_text204">B</string>
|
||||
<string name="short_text205">B</string>
|
||||
<string name="short_text206">B</string>
|
||||
<string name="short_text207">B</string>
|
||||
<string name="short_text208">B</string>
|
||||
<string name="short_text209">B</string>
|
||||
<string name="short_text210">B</string>
|
||||
<string name="short_text211">B</string>
|
||||
<string name="short_text212">B</string>
|
||||
<string name="short_text213">B</string>
|
||||
<string name="short_text214">B</string>
|
||||
<string name="short_text215">B</string>
|
||||
<string name="short_text216">B</string>
|
||||
<string name="short_text217">B</string>
|
||||
<string name="short_text218">B</string>
|
||||
<string name="short_text219">B</string>
|
||||
<string name="short_text220">B</string>
|
||||
<string name="short_text221">B</string>
|
||||
<string name="short_text222">B</string>
|
||||
<string name="short_text223">B</string>
|
||||
<string name="short_text224">B</string>
|
||||
<string name="short_text225">B</string>
|
||||
<string name="short_text226">B</string>
|
||||
<string name="short_text227">B</string>
|
||||
<string name="short_text228">B</string>
|
||||
<string name="short_text229">B</string>
|
||||
<string name="short_text230">B</string>
|
||||
<string name="short_text231">B</string>
|
||||
<string name="short_text232">B</string>
|
||||
<string name="short_text233">B</string>
|
||||
<string name="short_text234">B</string>
|
||||
<string name="short_text235">B</string>
|
||||
<string name="short_text236">B</string>
|
||||
<string name="short_text237">B</string>
|
||||
<string name="short_text238">B</string>
|
||||
<string name="short_text239">B</string>
|
||||
<string name="short_text240">B</string>
|
||||
<string name="short_text241">B</string>
|
||||
<string name="short_text242">B</string>
|
||||
<string name="short_text243">B</string>
|
||||
<string name="short_text244">B</string>
|
||||
<string name="short_text245">B</string>
|
||||
<string name="short_text246">B</string>
|
||||
<string name="short_text247">B</string>
|
||||
<string name="short_text248">B</string>
|
||||
<string name="short_text249">B</string>
|
||||
<string name="short_text250">B</string>
|
||||
<string name="short_text251">B</string>
|
||||
<string name="short_text252">B</string>
|
||||
<string name="short_text253">B</string>
|
||||
<string name="short_text254">B</string>
|
||||
<string name="short_text255">B</string>
|
||||
</resources>
|
||||
34
apct-tests/perftests/core/apps/reources_manager/Android.bp
Normal file
34
apct-tests/perftests/core/apps/reources_manager/Android.bp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2019 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
android_test_helper_app {
|
||||
name: "LargeResourcesCompressed",
|
||||
static_libs: [ "androidx.appcompat_appcompat" ],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "LargeResourcesUncompressed",
|
||||
srcs: [ ":LargeResourcesCompressed" ],
|
||||
out: ["LargeResourcesUncompressed.apk"],
|
||||
cmd: "cp $(in) $(out) && unzip -o $(out) resources.arsc"
|
||||
+ " && zip $(out) resources.arsc"
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "apct-perftests-resources-manager-apps",
|
||||
java_resources: [
|
||||
":LargeResourcesCompressed",
|
||||
":LargeResourcesUncompressed",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="fake.android">
|
||||
<application android:hasCode="false" />
|
||||
<uses-sdk android:targetSdkVersion="29" />
|
||||
</manifest>
|
||||
25
apct-tests/perftests/core/res/color/color_state_list.xml
Normal file
25
apct-tests/perftests/core/res/color/color_state_list.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_focused="true"
|
||||
android:color="#000000" />
|
||||
<item android:state_pressed="true"
|
||||
android:state_enabled="false"
|
||||
android:color="#212121" />
|
||||
<item android:state_enabled="false"
|
||||
android:color="#414141" />
|
||||
<item android:color="#616161" />
|
||||
</selector>
|
||||
280
apct-tests/perftests/core/res/values/overlayable.xml
Normal file
280
apct-tests/perftests/core/res/values/overlayable.xml
Normal file
@@ -0,0 +1,280 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2019 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<overlayable name="TestResources">
|
||||
<policy type="public">
|
||||
<item type="string" name="short_text" />
|
||||
<item type="string" name="short_text000" />
|
||||
<item type="string" name="short_text001" />
|
||||
<item type="string" name="short_text002" />
|
||||
<item type="string" name="short_text003" />
|
||||
<item type="string" name="short_text004" />
|
||||
<item type="string" name="short_text005" />
|
||||
<item type="string" name="short_text006" />
|
||||
<item type="string" name="short_text007" />
|
||||
<item type="string" name="short_text008" />
|
||||
<item type="string" name="short_text009" />
|
||||
<item type="string" name="short_text010" />
|
||||
<item type="string" name="short_text011" />
|
||||
<item type="string" name="short_text012" />
|
||||
<item type="string" name="short_text013" />
|
||||
<item type="string" name="short_text014" />
|
||||
<item type="string" name="short_text015" />
|
||||
<item type="string" name="short_text016" />
|
||||
<item type="string" name="short_text017" />
|
||||
<item type="string" name="short_text018" />
|
||||
<item type="string" name="short_text019" />
|
||||
<item type="string" name="short_text020" />
|
||||
<item type="string" name="short_text021" />
|
||||
<item type="string" name="short_text022" />
|
||||
<item type="string" name="short_text023" />
|
||||
<item type="string" name="short_text024" />
|
||||
<item type="string" name="short_text025" />
|
||||
<item type="string" name="short_text026" />
|
||||
<item type="string" name="short_text027" />
|
||||
<item type="string" name="short_text028" />
|
||||
<item type="string" name="short_text029" />
|
||||
<item type="string" name="short_text030" />
|
||||
<item type="string" name="short_text031" />
|
||||
<item type="string" name="short_text032" />
|
||||
<item type="string" name="short_text033" />
|
||||
<item type="string" name="short_text034" />
|
||||
<item type="string" name="short_text035" />
|
||||
<item type="string" name="short_text036" />
|
||||
<item type="string" name="short_text037" />
|
||||
<item type="string" name="short_text038" />
|
||||
<item type="string" name="short_text039" />
|
||||
<item type="string" name="short_text040" />
|
||||
<item type="string" name="short_text041" />
|
||||
<item type="string" name="short_text042" />
|
||||
<item type="string" name="short_text043" />
|
||||
<item type="string" name="short_text044" />
|
||||
<item type="string" name="short_text045" />
|
||||
<item type="string" name="short_text046" />
|
||||
<item type="string" name="short_text047" />
|
||||
<item type="string" name="short_text048" />
|
||||
<item type="string" name="short_text049" />
|
||||
<item type="string" name="short_text050" />
|
||||
<item type="string" name="short_text051" />
|
||||
<item type="string" name="short_text052" />
|
||||
<item type="string" name="short_text053" />
|
||||
<item type="string" name="short_text054" />
|
||||
<item type="string" name="short_text055" />
|
||||
<item type="string" name="short_text056" />
|
||||
<item type="string" name="short_text057" />
|
||||
<item type="string" name="short_text058" />
|
||||
<item type="string" name="short_text059" />
|
||||
<item type="string" name="short_text060" />
|
||||
<item type="string" name="short_text061" />
|
||||
<item type="string" name="short_text062" />
|
||||
<item type="string" name="short_text063" />
|
||||
<item type="string" name="short_text064" />
|
||||
<item type="string" name="short_text065" />
|
||||
<item type="string" name="short_text066" />
|
||||
<item type="string" name="short_text067" />
|
||||
<item type="string" name="short_text068" />
|
||||
<item type="string" name="short_text069" />
|
||||
<item type="string" name="short_text070" />
|
||||
<item type="string" name="short_text071" />
|
||||
<item type="string" name="short_text072" />
|
||||
<item type="string" name="short_text073" />
|
||||
<item type="string" name="short_text074" />
|
||||
<item type="string" name="short_text075" />
|
||||
<item type="string" name="short_text076" />
|
||||
<item type="string" name="short_text077" />
|
||||
<item type="string" name="short_text078" />
|
||||
<item type="string" name="short_text079" />
|
||||
<item type="string" name="short_text080" />
|
||||
<item type="string" name="short_text081" />
|
||||
<item type="string" name="short_text082" />
|
||||
<item type="string" name="short_text083" />
|
||||
<item type="string" name="short_text084" />
|
||||
<item type="string" name="short_text085" />
|
||||
<item type="string" name="short_text086" />
|
||||
<item type="string" name="short_text087" />
|
||||
<item type="string" name="short_text088" />
|
||||
<item type="string" name="short_text089" />
|
||||
<item type="string" name="short_text090" />
|
||||
<item type="string" name="short_text091" />
|
||||
<item type="string" name="short_text092" />
|
||||
<item type="string" name="short_text093" />
|
||||
<item type="string" name="short_text094" />
|
||||
<item type="string" name="short_text095" />
|
||||
<item type="string" name="short_text096" />
|
||||
<item type="string" name="short_text097" />
|
||||
<item type="string" name="short_text098" />
|
||||
<item type="string" name="short_text099" />
|
||||
<item type="string" name="short_text100" />
|
||||
<item type="string" name="short_text101" />
|
||||
<item type="string" name="short_text102" />
|
||||
<item type="string" name="short_text103" />
|
||||
<item type="string" name="short_text104" />
|
||||
<item type="string" name="short_text105" />
|
||||
<item type="string" name="short_text106" />
|
||||
<item type="string" name="short_text107" />
|
||||
<item type="string" name="short_text108" />
|
||||
<item type="string" name="short_text109" />
|
||||
<item type="string" name="short_text110" />
|
||||
<item type="string" name="short_text111" />
|
||||
<item type="string" name="short_text112" />
|
||||
<item type="string" name="short_text113" />
|
||||
<item type="string" name="short_text114" />
|
||||
<item type="string" name="short_text115" />
|
||||
<item type="string" name="short_text116" />
|
||||
<item type="string" name="short_text117" />
|
||||
<item type="string" name="short_text118" />
|
||||
<item type="string" name="short_text119" />
|
||||
<item type="string" name="short_text120" />
|
||||
<item type="string" name="short_text121" />
|
||||
<item type="string" name="short_text122" />
|
||||
<item type="string" name="short_text123" />
|
||||
<item type="string" name="short_text124" />
|
||||
<item type="string" name="short_text125" />
|
||||
<item type="string" name="short_text126" />
|
||||
<item type="string" name="short_text127" />
|
||||
<item type="string" name="short_text128" />
|
||||
<item type="string" name="short_text129" />
|
||||
<item type="string" name="short_text130" />
|
||||
<item type="string" name="short_text131" />
|
||||
<item type="string" name="short_text132" />
|
||||
<item type="string" name="short_text133" />
|
||||
<item type="string" name="short_text134" />
|
||||
<item type="string" name="short_text135" />
|
||||
<item type="string" name="short_text136" />
|
||||
<item type="string" name="short_text137" />
|
||||
<item type="string" name="short_text138" />
|
||||
<item type="string" name="short_text139" />
|
||||
<item type="string" name="short_text140" />
|
||||
<item type="string" name="short_text141" />
|
||||
<item type="string" name="short_text142" />
|
||||
<item type="string" name="short_text143" />
|
||||
<item type="string" name="short_text144" />
|
||||
<item type="string" name="short_text145" />
|
||||
<item type="string" name="short_text146" />
|
||||
<item type="string" name="short_text147" />
|
||||
<item type="string" name="short_text148" />
|
||||
<item type="string" name="short_text149" />
|
||||
<item type="string" name="short_text150" />
|
||||
<item type="string" name="short_text151" />
|
||||
<item type="string" name="short_text152" />
|
||||
<item type="string" name="short_text153" />
|
||||
<item type="string" name="short_text154" />
|
||||
<item type="string" name="short_text155" />
|
||||
<item type="string" name="short_text156" />
|
||||
<item type="string" name="short_text157" />
|
||||
<item type="string" name="short_text158" />
|
||||
<item type="string" name="short_text159" />
|
||||
<item type="string" name="short_text160" />
|
||||
<item type="string" name="short_text161" />
|
||||
<item type="string" name="short_text162" />
|
||||
<item type="string" name="short_text163" />
|
||||
<item type="string" name="short_text164" />
|
||||
<item type="string" name="short_text165" />
|
||||
<item type="string" name="short_text166" />
|
||||
<item type="string" name="short_text167" />
|
||||
<item type="string" name="short_text168" />
|
||||
<item type="string" name="short_text169" />
|
||||
<item type="string" name="short_text170" />
|
||||
<item type="string" name="short_text171" />
|
||||
<item type="string" name="short_text172" />
|
||||
<item type="string" name="short_text173" />
|
||||
<item type="string" name="short_text174" />
|
||||
<item type="string" name="short_text175" />
|
||||
<item type="string" name="short_text176" />
|
||||
<item type="string" name="short_text177" />
|
||||
<item type="string" name="short_text178" />
|
||||
<item type="string" name="short_text179" />
|
||||
<item type="string" name="short_text180" />
|
||||
<item type="string" name="short_text181" />
|
||||
<item type="string" name="short_text182" />
|
||||
<item type="string" name="short_text183" />
|
||||
<item type="string" name="short_text184" />
|
||||
<item type="string" name="short_text185" />
|
||||
<item type="string" name="short_text186" />
|
||||
<item type="string" name="short_text187" />
|
||||
<item type="string" name="short_text188" />
|
||||
<item type="string" name="short_text189" />
|
||||
<item type="string" name="short_text190" />
|
||||
<item type="string" name="short_text191" />
|
||||
<item type="string" name="short_text192" />
|
||||
<item type="string" name="short_text193" />
|
||||
<item type="string" name="short_text194" />
|
||||
<item type="string" name="short_text195" />
|
||||
<item type="string" name="short_text196" />
|
||||
<item type="string" name="short_text197" />
|
||||
<item type="string" name="short_text198" />
|
||||
<item type="string" name="short_text199" />
|
||||
<item type="string" name="short_text200" />
|
||||
<item type="string" name="short_text201" />
|
||||
<item type="string" name="short_text202" />
|
||||
<item type="string" name="short_text203" />
|
||||
<item type="string" name="short_text204" />
|
||||
<item type="string" name="short_text205" />
|
||||
<item type="string" name="short_text206" />
|
||||
<item type="string" name="short_text207" />
|
||||
<item type="string" name="short_text208" />
|
||||
<item type="string" name="short_text209" />
|
||||
<item type="string" name="short_text210" />
|
||||
<item type="string" name="short_text211" />
|
||||
<item type="string" name="short_text212" />
|
||||
<item type="string" name="short_text213" />
|
||||
<item type="string" name="short_text214" />
|
||||
<item type="string" name="short_text215" />
|
||||
<item type="string" name="short_text216" />
|
||||
<item type="string" name="short_text217" />
|
||||
<item type="string" name="short_text218" />
|
||||
<item type="string" name="short_text219" />
|
||||
<item type="string" name="short_text220" />
|
||||
<item type="string" name="short_text221" />
|
||||
<item type="string" name="short_text222" />
|
||||
<item type="string" name="short_text223" />
|
||||
<item type="string" name="short_text224" />
|
||||
<item type="string" name="short_text225" />
|
||||
<item type="string" name="short_text226" />
|
||||
<item type="string" name="short_text227" />
|
||||
<item type="string" name="short_text228" />
|
||||
<item type="string" name="short_text229" />
|
||||
<item type="string" name="short_text230" />
|
||||
<item type="string" name="short_text231" />
|
||||
<item type="string" name="short_text232" />
|
||||
<item type="string" name="short_text233" />
|
||||
<item type="string" name="short_text234" />
|
||||
<item type="string" name="short_text235" />
|
||||
<item type="string" name="short_text236" />
|
||||
<item type="string" name="short_text237" />
|
||||
<item type="string" name="short_text238" />
|
||||
<item type="string" name="short_text239" />
|
||||
<item type="string" name="short_text240" />
|
||||
<item type="string" name="short_text241" />
|
||||
<item type="string" name="short_text242" />
|
||||
<item type="string" name="short_text243" />
|
||||
<item type="string" name="short_text244" />
|
||||
<item type="string" name="short_text245" />
|
||||
<item type="string" name="short_text246" />
|
||||
<item type="string" name="short_text247" />
|
||||
<item type="string" name="short_text248" />
|
||||
<item type="string" name="short_text249" />
|
||||
<item type="string" name="short_text250" />
|
||||
<item type="string" name="short_text251" />
|
||||
<item type="string" name="short_text252" />
|
||||
<item type="string" name="short_text253" />
|
||||
<item type="string" name="short_text254" />
|
||||
<item type="string" name="short_text255" />
|
||||
</policy>
|
||||
</overlayable>
|
||||
</resources>
|
||||
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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
|
||||
-->
|
||||
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="long_text">text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text typo text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text </string>
|
||||
<string name="short_text">text text</string>
|
||||
</resources>
|
||||
368
apct-tests/perftests/core/res/values/values.xml
Normal file
368
apct-tests/perftests/core/res/values/values.xml
Normal file
@@ -0,0 +1,368 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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
|
||||
-->
|
||||
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="long_text">text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text typo text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text text text text text
|
||||
text text text text text text text text text text text text </string>
|
||||
|
||||
<plurals name="plurals_text">
|
||||
<item quantity="one">1 text</item>
|
||||
<item quantity="other"><xliff:g id="count" example="3">%d</xliff:g> texts</item>
|
||||
</plurals>
|
||||
|
||||
<string-array name="strings">
|
||||
<item>Run</item>
|
||||
<item>the</item>
|
||||
<item>performance</item>
|
||||
<item>tests!</item>
|
||||
<item>Run</item>
|
||||
<item>the</item>
|
||||
<item>performance</item>
|
||||
<item>tests!</item>
|
||||
<item>Run</item>
|
||||
<item>the</item>
|
||||
<item>performance</item>
|
||||
<item>tests!</item>
|
||||
<item>Run</item>
|
||||
<item>the</item>
|
||||
<item>performance</item>
|
||||
<item>tests!</item>
|
||||
<item>Run</item>
|
||||
<item>the</item>
|
||||
<item>performance</item>
|
||||
<item>tests!</item>
|
||||
<item>Run</item>
|
||||
<item>the</item>
|
||||
<item>performance</item>
|
||||
<item>tests!</item>
|
||||
<item>Run</item>
|
||||
<item>the</item>
|
||||
<item>performance</item>
|
||||
<item>tests!</item>
|
||||
<item>Run</item>
|
||||
<item>the</item>
|
||||
<item>performance</item>
|
||||
<item>tests!</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="ints">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
</integer-array>
|
||||
|
||||
<color name="white">#ffffff</color>
|
||||
|
||||
<integer name="forty_two">42</integer>
|
||||
|
||||
<string name="short_text">text text</string>
|
||||
<string name="short_text000">B</string>
|
||||
<string name="short_text001">B</string>
|
||||
<string name="short_text002">B</string>
|
||||
<string name="short_text003">B</string>
|
||||
<string name="short_text004">B</string>
|
||||
<string name="short_text005">B</string>
|
||||
<string name="short_text006">B</string>
|
||||
<string name="short_text007">B</string>
|
||||
<string name="short_text008">B</string>
|
||||
<string name="short_text009">B</string>
|
||||
<string name="short_text010">B</string>
|
||||
<string name="short_text011">B</string>
|
||||
<string name="short_text012">B</string>
|
||||
<string name="short_text013">B</string>
|
||||
<string name="short_text014">B</string>
|
||||
<string name="short_text015">B</string>
|
||||
<string name="short_text016">B</string>
|
||||
<string name="short_text017">B</string>
|
||||
<string name="short_text018">B</string>
|
||||
<string name="short_text019">B</string>
|
||||
<string name="short_text020">B</string>
|
||||
<string name="short_text021">B</string>
|
||||
<string name="short_text022">B</string>
|
||||
<string name="short_text023">B</string>
|
||||
<string name="short_text024">B</string>
|
||||
<string name="short_text025">B</string>
|
||||
<string name="short_text026">B</string>
|
||||
<string name="short_text027">B</string>
|
||||
<string name="short_text028">B</string>
|
||||
<string name="short_text029">B</string>
|
||||
<string name="short_text030">B</string>
|
||||
<string name="short_text031">B</string>
|
||||
<string name="short_text032">B</string>
|
||||
<string name="short_text033">B</string>
|
||||
<string name="short_text034">B</string>
|
||||
<string name="short_text035">B</string>
|
||||
<string name="short_text036">B</string>
|
||||
<string name="short_text037">B</string>
|
||||
<string name="short_text038">B</string>
|
||||
<string name="short_text039">B</string>
|
||||
<string name="short_text040">B</string>
|
||||
<string name="short_text041">B</string>
|
||||
<string name="short_text042">B</string>
|
||||
<string name="short_text043">B</string>
|
||||
<string name="short_text044">B</string>
|
||||
<string name="short_text045">B</string>
|
||||
<string name="short_text046">B</string>
|
||||
<string name="short_text047">B</string>
|
||||
<string name="short_text048">B</string>
|
||||
<string name="short_text049">B</string>
|
||||
<string name="short_text050">B</string>
|
||||
<string name="short_text051">B</string>
|
||||
<string name="short_text052">B</string>
|
||||
<string name="short_text053">B</string>
|
||||
<string name="short_text054">B</string>
|
||||
<string name="short_text055">B</string>
|
||||
<string name="short_text056">B</string>
|
||||
<string name="short_text057">B</string>
|
||||
<string name="short_text058">B</string>
|
||||
<string name="short_text059">B</string>
|
||||
<string name="short_text060">B</string>
|
||||
<string name="short_text061">B</string>
|
||||
<string name="short_text062">B</string>
|
||||
<string name="short_text063">B</string>
|
||||
<string name="short_text064">B</string>
|
||||
<string name="short_text065">B</string>
|
||||
<string name="short_text066">B</string>
|
||||
<string name="short_text067">B</string>
|
||||
<string name="short_text068">B</string>
|
||||
<string name="short_text069">B</string>
|
||||
<string name="short_text070">B</string>
|
||||
<string name="short_text071">B</string>
|
||||
<string name="short_text072">B</string>
|
||||
<string name="short_text073">B</string>
|
||||
<string name="short_text074">B</string>
|
||||
<string name="short_text075">B</string>
|
||||
<string name="short_text076">B</string>
|
||||
<string name="short_text077">B</string>
|
||||
<string name="short_text078">B</string>
|
||||
<string name="short_text079">B</string>
|
||||
<string name="short_text080">B</string>
|
||||
<string name="short_text081">B</string>
|
||||
<string name="short_text082">B</string>
|
||||
<string name="short_text083">B</string>
|
||||
<string name="short_text084">B</string>
|
||||
<string name="short_text085">B</string>
|
||||
<string name="short_text086">B</string>
|
||||
<string name="short_text087">B</string>
|
||||
<string name="short_text088">B</string>
|
||||
<string name="short_text089">B</string>
|
||||
<string name="short_text090">B</string>
|
||||
<string name="short_text091">B</string>
|
||||
<string name="short_text092">B</string>
|
||||
<string name="short_text093">B</string>
|
||||
<string name="short_text094">B</string>
|
||||
<string name="short_text095">B</string>
|
||||
<string name="short_text096">B</string>
|
||||
<string name="short_text097">B</string>
|
||||
<string name="short_text098">B</string>
|
||||
<string name="short_text099">B</string>
|
||||
<string name="short_text100">B</string>
|
||||
<string name="short_text101">B</string>
|
||||
<string name="short_text102">B</string>
|
||||
<string name="short_text103">B</string>
|
||||
<string name="short_text104">B</string>
|
||||
<string name="short_text105">B</string>
|
||||
<string name="short_text106">B</string>
|
||||
<string name="short_text107">B</string>
|
||||
<string name="short_text108">B</string>
|
||||
<string name="short_text109">B</string>
|
||||
<string name="short_text110">B</string>
|
||||
<string name="short_text111">B</string>
|
||||
<string name="short_text112">B</string>
|
||||
<string name="short_text113">B</string>
|
||||
<string name="short_text114">B</string>
|
||||
<string name="short_text115">B</string>
|
||||
<string name="short_text116">B</string>
|
||||
<string name="short_text117">B</string>
|
||||
<string name="short_text118">B</string>
|
||||
<string name="short_text119">B</string>
|
||||
<string name="short_text120">B</string>
|
||||
<string name="short_text121">B</string>
|
||||
<string name="short_text122">B</string>
|
||||
<string name="short_text123">B</string>
|
||||
<string name="short_text124">B</string>
|
||||
<string name="short_text125">B</string>
|
||||
<string name="short_text126">B</string>
|
||||
<string name="short_text127">B</string>
|
||||
<string name="short_text128">B</string>
|
||||
<string name="short_text129">B</string>
|
||||
<string name="short_text130">B</string>
|
||||
<string name="short_text131">B</string>
|
||||
<string name="short_text132">B</string>
|
||||
<string name="short_text133">B</string>
|
||||
<string name="short_text134">B</string>
|
||||
<string name="short_text135">B</string>
|
||||
<string name="short_text136">B</string>
|
||||
<string name="short_text137">B</string>
|
||||
<string name="short_text138">B</string>
|
||||
<string name="short_text139">B</string>
|
||||
<string name="short_text140">B</string>
|
||||
<string name="short_text141">B</string>
|
||||
<string name="short_text142">B</string>
|
||||
<string name="short_text143">B</string>
|
||||
<string name="short_text144">B</string>
|
||||
<string name="short_text145">B</string>
|
||||
<string name="short_text146">B</string>
|
||||
<string name="short_text147">B</string>
|
||||
<string name="short_text148">B</string>
|
||||
<string name="short_text149">B</string>
|
||||
<string name="short_text150">B</string>
|
||||
<string name="short_text151">B</string>
|
||||
<string name="short_text152">B</string>
|
||||
<string name="short_text153">B</string>
|
||||
<string name="short_text154">B</string>
|
||||
<string name="short_text155">B</string>
|
||||
<string name="short_text156">B</string>
|
||||
<string name="short_text157">B</string>
|
||||
<string name="short_text158">B</string>
|
||||
<string name="short_text159">B</string>
|
||||
<string name="short_text160">B</string>
|
||||
<string name="short_text161">B</string>
|
||||
<string name="short_text162">B</string>
|
||||
<string name="short_text163">B</string>
|
||||
<string name="short_text164">B</string>
|
||||
<string name="short_text165">B</string>
|
||||
<string name="short_text166">B</string>
|
||||
<string name="short_text167">B</string>
|
||||
<string name="short_text168">B</string>
|
||||
<string name="short_text169">B</string>
|
||||
<string name="short_text170">B</string>
|
||||
<string name="short_text171">B</string>
|
||||
<string name="short_text172">B</string>
|
||||
<string name="short_text173">B</string>
|
||||
<string name="short_text174">B</string>
|
||||
<string name="short_text175">B</string>
|
||||
<string name="short_text176">B</string>
|
||||
<string name="short_text177">B</string>
|
||||
<string name="short_text178">B</string>
|
||||
<string name="short_text179">B</string>
|
||||
<string name="short_text180">B</string>
|
||||
<string name="short_text181">B</string>
|
||||
<string name="short_text182">B</string>
|
||||
<string name="short_text183">B</string>
|
||||
<string name="short_text184">B</string>
|
||||
<string name="short_text185">B</string>
|
||||
<string name="short_text186">B</string>
|
||||
<string name="short_text187">B</string>
|
||||
<string name="short_text188">B</string>
|
||||
<string name="short_text189">B</string>
|
||||
<string name="short_text190">B</string>
|
||||
<string name="short_text191">B</string>
|
||||
<string name="short_text192">B</string>
|
||||
<string name="short_text193">B</string>
|
||||
<string name="short_text194">B</string>
|
||||
<string name="short_text195">B</string>
|
||||
<string name="short_text196">B</string>
|
||||
<string name="short_text197">B</string>
|
||||
<string name="short_text198">B</string>
|
||||
<string name="short_text199">B</string>
|
||||
<string name="short_text200">B</string>
|
||||
<string name="short_text201">B</string>
|
||||
<string name="short_text202">B</string>
|
||||
<string name="short_text203">B</string>
|
||||
<string name="short_text204">B</string>
|
||||
<string name="short_text205">B</string>
|
||||
<string name="short_text206">B</string>
|
||||
<string name="short_text207">B</string>
|
||||
<string name="short_text208">B</string>
|
||||
<string name="short_text209">B</string>
|
||||
<string name="short_text210">B</string>
|
||||
<string name="short_text211">B</string>
|
||||
<string name="short_text212">B</string>
|
||||
<string name="short_text213">B</string>
|
||||
<string name="short_text214">B</string>
|
||||
<string name="short_text215">B</string>
|
||||
<string name="short_text216">B</string>
|
||||
<string name="short_text217">B</string>
|
||||
<string name="short_text218">B</string>
|
||||
<string name="short_text219">B</string>
|
||||
<string name="short_text220">B</string>
|
||||
<string name="short_text221">B</string>
|
||||
<string name="short_text222">B</string>
|
||||
<string name="short_text223">B</string>
|
||||
<string name="short_text224">B</string>
|
||||
<string name="short_text225">B</string>
|
||||
<string name="short_text226">B</string>
|
||||
<string name="short_text227">B</string>
|
||||
<string name="short_text228">B</string>
|
||||
<string name="short_text229">B</string>
|
||||
<string name="short_text230">B</string>
|
||||
<string name="short_text231">B</string>
|
||||
<string name="short_text232">B</string>
|
||||
<string name="short_text233">B</string>
|
||||
<string name="short_text234">B</string>
|
||||
<string name="short_text235">B</string>
|
||||
<string name="short_text236">B</string>
|
||||
<string name="short_text237">B</string>
|
||||
<string name="short_text238">B</string>
|
||||
<string name="short_text239">B</string>
|
||||
<string name="short_text240">B</string>
|
||||
<string name="short_text241">B</string>
|
||||
<string name="short_text242">B</string>
|
||||
<string name="short_text243">B</string>
|
||||
<string name="short_text244">B</string>
|
||||
<string name="short_text245">B</string>
|
||||
<string name="short_text246">B</string>
|
||||
<string name="short_text247">B</string>
|
||||
<string name="short_text248">B</string>
|
||||
<string name="short_text249">B</string>
|
||||
<string name="short_text250">B</string>
|
||||
<string name="short_text251">B</string>
|
||||
<string name="short_text252">B</string>
|
||||
<string name="short_text253">B</string>
|
||||
<string name="short_text254">B</string>
|
||||
<string name="short_text255">B</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.om.OverlayManager;
|
||||
import android.os.UserHandle;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.TestPackageInstaller;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.LargeTest;
|
||||
|
||||
import com.android.perftests.core.R;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Benchmarks for {@link android.content.om.OverlayManager}.
|
||||
*/
|
||||
@LargeTest
|
||||
public class OverlayManagerPerfTest {
|
||||
private static final int OVERLAY_PKG_COUNT = 10;
|
||||
private static Context sContext;
|
||||
private static OverlayManager sOverlayManager;
|
||||
private static Executor sExecutor;
|
||||
private static ArrayList<TestPackageInstaller.InstalledPackage> sSmallOverlays =
|
||||
new ArrayList<>();
|
||||
private static ArrayList<TestPackageInstaller.InstalledPackage> sLargeOverlays =
|
||||
new ArrayList<>();
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@BeforeClass
|
||||
public static void classSetUp() throws Exception {
|
||||
sContext = InstrumentationRegistry.getTargetContext();
|
||||
sOverlayManager = new OverlayManager(sContext);
|
||||
sExecutor = (command) -> new Thread(command).start();
|
||||
|
||||
// Install all of the test overlays.
|
||||
TestPackageInstaller installer = new TestPackageInstaller(sContext);
|
||||
for (int i = 0; i < OVERLAY_PKG_COUNT; i++) {
|
||||
sSmallOverlays.add(installer.installPackage("Overlay" + i +".apk"));
|
||||
sLargeOverlays.add(installer.installPackage("LargeOverlay" + i +".apk"));
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void classTearDown() throws Exception {
|
||||
for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
|
||||
overlay.uninstall();
|
||||
}
|
||||
|
||||
for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
|
||||
overlay.uninstall();
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
// Disable all test overlays after each test.
|
||||
for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
|
||||
assertSetEnabled(sContext, overlay.getPackageName(), false);
|
||||
}
|
||||
|
||||
for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
|
||||
assertSetEnabled(sContext, overlay.getPackageName(), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the overlay and waits for the APK path change sto be propagated to the context
|
||||
* AssetManager.
|
||||
*/
|
||||
private void assertSetEnabled(Context context, String overlayPackage, boolean eanabled)
|
||||
throws Exception {
|
||||
sOverlayManager.setEnabled(overlayPackage, true, UserHandle.SYSTEM);
|
||||
|
||||
// Wait for the overlay changes to propagate
|
||||
FutureTask<Boolean> task = new FutureTask<>(() -> {
|
||||
while (true) {
|
||||
for (String path : context.getAssets().getApkPaths()) {
|
||||
if (eanabled == path.contains(overlayPackage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
sExecutor.execute(task);
|
||||
assertTrue("Failed to load overlay " + overlayPackage,
|
||||
task.get(20, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setEnabledWarmCache() throws Exception {
|
||||
String packageName = sSmallOverlays.get(0).getPackageName();
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
assertSetEnabled(sContext, packageName, true);
|
||||
|
||||
// Disable the overlay for the next iteration of the test
|
||||
state.pauseTiming();
|
||||
assertSetEnabled(sContext, packageName, false);
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setEnabledColdCacheSmallOverlay() throws Exception {
|
||||
String packageName = sSmallOverlays.get(0).getPackageName();
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
assertSetEnabled(sContext, packageName, true);
|
||||
|
||||
// Disable the overlay and remove the idmap for the next iteration of the test
|
||||
state.pauseTiming();
|
||||
assertSetEnabled(sContext, packageName, false);
|
||||
sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setEnabledColdCacheLargeOverlay() throws Exception {
|
||||
String packageName = sLargeOverlays.get(0).getPackageName();
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
assertSetEnabled(sContext, packageName, true);
|
||||
|
||||
// Disable the overlay and remove the idmap for the next iteration of the test
|
||||
state.pauseTiming();
|
||||
assertSetEnabled(sContext, packageName, false);
|
||||
sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setEnabledDisable() throws Exception {
|
||||
String packageName = sSmallOverlays.get(0).getPackageName();
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
assertSetEnabled(sContext, packageName, true);
|
||||
state.resumeTiming();
|
||||
|
||||
assertSetEnabled(sContext, packageName, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStringOneSmallOverlay() throws Exception {
|
||||
String packageName = sSmallOverlays.get(0).getPackageName();
|
||||
assertSetEnabled(sContext, packageName, true);
|
||||
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
sContext.getString(R.string.short_text);
|
||||
}
|
||||
|
||||
assertSetEnabled(sContext, packageName, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStringOneLargeOverlay() throws Exception {
|
||||
String packageName = sLargeOverlays.get(0).getPackageName();
|
||||
assertSetEnabled(sContext, packageName, true);
|
||||
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
for (int resId = R.string.short_text000; resId < R.string.short_text255; resId++) {
|
||||
sContext.getString(resId);
|
||||
}
|
||||
}
|
||||
|
||||
assertSetEnabled(sContext, packageName, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStringTenOverlays() throws Exception {
|
||||
// Enable all test overlays
|
||||
for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
|
||||
assertSetEnabled(sContext, overlay.getPackageName(), true);
|
||||
}
|
||||
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
sContext.getString(R.string.short_text);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStringLargeTenOverlays() throws Exception {
|
||||
// Enable all test overlays
|
||||
for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
|
||||
assertSetEnabled(sContext, overlay.getPackageName(), true);
|
||||
}
|
||||
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
for (int resId = R.string.short_text000; resId < R.string.short_text255; resId++) {
|
||||
sContext.getString(resId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.LargeTest;
|
||||
@@ -48,7 +48,7 @@ public class PendingIntentPerfTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = InstrumentationRegistry.getTargetContext();
|
||||
mIntent = StubActivity.createLaunchIntent(mContext);
|
||||
mIntent = PerfTestActivity.createLaunchIntent(mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app;
|
||||
|
||||
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;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.LargeTest;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Benchmarks for {@link android.app.ResourcesManager}.
|
||||
*/
|
||||
@LargeTest
|
||||
public class ResourcesManagerPerfTest {
|
||||
private static Context sContext;
|
||||
private static File sResourcesCompressed;
|
||||
private static File sResourcesUncompressed;
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception {
|
||||
sContext = InstrumentationRegistry.getTargetContext();
|
||||
sResourcesCompressed = copyApkToTemp("LargeResourcesCompressed.apk",
|
||||
"LargeResourcesCompressed.apk");
|
||||
sResourcesUncompressed = copyApkToTemp("LargeResourcesUncompressed.apk",
|
||||
"LargeResourcesUncompressed.apk");
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDown() {
|
||||
Assert.assertTrue(sResourcesCompressed.delete());
|
||||
Assert.assertTrue(sResourcesUncompressed.delete());
|
||||
}
|
||||
|
||||
private static File copyApkToTemp(String inputFileName, String fileName) throws Exception {
|
||||
File file = File.createTempFile(fileName, null, sContext.getCacheDir());
|
||||
try (OutputStream tempOutputStream = new FileOutputStream(file);
|
||||
InputStream is = sContext.getResources().getAssets().openNonAsset(inputFileName)) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int n;
|
||||
while ((n = is.read(buffer)) >= 0) {
|
||||
tempOutputStream.write(buffer, 0, n);
|
||||
}
|
||||
tempOutputStream.flush();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private void getResourcesForPath(String path) {
|
||||
ResourcesManager.getInstance().getResources(null, path, null, null, null,
|
||||
Display.DEFAULT_DISPLAY, null, sContext.getResources().getCompatibilityInfo(),
|
||||
null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getResourcesCached() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
getResourcesForPath(sResourcesCompressed.getPath());
|
||||
while (state.keepRunning()) {
|
||||
getResourcesForPath(sResourcesCompressed.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getResourcesCompressedUncached() {
|
||||
ResourcesManager resourcesManager = ResourcesManager.getInstance();
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
resourcesManager.invalidatePath(sResourcesCompressed.getPath());
|
||||
state.resumeTiming();
|
||||
|
||||
getResourcesForPath(sResourcesCompressed.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getResourcesUncompressedUncached() {
|
||||
ResourcesManager resourcesManager = ResourcesManager.getInstance();
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
resourcesManager.invalidatePath(sResourcesUncompressed.getPath());
|
||||
state.resumeTiming();
|
||||
|
||||
getResourcesForPath(sResourcesUncompressed.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyConfigurationToResourcesLocked() {
|
||||
ResourcesManager resourcesManager = ResourcesManager.getInstance();
|
||||
Configuration c = new Configuration(resourcesManager.getConfiguration());
|
||||
c.uiMode = Configuration.UI_MODE_TYPE_WATCH;
|
||||
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
resourcesManager.applyConfigurationToResources(c, null);
|
||||
|
||||
// Alternate configurations to ensure the set configuration is different each iteration
|
||||
if (c.uiMode == Configuration.UI_MODE_TYPE_WATCH) {
|
||||
c.uiMode = Configuration.UI_MODE_TYPE_TELEVISION;
|
||||
} else {
|
||||
c.uiMode = Configuration.UI_MODE_TYPE_WATCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,15 +18,18 @@ package android.app;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.LargeTest;
|
||||
|
||||
import org.junit.After;
|
||||
import com.android.perftests.core.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -43,36 +46,123 @@ public class ResourcesPerfTest {
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private AssetManager mAsset;
|
||||
private Resources mRes;
|
||||
|
||||
private int mTextId;
|
||||
private int mColorId;
|
||||
private int mIntegerId;
|
||||
private int mLayoutId;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mAsset = new AssetManager();
|
||||
mAsset.addAssetPath("/system/framework/framework-res.apk");
|
||||
mRes = new Resources(mAsset, null, null);
|
||||
|
||||
mTextId = mRes.getIdentifier("cancel", "string", "android");
|
||||
mColorId = mRes.getIdentifier("transparent", "color", "android");
|
||||
mIntegerId = mRes.getIdentifier("config_shortAnimTime", "integer", "android");
|
||||
mLayoutId = mRes.getIdentifier("two_line_list_item", "layout", "android");
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
mRes = context.getResources();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mAsset.close();
|
||||
@Test
|
||||
public void getValue() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
TypedValue value = new TypedValue();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getValue(R.integer.forty_two, value, false /* resolve_refs */);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFrameworkValue() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
TypedValue value = new TypedValue();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getValue(com.android.internal.R.integer.autofill_max_visible_datasets, value,
|
||||
false /* resolve_refs */);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValueString() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
TypedValue value = new TypedValue();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getValue(R.string.long_text, value, false /* resolve_refs */);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFrameworkStringValue() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
TypedValue value = new TypedValue();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getValue(com.android.internal.R.string.cancel, value, false /* resolve_refs */);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getValueManyConfigurations() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
TypedValue value = new TypedValue();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getValue(com.android.internal.R.string.mmcc_illegal_me, value,
|
||||
false /* resolve_refs */);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getText() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getText(mTextId);
|
||||
mRes.getText(R.string.long_text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getFont() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getFont(R.font.samplefont);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getString() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getString(R.string.long_text);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getQuantityString() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getQuantityString(R.plurals.plurals_text, 5);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getQuantityText() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getQuantityText(R.plurals.plurals_text, 5);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTextArray() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getTextArray(R.array.strings);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStringArray() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getStringArray(R.array.strings);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIntegerArray() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getIntArray(R.array.ints);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,15 +170,23 @@ public class ResourcesPerfTest {
|
||||
public void getColor() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getColor(mColorId, null);
|
||||
mRes.getColor(R.color.white, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getInteger() {
|
||||
public void getColorStateList() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getInteger(mIntegerId);
|
||||
mRes.getColorStateList(R.color.color_state_list, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVectorDrawable() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
mRes.getDrawable(R.drawable.vector_drawable01, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +194,7 @@ public class ResourcesPerfTest {
|
||||
public void getLayoutAndTravese() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
try (XmlResourceParser parser = mRes.getLayout(mLayoutId)) {
|
||||
try (XmlResourceParser parser = mRes.getLayout(R.layout.test_relative_layout)) {
|
||||
while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
||||
// Walk the entire tree
|
||||
}
|
||||
@@ -105,4 +203,23 @@ public class ResourcesPerfTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLayoutAndTraverseInvalidateCaches() {
|
||||
mRes.flushLayoutCache();
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
try (XmlResourceParser parser = mRes.getLayout(R.layout.test_relative_layout)) {
|
||||
while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
||||
// Walk the entire tree
|
||||
}
|
||||
} catch (IOException | XmlPullParserException exception) {
|
||||
fail("Parsing of the layout failed. Something is really broken");
|
||||
}
|
||||
|
||||
state.pauseTiming();
|
||||
mRes.flushLayoutCache();
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,19 @@
|
||||
package android.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.UserHandle;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.view.Display;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.LargeTest;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -35,13 +41,69 @@ public class ResourcesThemePerfTest {
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private Context mContext;
|
||||
private int mThemeResId;
|
||||
private Resources.Theme mTheme;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
mTheme = context.getTheme();
|
||||
public void setUp() throws Exception {
|
||||
mContext = InstrumentationRegistry.getTargetContext();
|
||||
mThemeResId = com.android.perftests.core.R.style.Base_V7_Theme_AppCompat;
|
||||
mTheme = mContext.getResources().newTheme();
|
||||
mTheme.applyStyle(mThemeResId, true /* force */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void applyStyle() {
|
||||
Resources.Theme destTheme = mContext.getResources().newTheme();
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
destTheme.applyStyle(mThemeResId, true /* force */);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rebase() {
|
||||
Resources.Theme destTheme = mContext.getResources().newTheme();
|
||||
destTheme.applyStyle(mThemeResId, true /* force */);
|
||||
destTheme.applyStyle(android.R.style.Theme_Material, true /* force */);
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
destTheme.rebase();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setToSameAssetManager() {
|
||||
Resources.Theme destTheme = mContext.getResources().newTheme();
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
destTheme.setTo(mTheme);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setToDifferentAssetManager() throws Exception {
|
||||
// Create a new Resources object with the same asset paths but a different AssetManager
|
||||
PackageManager packageManager = mContext.getApplicationContext().getPackageManager();
|
||||
ApplicationInfo ai = packageManager.getApplicationInfo(mContext.getPackageName(),
|
||||
UserHandle.myUserId());
|
||||
|
||||
ResourcesManager resourcesManager = ResourcesManager.getInstance();
|
||||
Configuration c = resourcesManager.getConfiguration();
|
||||
c.orientation = (c.orientation == Configuration.ORIENTATION_PORTRAIT)
|
||||
? 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);
|
||||
Assert.assertNotEquals(destResources.getAssets(), mContext.getAssets());
|
||||
|
||||
Resources.Theme destTheme = destResources.newTheme();
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
destTheme.setTo(mTheme);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -51,5 +113,4 @@ public class ResourcesThemePerfTest {
|
||||
mTheme.obtainStyledAttributes(android.R.style.Theme_Material, android.R.styleable.View);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ package android.graphics.perftests;
|
||||
import android.graphics.Paint;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
@@ -58,7 +58,8 @@ public class PaintHasGlyphPerfTest {
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule<>(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@@ -26,7 +26,7 @@ import android.graphics.drawable.VectorDrawable;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.BitmapUtils;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
import android.test.suitebuilder.annotation.LargeTest;
|
||||
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
@@ -48,8 +48,8 @@ public class VectorDrawablePerfTest {
|
||||
private int[] mTestHeights = {512, 1024};
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<StubActivity> mActivityRule =
|
||||
new ActivityTestRule(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule<>(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
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 PackageManagerPerfTest {
|
||||
private static final String PERMISSION_NAME_EXISTS =
|
||||
"com.android.perftests.core.TestPermission";
|
||||
private static final String PERMISSION_NAME_DOESNT_EXIST =
|
||||
"com.android.perftests.core.TestBadPermission";
|
||||
private static final ComponentName TEST_ACTIVITY =
|
||||
new ComponentName("com.android.perftests.core", "android.perftests.utils.StubActivity");
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@Test
|
||||
public void testCheckPermissionExists() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
final String packageName = TEST_ACTIVITY.getPackageName();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckPermissionDoesntExist() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
final String packageName = TEST_ACTIVITY.getPackageName();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryIntentActivities() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.queryIntentActivities(intent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPackageInfo() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
final String packageName = TEST_ACTIVITY.getPackageName();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.getPackageInfo(packageName, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetApplicationInfo() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
final String packageName = TEST_ACTIVITY.getPackageName();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.getApplicationInfo(packageName, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetActivityInfo() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.getActivityInfo(TEST_ACTIVITY, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* 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.pm.PackageParser
|
||||
import android.content.pm.PackageParserCacheHelper.ReadHelper
|
||||
import android.content.pm.PackageParserCacheHelper.WriteHelper
|
||||
import android.content.pm.parsing.ParsingPackageImpl
|
||||
import android.content.pm.parsing.ParsingPackageRead
|
||||
import android.content.pm.parsing.ParsingPackageUtils
|
||||
import android.content.pm.parsing.result.ParseInput
|
||||
import android.content.pm.parsing.result.ParseTypeImpl
|
||||
import android.content.res.TypedArray
|
||||
import android.perftests.utils.BenchmarkState
|
||||
import android.perftests.utils.PerfStatusReporter
|
||||
import androidx.test.filters.LargeTest
|
||||
import com.android.internal.util.ConcurrentUtils
|
||||
import libcore.io.IoUtils
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.concurrent.ArrayBlockingQueue
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@LargeTest
|
||||
@RunWith(Parameterized::class)
|
||||
class PackageParsingPerfTest {
|
||||
|
||||
companion object {
|
||||
private const val PARALLEL_QUEUE_CAPACITY = 10
|
||||
private const val PARALLEL_MAX_THREADS = 4
|
||||
|
||||
private const val QUEUE_POLL_TIMEOUT_SECONDS = 5L
|
||||
|
||||
// TODO: Replace this with core version of SYSTEM_PARTITIONS
|
||||
val FOLDERS_TO_TEST = listOf(
|
||||
Environment.getRootDirectory(),
|
||||
Environment.getVendorDirectory(),
|
||||
Environment.getOdmDirectory(),
|
||||
Environment.getOemDirectory(),
|
||||
Environment.getOemDirectory(),
|
||||
Environment.getSystemExtDirectory()
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
fun parameters(): Array<Params> {
|
||||
val apks = FOLDERS_TO_TEST
|
||||
.filter(File::exists)
|
||||
.map(File::walkTopDown)
|
||||
.flatMap(Sequence<File>::asIterable)
|
||||
.filter { it.name.endsWith(".apk") }
|
||||
|
||||
return arrayOf(
|
||||
Params(1, apks) { ParallelParser1(it?.let(::PackageCacher1)) },
|
||||
Params(2, apks) { ParallelParser2(it?.let(::PackageCacher2)) }
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val version: Int,
|
||||
val apks: List<File>,
|
||||
val cacheDirToParser: (File?) -> ParallelParser<*>
|
||||
) {
|
||||
// For test name formatting
|
||||
override fun toString() = "v$version"
|
||||
}
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
var perfStatusReporter = PerfStatusReporter()
|
||||
|
||||
@get:Rule
|
||||
var testFolder = TemporaryFolder()
|
||||
|
||||
@Parameterized.Parameter(0)
|
||||
lateinit var params: Params
|
||||
|
||||
private val state: BenchmarkState get() = perfStatusReporter.benchmarkState
|
||||
private val apks: List<File> get() = params.apks
|
||||
|
||||
@Test
|
||||
fun sequentialNoCache() {
|
||||
params.cacheDirToParser(null).use { parser ->
|
||||
while (state.keepRunning()) {
|
||||
apks.forEach { parser.parse(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sequentialCached() {
|
||||
params.cacheDirToParser(testFolder.newFolder()).use { parser ->
|
||||
// Fill the cache
|
||||
apks.forEach { parser.parse(it) }
|
||||
|
||||
while (state.keepRunning()) {
|
||||
apks.forEach { parser.parse(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parallelNoCache() {
|
||||
params.cacheDirToParser(null).use { parser ->
|
||||
while (state.keepRunning()) {
|
||||
apks.forEach { parser.submit(it) }
|
||||
repeat(apks.size) { parser.take() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parallelCached() {
|
||||
params.cacheDirToParser(testFolder.newFolder()).use { parser ->
|
||||
// Fill the cache
|
||||
apks.forEach { parser.parse(it) }
|
||||
|
||||
while (state.keepRunning()) {
|
||||
apks.forEach { parser.submit(it) }
|
||||
repeat(apks.size) { parser.take() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ParallelParser<PackageType : Parcelable>(
|
||||
private val cacher: PackageCacher<PackageType>? = null
|
||||
) : AutoCloseable {
|
||||
private val queue = ArrayBlockingQueue<Any>(PARALLEL_QUEUE_CAPACITY)
|
||||
private val service = ConcurrentUtils.newFixedThreadPool(
|
||||
PARALLEL_MAX_THREADS, "package-parsing-test",
|
||||
Process.THREAD_PRIORITY_FOREGROUND)
|
||||
|
||||
fun submit(file: File) = service.submit { queue.put(parse(file)) }
|
||||
|
||||
fun take() = queue.poll(QUEUE_POLL_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
|
||||
override fun close() {
|
||||
service.shutdownNow()
|
||||
}
|
||||
|
||||
fun parse(file: File) = cacher?.getCachedResult(file)
|
||||
?: parseImpl(file).also { cacher?.cacheResult(file, it) }
|
||||
|
||||
protected abstract fun parseImpl(file: File): PackageType
|
||||
}
|
||||
|
||||
class ParallelParser1(private val cacher: PackageCacher1? = null)
|
||||
: ParallelParser<PackageParser.Package>(cacher) {
|
||||
val parser = PackageParser().apply {
|
||||
setCallback { true }
|
||||
}
|
||||
|
||||
override fun parseImpl(file: File) = parser.parsePackage(file, 0, cacher != null)
|
||||
}
|
||||
|
||||
class ParallelParser2(cacher: PackageCacher2? = null)
|
||||
: ParallelParser<ParsingPackageRead>(cacher) {
|
||||
val input = ThreadLocal.withInitial {
|
||||
// For testing, just disable enforcement to avoid hooking up to compat framework
|
||||
ParseTypeImpl(ParseInput.Callback { _, _, _ -> false })
|
||||
}
|
||||
val parser = ParsingPackageUtils(false, null, null,
|
||||
object : ParsingPackageUtils.Callback {
|
||||
override fun hasFeature(feature: String) = true
|
||||
|
||||
override fun startParsingPackage(
|
||||
packageName: String,
|
||||
baseCodePath: String,
|
||||
codePath: String,
|
||||
manifestArray: TypedArray,
|
||||
isCoreApp: Boolean
|
||||
) = ParsingPackageImpl(packageName, baseCodePath, codePath, manifestArray)
|
||||
})
|
||||
|
||||
override fun parseImpl(file: File) =
|
||||
parser.parsePackage(input.get()!!.reset(), file, 0).result
|
||||
}
|
||||
|
||||
abstract class PackageCacher<PackageType : Parcelable>(private val cacheDir: File) {
|
||||
|
||||
fun getCachedResult(file: File): PackageType? {
|
||||
val cacheFile = File(cacheDir, file.name)
|
||||
if (!cacheFile.exists()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val bytes = IoUtils.readFileAsByteArray(cacheFile.absolutePath)
|
||||
val parcel = Parcel.obtain().apply {
|
||||
unmarshall(bytes, 0, bytes.size)
|
||||
setDataPosition(0)
|
||||
}
|
||||
ReadHelper(parcel).apply { startAndInstall() }
|
||||
return fromParcel(parcel).also {
|
||||
parcel.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
fun cacheResult(file: File, parsed: Parcelable) {
|
||||
val cacheFile = File(cacheDir, file.name)
|
||||
if (cacheFile.exists()) {
|
||||
if (!cacheFile.delete()) {
|
||||
throw IllegalStateException("Unable to delete cache file: $cacheFile")
|
||||
}
|
||||
}
|
||||
val cacheEntry = toCacheEntry(parsed)
|
||||
return FileOutputStream(cacheFile).use { fos -> fos.write(cacheEntry) }
|
||||
}
|
||||
|
||||
private fun toCacheEntry(pkg: Parcelable): ByteArray {
|
||||
val parcel = Parcel.obtain()
|
||||
val helper = WriteHelper(parcel)
|
||||
pkg.writeToParcel(parcel, 0 /* flags */)
|
||||
helper.finishAndUninstall()
|
||||
return parcel.marshall().also {
|
||||
parcel.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun fromParcel(parcel: Parcel): PackageType
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-implementation of v1's cache, since that's gone in R+.
|
||||
*/
|
||||
class PackageCacher1(cacheDir: File) : PackageCacher<PackageParser.Package>(cacheDir) {
|
||||
override fun fromParcel(parcel: Parcel) = PackageParser.Package(parcel)
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-implementation of the server side PackageCacher, as it's inaccessible here.
|
||||
*/
|
||||
class PackageCacher2(cacheDir: File) : PackageCacher<ParsingPackageRead>(cacheDir) {
|
||||
override fun fromParcel(parcel: Parcel) = ParsingPackageImpl(parcel)
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import android.graphics.Paint;
|
||||
import android.graphics.Paint.FontMetricsInt;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
import android.text.style.ReplacementSpan;
|
||||
import android.util.ArraySet;
|
||||
|
||||
@@ -75,7 +75,8 @@ public class DynamicLayoutPerfTest {
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule<>(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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 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.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;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class CutoutSpecificationBenchmark {
|
||||
private static final String TAG = "CutoutSpecificationBenchmark";
|
||||
|
||||
private static final String BOTTOM_MARKER = "@bottom";
|
||||
private static final String DP_MARKER = "@dp";
|
||||
private static final String RIGHT_MARKER = "@right";
|
||||
private static final String LEFT_MARKER = "@left";
|
||||
|
||||
private static final String DOUBLE_CUTOUT_SPEC = "M 0,0\n"
|
||||
+ "L -72, 0\n"
|
||||
+ "L -69.9940446283, 20.0595537175\n"
|
||||
+ "C -69.1582133885, 28.4178661152 -65.2, 32.0 -56.8, 32.0\n"
|
||||
+ "L 56.8, 32.0\n"
|
||||
+ "C 65.2, 32.0 69.1582133885, 28.4178661152 69.9940446283, 20.0595537175\n"
|
||||
+ "L 72, 0\n"
|
||||
+ "Z\n"
|
||||
+ "@bottom\n"
|
||||
+ "M 0,0\n"
|
||||
+ "L -72, 0\n"
|
||||
+ "L -69.9940446283, -20.0595537175\n"
|
||||
+ "C -69.1582133885, -28.4178661152 -65.2, -32.0 -56.8, -32.0\n"
|
||||
+ "L 56.8, -32.0\n"
|
||||
+ "C 65.2, -32.0 69.1582133885, -28.4178661152 69.9940446283, -20.0595537175\n"
|
||||
+ "L 72, 0\n"
|
||||
+ "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);
|
||||
}
|
||||
|
||||
|
||||
private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
|
||||
final RectF rectF = new RectF();
|
||||
p.computeBounds(rectF, false /* unused */);
|
||||
rectF.round(inoutRect);
|
||||
inoutRegion.op(inoutRect, Region.Op.UNION);
|
||||
}
|
||||
|
||||
private static void oldMethodParsingSpec(String spec, int displayWidth, int displayHeight,
|
||||
float density) {
|
||||
Path p = null;
|
||||
Rect boundTop = null;
|
||||
Rect boundBottom = null;
|
||||
Rect safeInset = new Rect();
|
||||
String bottomSpec = null;
|
||||
if (!TextUtils.isEmpty(spec)) {
|
||||
spec = spec.trim();
|
||||
final float offsetX;
|
||||
if (spec.endsWith(RIGHT_MARKER)) {
|
||||
offsetX = displayWidth;
|
||||
spec = spec.substring(0, spec.length() - RIGHT_MARKER.length()).trim();
|
||||
} else if (spec.endsWith(LEFT_MARKER)) {
|
||||
offsetX = 0;
|
||||
spec = spec.substring(0, spec.length() - LEFT_MARKER.length()).trim();
|
||||
} else {
|
||||
offsetX = displayWidth / 2f;
|
||||
}
|
||||
final boolean inDp = spec.endsWith(DP_MARKER);
|
||||
if (inDp) {
|
||||
spec = spec.substring(0, spec.length() - DP_MARKER.length());
|
||||
}
|
||||
|
||||
if (spec.contains(BOTTOM_MARKER)) {
|
||||
String[] splits = spec.split(BOTTOM_MARKER, 2);
|
||||
spec = splits[0].trim();
|
||||
bottomSpec = splits[1].trim();
|
||||
}
|
||||
|
||||
final Matrix m = new Matrix();
|
||||
final Region r = Region.obtain();
|
||||
if (!spec.isEmpty()) {
|
||||
try {
|
||||
p = PathParser.createPathFromPathData(spec);
|
||||
} catch (Throwable e) {
|
||||
Log.wtf(TAG, "Could not inflate cutout: ", e);
|
||||
}
|
||||
|
||||
if (p != null) {
|
||||
if (inDp) {
|
||||
m.postScale(density, density);
|
||||
}
|
||||
m.postTranslate(offsetX, 0);
|
||||
p.transform(m);
|
||||
|
||||
boundTop = new Rect();
|
||||
toRectAndAddToRegion(p, r, boundTop);
|
||||
safeInset.top = boundTop.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
if (bottomSpec != null) {
|
||||
int bottomInset = 0;
|
||||
Path bottomPath = null;
|
||||
try {
|
||||
bottomPath = PathParser.createPathFromPathData(bottomSpec);
|
||||
} catch (Throwable e) {
|
||||
Log.wtf(TAG, "Could not inflate bottom cutout: ", e);
|
||||
}
|
||||
|
||||
if (bottomPath != null) {
|
||||
// Keep top transform
|
||||
m.postTranslate(0, displayHeight);
|
||||
bottomPath.transform(m);
|
||||
p.addPath(bottomPath);
|
||||
boundBottom = new Rect();
|
||||
toRectAndAddToRegion(bottomPath, r, boundBottom);
|
||||
bottomInset = displayHeight - boundBottom.top;
|
||||
}
|
||||
safeInset.bottom = bottomInset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseByOldMethodForDoubleCutout() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, mDisplayMetrics.widthPixels,
|
||||
mDisplayMetrics.heightPixels, mDisplayMetrics.density);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseByNewMethodForDoubleCutout() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
new CutoutSpecification.Parser(mDisplayMetrics.density,
|
||||
mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)
|
||||
.parse(DOUBLE_CUTOUT_SPEC);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseLongEdgeCutout() {
|
||||
final String spec = "M 0,0\n"
|
||||
+ "H 48\n"
|
||||
+ "V 48\n"
|
||||
+ "H -48\n"
|
||||
+ "Z\n"
|
||||
+ "@left\n"
|
||||
+ "@center_vertical\n"
|
||||
+ "M 0,0\n"
|
||||
+ "H 48\n"
|
||||
+ "V 48\n"
|
||||
+ "H -48\n"
|
||||
+ "Z\n"
|
||||
+ "@left\n"
|
||||
+ "@center_vertical\n"
|
||||
+ "M 0,0\n"
|
||||
+ "H -48\n"
|
||||
+ "V 48\n"
|
||||
+ "H 48\n"
|
||||
+ "Z\n"
|
||||
+ "@right\n"
|
||||
+ "@dp";
|
||||
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
new CutoutSpecification.Parser(mDisplayMetrics.density,
|
||||
mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseShortEdgeCutout() {
|
||||
final String spec = "M 0,0\n"
|
||||
+ "H 48\n"
|
||||
+ "V 48\n"
|
||||
+ "H -48\n"
|
||||
+ "Z\n"
|
||||
+ "@bottom\n"
|
||||
+ "M 0,0\n"
|
||||
+ "H 48\n"
|
||||
+ "V -48\n"
|
||||
+ "H -48\n"
|
||||
+ "Z\n"
|
||||
+ "@dp";
|
||||
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
new CutoutSpecification.Parser(mDisplayMetrics.density,
|
||||
mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
@@ -46,7 +46,8 @@ import java.util.List;
|
||||
public class ViewShowHidePerfTest {
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule mActivityRule = new ActivityTestRule(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule<>(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@@ -18,7 +18,7 @@ package android.widget;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
import android.text.Selection;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View.MeasureSpec;
|
||||
@@ -80,7 +80,8 @@ public class EditTextBackspacePerfTest {
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule<>(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@@ -18,7 +18,7 @@ package android.widget;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
import android.text.Selection;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View.MeasureSpec;
|
||||
@@ -74,7 +74,8 @@ public class EditTextCursorMovementPerfTest {
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule<>(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@@ -19,7 +19,7 @@ package android.widget;
|
||||
import android.app.Activity;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.ViewGroup;
|
||||
@@ -59,7 +59,8 @@ public class EditTextLongTextPerfTest {
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule<>(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@@ -28,7 +28,7 @@ import android.app.Activity;
|
||||
import android.os.Looper;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -72,8 +72,8 @@ public class LayoutPerfTest {
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<StubActivity> mActivityRule =
|
||||
new ActivityTestRule(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@@ -22,7 +22,7 @@ import android.app.Activity;
|
||||
import android.os.Looper;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
@@ -64,8 +64,8 @@ public class TextViewAutoSizeLayoutPerfTest {
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<StubActivity> mActivityRule =
|
||||
new ActivityTestRule(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@@ -18,7 +18,7 @@ package android.widget;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.perftests.utils.PerfTestActivity;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
@@ -56,7 +56,8 @@ public class TextViewSetTextLocalePerfTest {
|
||||
}
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
|
||||
public ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule<>(PerfTestActivity.class);
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@@ -17,6 +17,7 @@ android_test {
|
||||
srcs: ["src/**/*.java"],
|
||||
static_libs: [
|
||||
"androidx.test.rules",
|
||||
"collector-device-lib-platform",
|
||||
"apct-perftests-utils",
|
||||
],
|
||||
platform_apis: true,
|
||||
|
||||
@@ -17,9 +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.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.REAL_GET_TASKS" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="test-file-name" value="MultiUserPerfTests.apk" />
|
||||
<option name="test-file-name" value="MultiUserPerfDummyApp.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||
|
||||
21
apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
Normal file
21
apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2019 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
android_test_helper_app {
|
||||
name: "MultiUserPerfDummyApp",
|
||||
|
||||
srcs: ["src/**/*.java"],
|
||||
|
||||
sdk_version: "test_current",
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2019 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="perftests.multiuser.apps.dummyapp" >
|
||||
|
||||
<application android:label="Perftest Multiuser Dummy App">
|
||||
|
||||
<activity
|
||||
android:name=".DummyForegroundActivity"
|
||||
android:label="Perftest Multiuser Dummy App"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package perftests.multiuser.apps.dummyapp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
|
||||
/** An activity. */
|
||||
public class DummyForegroundActivity extends Activity {
|
||||
private static final String TAG = DummyForegroundActivity.class.getSimpleName();
|
||||
|
||||
public static final int TOP_SLEEP_TIME_MS = 2_000;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
doSleepWhileTop(TOP_SLEEP_TIME_MS);
|
||||
}
|
||||
|
||||
/** Does nothing, but asynchronously. */
|
||||
private void doSleepWhileTop(int sleepTime) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
SystemClock.sleep(sleepTime);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void nothing) {
|
||||
finish();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
@@ -15,19 +15,35 @@
|
||||
*/
|
||||
package android.multiuser;
|
||||
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.IStopUserCallback;
|
||||
import android.app.UserSwitchObserver;
|
||||
import android.app.WaitResult;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.IIntentReceiver;
|
||||
import android.content.IIntentSender;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.IPackageInstaller;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.IProgressListener;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.perftests.utils.ShellHelper;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.LargeTest;
|
||||
@@ -66,13 +82,25 @@ import java.util.concurrent.TimeUnit;
|
||||
public class UserLifecycleTests {
|
||||
private static final String TAG = UserLifecycleTests.class.getSimpleName();
|
||||
|
||||
private final int TIMEOUT_IN_SECOND = 30;
|
||||
private final int CHECK_USER_REMOVED_INTERVAL_MS = 200;
|
||||
private static final int TIMEOUT_IN_SECOND = 30;
|
||||
private static final int CHECK_USER_REMOVED_INTERVAL_MS = 200;
|
||||
|
||||
private static final String DUMMY_PACKAGE_NAME = "perftests.multiuser.apps.dummyapp";
|
||||
|
||||
// Copy of UserSystemPackageInstaller whitelist mode constants.
|
||||
private static final String PACKAGE_WHITELIST_MODE_PROP =
|
||||
"persist.debug.user.package_whitelist_mode";
|
||||
private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0;
|
||||
private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0b001;
|
||||
private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0b100;
|
||||
private static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT = -1;
|
||||
|
||||
private UserManager mUm;
|
||||
private ActivityManager mAm;
|
||||
private IActivityManager mIam;
|
||||
private PackageManager mPm;
|
||||
private ArrayList<Integer> mUsersToRemove;
|
||||
private boolean mHasManagedUserFeature;
|
||||
|
||||
private final BenchmarkRunner mRunner = new BenchmarkRunner();
|
||||
@Rule
|
||||
@@ -85,6 +113,8 @@ public class UserLifecycleTests {
|
||||
mAm = context.getSystemService(ActivityManager.class);
|
||||
mIam = ActivityManager.getService();
|
||||
mUsersToRemove = new ArrayList<>();
|
||||
mPm = context.getPackageManager();
|
||||
mHasManagedUserFeature = mPm.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -99,34 +129,89 @@ public class UserLifecycleTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAndStartUser() throws Exception {
|
||||
public void createUser() {
|
||||
while (mRunner.keepRunning()) {
|
||||
final UserInfo userInfo = mUm.createUser("TestUser", 0);
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
|
||||
mIam.startUserInBackground(userInfo.id);
|
||||
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
|
||||
final int userId = createUserNoFlags();
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userInfo.id);
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAndStartUser() throws Exception {
|
||||
while (mRunner.keepRunning()) {
|
||||
final int userId = createUserNoFlags();
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
|
||||
// Don't use this.startUserInBackgroundAndWaitForUnlock() since only waiting until
|
||||
// ACTION_USER_STARTED.
|
||||
mIam.startUserInBackground(userId);
|
||||
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the time until ACTION_USER_STARTED is received.
|
||||
*/
|
||||
@Test
|
||||
public void startUser() throws Exception {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createUserNoFlags();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
|
||||
mRunner.resumeTiming();
|
||||
|
||||
mIam.startUserInBackground(userId);
|
||||
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the time until unlock listener is triggered and user is unlocked.
|
||||
*/
|
||||
@Test
|
||||
public void startAndUnlockUser() throws Exception {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createUserNoFlags();
|
||||
mRunner.resumeTiming();
|
||||
|
||||
// Waits for UserState.mUnlockProgress.finish().
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void switchUser() throws Exception {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int startUser = mAm.getCurrentUser();
|
||||
final UserInfo userInfo = mUm.createUser("TestUser", 0);
|
||||
final int userId = createUserNoFlags();
|
||||
mRunner.resumeTiming();
|
||||
|
||||
switchUser(userInfo.id);
|
||||
switchUser(userId);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
switchUser(startUser);
|
||||
removeUser(userInfo.id);
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
@@ -176,17 +261,17 @@ public class UserLifecycleTests {
|
||||
public void stopUser() throws Exception {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final UserInfo userInfo = mUm.createUser("TestUser", 0);
|
||||
final int userId = createUserNoFlags();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
|
||||
mIam.startUserInBackground(userInfo.id);
|
||||
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
|
||||
mIam.startUserInBackground(userId);
|
||||
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
|
||||
mRunner.resumeTiming();
|
||||
|
||||
stopUser(userInfo.id, false);
|
||||
stopUser(userId, false);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userInfo.id);
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
@@ -196,64 +281,17 @@ public class UserLifecycleTests {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int startUser = mAm.getCurrentUser();
|
||||
final UserInfo userInfo = mUm.createUser("TestUser", 0);
|
||||
final int userId = createUserNoFlags();
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
registerUserSwitchObserver(null, latch, userInfo.id);
|
||||
registerUserSwitchObserver(null, latch, userId);
|
||||
mRunner.resumeTiming();
|
||||
|
||||
mAm.switchUser(userInfo.id);
|
||||
mAm.switchUser(userId);
|
||||
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
switchUser(startUser);
|
||||
removeUser(userInfo.id);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void managedProfileUnlock() throws Exception {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final UserInfo userInfo = mUm.createProfileForUser("TestUser",
|
||||
UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
|
||||
mRunner.resumeTiming();
|
||||
|
||||
mIam.startUserInBackground(userInfo.id);
|
||||
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userInfo.id);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests starting an already-created, but no-longer-running, profile. */
|
||||
@Test
|
||||
public void managedProfileUnlock_stopped() throws Exception {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final UserInfo userInfo = mUm.createProfileForUser("TestUser",
|
||||
UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
|
||||
// Start the profile initially, then stop it. Similar to setQuietModeEnabled.
|
||||
final CountDownLatch latch1 = new CountDownLatch(1);
|
||||
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch1, userInfo.id);
|
||||
mIam.startUserInBackground(userInfo.id);
|
||||
latch1.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
|
||||
stopUser(userInfo.id, true);
|
||||
|
||||
// Now we restart the profile.
|
||||
final CountDownLatch latch2 = new CountDownLatch(1);
|
||||
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch2, userInfo.id);
|
||||
mRunner.resumeTiming();
|
||||
|
||||
mIam.startUserInBackground(userInfo.id);
|
||||
latch2.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userInfo.id);
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
@@ -263,15 +301,14 @@ public class UserLifecycleTests {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int startUser = mAm.getCurrentUser();
|
||||
final UserInfo userInfo = mUm.createUser("TestUser",
|
||||
UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
|
||||
switchUser(userInfo.id);
|
||||
final int userId = createUserWithFlags(UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
|
||||
switchUser(userId);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
InstrumentationRegistry.getContext().registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Intent.ACTION_USER_STOPPED.equals(intent.getAction()) && intent.getIntExtra(
|
||||
Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userInfo.id) {
|
||||
Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userId) {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
@@ -285,31 +322,270 @@ public class UserLifecycleTests {
|
||||
|
||||
mRunner.pauseTiming();
|
||||
switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
|
||||
removeUser(userInfo.id);
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests creating a new profile. */
|
||||
@Test
|
||||
public void managedProfileStopped() throws Exception {
|
||||
public void managedProfileCreate() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
|
||||
while (mRunner.keepRunning()) {
|
||||
final int userId = createManagedProfile();
|
||||
|
||||
mRunner.pauseTiming();
|
||||
attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests starting (unlocking) a newly-created profile. */
|
||||
@Test
|
||||
public void managedProfileUnlock() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final UserInfo userInfo = mUm.createProfileForUser("TestUser",
|
||||
UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
|
||||
mIam.startUserInBackground(userInfo.id);
|
||||
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
|
||||
final int userId = createManagedProfile();
|
||||
mRunner.resumeTiming();
|
||||
|
||||
stopUser(userInfo.id, true);
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userInfo.id);
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests starting (unlocking) an already-created, but no-longer-running, profile. */
|
||||
@Test
|
||||
public void managedProfileUnlock_stopped() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createManagedProfile();
|
||||
// Start the profile initially, then stop it. Similar to setQuietModeEnabled.
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
stopUser(userId, true);
|
||||
mRunner.resumeTiming();
|
||||
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests starting (unlocking) and launching an already-installed app in a newly-created profile.
|
||||
*/
|
||||
@Test
|
||||
public void managedProfileUnlockAndLaunchApp() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createManagedProfile();
|
||||
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
|
||||
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
|
||||
mRunner.resumeTiming();
|
||||
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
startApp(userId, DUMMY_PACKAGE_NAME);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests starting (unlocking) and launching a previously-launched app
|
||||
* in an already-created, but no-longer-running, profile.
|
||||
* A sort of combination of {@link #managedProfileUnlockAndLaunchApp} and
|
||||
* {@link #managedProfileUnlock_stopped}}.
|
||||
*/
|
||||
@Test
|
||||
public void managedProfileUnlockAndLaunchApp_stopped() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createManagedProfile();
|
||||
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
|
||||
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
startApp(userId, DUMMY_PACKAGE_NAME);
|
||||
stopUser(userId, true);
|
||||
TimeUnit.SECONDS.sleep(1); // Brief cool-down before re-starting profile.
|
||||
mRunner.resumeTiming();
|
||||
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
startApp(userId, DUMMY_PACKAGE_NAME);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests installing a pre-existing app in a newly-created profile. */
|
||||
@Test
|
||||
public void managedProfileInstall() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createManagedProfile();
|
||||
mRunner.resumeTiming();
|
||||
|
||||
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating a new profile, starting (unlocking) it, installing an app,
|
||||
* and launching that app in it.
|
||||
*/
|
||||
@Test
|
||||
public void managedProfileCreateUnlockInstallAndLaunchApp() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
|
||||
mRunner.resumeTiming();
|
||||
|
||||
final int userId = createManagedProfile();
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
installPreexistingApp(userId, DUMMY_PACKAGE_NAME);
|
||||
startApp(userId, DUMMY_PACKAGE_NAME);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests stopping a profile. */
|
||||
@Test
|
||||
public void managedProfileStopped() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createManagedProfile();
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
mRunner.resumeTiming();
|
||||
|
||||
stopUser(userId, true);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
final int origMode = getUserTypePackageWhitelistMode();
|
||||
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE
|
||||
| USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST);
|
||||
|
||||
try {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createManagedProfile();
|
||||
mRunner.resumeTiming();
|
||||
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
} finally {
|
||||
setUserTypePackageWhitelistMode(origMode);
|
||||
}
|
||||
}
|
||||
/** Tests starting (unlocking) a newly-created profile NOT using the user-type-pkg-whitelist. */
|
||||
@Test
|
||||
public void managedProfileUnlock_notUsingWhitelist() throws Exception {
|
||||
assumeTrue(mHasManagedUserFeature);
|
||||
final int origMode = getUserTypePackageWhitelistMode();
|
||||
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE);
|
||||
|
||||
try {
|
||||
while (mRunner.keepRunning()) {
|
||||
mRunner.pauseTiming();
|
||||
final int userId = createManagedProfile();
|
||||
mRunner.resumeTiming();
|
||||
|
||||
startUserInBackgroundAndWaitForUnlock(userId);
|
||||
|
||||
mRunner.pauseTiming();
|
||||
removeUser(userId);
|
||||
mRunner.resumeTiming();
|
||||
}
|
||||
} finally {
|
||||
setUserTypePackageWhitelistMode(origMode);
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a new user, returning its userId. */
|
||||
private int createUserNoFlags() {
|
||||
return createUserWithFlags(/* flags= */ 0);
|
||||
}
|
||||
|
||||
/** Creates a new user with the given flags, returning its userId. */
|
||||
private int createUserWithFlags(int flags) {
|
||||
int userId = mUm.createUser("TestUser", 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",
|
||||
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.");
|
||||
}
|
||||
mUsersToRemove.add(userInfo.id);
|
||||
return userInfo.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start user in background and wait for it to unlock by waiting for
|
||||
* UserState.mUnlockProgress.finish().
|
||||
* <p> To start in foreground instead, see {@link #switchUser(int)}.
|
||||
* <p> This should always be used for profiles since profiles cannot be started in foreground.
|
||||
*/
|
||||
private void startUserInBackgroundAndWaitForUnlock(int userId) {
|
||||
final ProgressWaiter waiter = new ProgressWaiter();
|
||||
try {
|
||||
mIam.startUserInBackgroundWithListener(userId, waiter);
|
||||
boolean success = waiter.waitForFinish(TIMEOUT_IN_SECOND);
|
||||
attestTrue("Failed to start user " + userId + " in background.", success);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "startUserInBackgroundAndWaitForUnlock failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts the given user in the foreground. */
|
||||
private void switchUser(int userId) throws Exception {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
registerUserSwitchObserver(latch, null, userId);
|
||||
@@ -342,7 +618,7 @@ public class UserLifecycleTests {
|
||||
private int initializeNewUserAndSwitchBack(boolean stopNewUser) throws Exception {
|
||||
final int origUser = mAm.getCurrentUser();
|
||||
// First, create and switch to testUser, waiting for its ACTION_USER_UNLOCKED
|
||||
final int testUser = mUm.createUser("TestUser", 0).id;
|
||||
final int testUser = createUserNoFlags();
|
||||
final CountDownLatch latch1 = new CountDownLatch(1);
|
||||
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch1, testUser);
|
||||
mAm.switchUser(testUser);
|
||||
@@ -361,6 +637,46 @@ public class UserLifecycleTests {
|
||||
return testUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the given package in the given user.
|
||||
*/
|
||||
private void installPreexistingApp(int userId, String packageName) throws RemoteException {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
final IntentSender sender = new IntentSender((IIntentSender) new IIntentSender.Stub() {
|
||||
@Override
|
||||
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
|
||||
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
final IPackageInstaller installer = AppGlobals.getPackageManager().getPackageInstaller();
|
||||
installer.installExistingPackage(packageName,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the given package in the given user.
|
||||
* Make sure the keyguard has been dismissed prior to calling.
|
||||
*/
|
||||
private void startApp(int userId, String packageName) throws RemoteException {
|
||||
final Context context = InstrumentationRegistry.getContext();
|
||||
final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null,
|
||||
context.getPackageName(), context.getAttributionTag(),
|
||||
context.getPackageManager().getLaunchIntentForPackage(packageName), null, null,
|
||||
null, 0, 0, null, null, userId);
|
||||
attestTrue("User " + userId + " failed to start " + packageName,
|
||||
result.result == ActivityManager.START_SUCCESS);
|
||||
}
|
||||
|
||||
private void registerUserSwitchObserver(final CountDownLatch switchLatch,
|
||||
final CountDownLatch bootCompleteLatch, final int userId) throws Exception {
|
||||
ActivityManager.getService().registerUserSwitchObserver(
|
||||
@@ -394,6 +710,44 @@ public class UserLifecycleTests {
|
||||
}, UserHandle.of(userId), new IntentFilter(action), null, null);
|
||||
}
|
||||
|
||||
private class ProgressWaiter extends IProgressListener.Stub {
|
||||
private final CountDownLatch mFinishedLatch = new CountDownLatch(1);
|
||||
|
||||
@Override
|
||||
public void onStarted(int id, Bundle extras) {}
|
||||
|
||||
@Override
|
||||
public void onProgress(int id, int progress, Bundle extras) {}
|
||||
|
||||
@Override
|
||||
public void onFinished(int id, Bundle extras) {
|
||||
mFinishedLatch.countDown();
|
||||
}
|
||||
|
||||
public boolean waitForFinish(long timeoutSecs) {
|
||||
try {
|
||||
return mFinishedLatch.await(timeoutSecs, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Log.e(TAG, "Thread interrupted unexpectedly.", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the PACKAGE_WHITELIST_MODE_PROP System Property. */
|
||||
private int getUserTypePackageWhitelistMode() {
|
||||
return SystemProperties.getInt(PACKAGE_WHITELIST_MODE_PROP,
|
||||
USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT);
|
||||
}
|
||||
|
||||
/** Sets the PACKAGE_WHITELIST_MODE_PROP System Property to the given value. */
|
||||
private void setUserTypePackageWhitelistMode(int mode) {
|
||||
String result = ShellHelper.runShellCommand(
|
||||
String.format("setprop %s %d", PACKAGE_WHITELIST_MODE_PROP, mode));
|
||||
attestFalse("Failed to set sysprop " + PACKAGE_WHITELIST_MODE_PROP + ": " + result,
|
||||
result != null && result.contains("Failed"));
|
||||
}
|
||||
|
||||
private void removeUser(int userId) {
|
||||
try {
|
||||
mUm.removeUser(userId);
|
||||
@@ -413,13 +767,13 @@ public class UserLifecycleTests {
|
||||
}
|
||||
}
|
||||
|
||||
private void attestTrue(String message, boolean attestion) {
|
||||
if (!attestion) {
|
||||
private void attestTrue(String message, boolean assertion) {
|
||||
if (!assertion) {
|
||||
Log.w(TAG, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void attestFalse(String message, boolean attestion) {
|
||||
attestTrue(message, !attestion);
|
||||
private void attestFalse(String message, boolean assertion) {
|
||||
attestTrue(message, !assertion);
|
||||
}
|
||||
}
|
||||
|
||||
21
apct-tests/perftests/packagemanager/Android.bp
Normal file
21
apct-tests/perftests/packagemanager/Android.bp
Normal file
@@ -0,0 +1,21 @@
|
||||
android_test {
|
||||
name: "PackageManagerPerfTests",
|
||||
|
||||
srcs: ["src/**/*.java"],
|
||||
|
||||
static_libs: [
|
||||
"platform-compat-test-rules",
|
||||
"androidx.appcompat_appcompat",
|
||||
"androidx.test.rules",
|
||||
"androidx.test.ext.junit",
|
||||
"androidx.annotation_annotation",
|
||||
"apct-perftests-utils",
|
||||
],
|
||||
|
||||
libs: ["android.test.base"],
|
||||
|
||||
platform_apis: true,
|
||||
|
||||
test_suites: ["device-tests"],
|
||||
|
||||
}
|
||||
89
apct-tests/perftests/packagemanager/AndroidManifest.xml
Normal file
89
apct-tests/perftests/packagemanager/AndroidManifest.xml
Normal file
@@ -0,0 +1,89 @@
|
||||
<?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.packagemanager">
|
||||
|
||||
<permission android:name="com.android.perftests.packagemanager.TestPermission" />
|
||||
<uses-permission android:name="com.android.perftests.packagemanager.TestPermission" />
|
||||
|
||||
<queries>
|
||||
<package android:name="com.android.perftests.appenumeration0" />
|
||||
<package android:name="com.android.perftests.appenumeration1" />
|
||||
<package android:name="com.android.perftests.appenumeration2" />
|
||||
<package android:name="com.android.perftests.appenumeration3" />
|
||||
<package android:name="com.android.perftests.appenumeration4" />
|
||||
<package android:name="com.android.perftests.appenumeration5" />
|
||||
<package android:name="com.android.perftests.appenumeration6" />
|
||||
<package android:name="com.android.perftests.appenumeration7" />
|
||||
<package android:name="com.android.perftests.appenumeration8" />
|
||||
<package android:name="com.android.perftests.appenumeration9" />
|
||||
<package android:name="com.android.perftests.appenumeration10" />
|
||||
<package android:name="com.android.perftests.appenumeration11" />
|
||||
<package android:name="com.android.perftests.appenumeration12" />
|
||||
<package android:name="com.android.perftests.appenumeration13" />
|
||||
<package android:name="com.android.perftests.appenumeration14" />
|
||||
<package android:name="com.android.perftests.appenumeration15" />
|
||||
<package android:name="com.android.perftests.appenumeration16" />
|
||||
<package android:name="com.android.perftests.appenumeration17" />
|
||||
<package android:name="com.android.perftests.appenumeration18" />
|
||||
<package android:name="com.android.perftests.appenumeration19" />
|
||||
<package android:name="com.android.perftests.appenumeration20" />
|
||||
<package android:name="com.android.perftests.appenumeration21" />
|
||||
<package android:name="com.android.perftests.appenumeration22" />
|
||||
<package android:name="com.android.perftests.appenumeration23" />
|
||||
<package android:name="com.android.perftests.appenumeration24" />
|
||||
<package android:name="com.android.perftests.appenumeration25" />
|
||||
<package android:name="com.android.perftests.appenumeration26" />
|
||||
<package android:name="com.android.perftests.appenumeration27" />
|
||||
<package android:name="com.android.perftests.appenumeration28" />
|
||||
<package android:name="com.android.perftests.appenumeration29" />
|
||||
<package android:name="com.android.perftests.appenumeration30" />
|
||||
<package android:name="com.android.perftests.appenumeration31" />
|
||||
<package android:name="com.android.perftests.appenumeration32" />
|
||||
<package android:name="com.android.perftests.appenumeration33" />
|
||||
<package android:name="com.android.perftests.appenumeration34" />
|
||||
<package android:name="com.android.perftests.appenumeration35" />
|
||||
<package android:name="com.android.perftests.appenumeration36" />
|
||||
<package android:name="com.android.perftests.appenumeration37" />
|
||||
<package android:name="com.android.perftests.appenumeration38" />
|
||||
<package android:name="com.android.perftests.appenumeration39" />
|
||||
<package android:name="com.android.perftests.appenumeration40" />
|
||||
<package android:name="com.android.perftests.appenumeration41" />
|
||||
<package android:name="com.android.perftests.appenumeration42" />
|
||||
<package android:name="com.android.perftests.appenumeration43" />
|
||||
<package android:name="com.android.perftests.appenumeration44" />
|
||||
<package android:name="com.android.perftests.appenumeration45" />
|
||||
<package android:name="com.android.perftests.appenumeration46" />
|
||||
<package android:name="com.android.perftests.appenumeration47" />
|
||||
<package android:name="com.android.perftests.appenumeration48" />
|
||||
<package android:name="com.android.perftests.appenumeration49" />
|
||||
</queries>
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
<activity android:name="android.perftests.utils.PerfTestActivity">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.perftests.packagemanager.PERFTEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.perftests.packagemanager"/>
|
||||
|
||||
</manifest>
|
||||
88
apct-tests/perftests/packagemanager/AndroidTest.xml
Normal file
88
apct-tests/perftests/packagemanager/AndroidTest.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<?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 PackageManagerPerfTests 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" />
|
||||
</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" />
|
||||
</target_preparer>
|
||||
|
||||
<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" />
|
||||
</metrics_collector>
|
||||
</configuration>
|
||||
314
apct-tests/perftests/packagemanager/apps/query-all/Android.bp
Normal file
314
apct-tests/perftests/packagemanager/apps/query-all/Android.bp
Normal file
@@ -0,0 +1,314 @@
|
||||
// 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.
|
||||
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll0",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration0",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll1",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration1",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll2",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration2",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll3",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration3",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll4",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration4",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll5",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration5",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll6",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration6",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll7",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration7",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll8",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration8",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll9",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration9",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll10",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration10",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll11",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration11",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll12",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration12",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll13",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration13",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll14",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration14",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll15",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration15",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll16",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration16",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll17",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration17",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll18",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration18",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll19",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration19",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll20",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration20",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll21",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration21",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll22",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration22",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll23",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration23",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll24",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration24",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll25",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration25",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll26",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration26",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll27",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration27",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll28",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration28",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll29",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration29",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll30",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration30",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll31",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration31",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll32",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration32",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll33",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration33",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll34",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration34",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll35",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration35",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll36",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration36",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll37",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration37",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll38",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration38",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll39",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration39",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll40",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration40",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll41",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration41",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll42",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration42",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll43",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration43",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll44",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration44",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll45",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration45",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll46",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration46",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll47",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration47",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll48",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration48",
|
||||
]
|
||||
}
|
||||
android_test_helper_app {
|
||||
name: "QueriesAll49",
|
||||
aaptflags: [
|
||||
"--rename-manifest-package com.android.perftests.appenumeration49",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?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.appenumeration">
|
||||
|
||||
<application android:hasCode="false" >
|
||||
<activity android:name="android.perftests.utils.PerfTestActivity">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.perftests.packagemanager.PERFTEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<queries>
|
||||
<package android:name="com.android.perftests.appenumeration0" />
|
||||
<package android:name="com.android.perftests.appenumeration1" />
|
||||
<package android:name="com.android.perftests.appenumeration2" />
|
||||
<package android:name="com.android.perftests.appenumeration3" />
|
||||
<package android:name="com.android.perftests.appenumeration4" />
|
||||
<package android:name="com.android.perftests.appenumeration5" />
|
||||
<package android:name="com.android.perftests.appenumeration6" />
|
||||
<package android:name="com.android.perftests.appenumeration7" />
|
||||
<package android:name="com.android.perftests.appenumeration8" />
|
||||
<package android:name="com.android.perftests.appenumeration9" />
|
||||
<package android:name="com.android.perftests.appenumeration10" />
|
||||
<package android:name="com.android.perftests.appenumeration11" />
|
||||
<package android:name="com.android.perftests.appenumeration12" />
|
||||
<package android:name="com.android.perftests.appenumeration13" />
|
||||
<package android:name="com.android.perftests.appenumeration14" />
|
||||
<package android:name="com.android.perftests.appenumeration15" />
|
||||
<package android:name="com.android.perftests.appenumeration16" />
|
||||
<package android:name="com.android.perftests.appenumeration17" />
|
||||
<package android:name="com.android.perftests.appenumeration18" />
|
||||
<package android:name="com.android.perftests.appenumeration19" />
|
||||
<package android:name="com.android.perftests.appenumeration20" />
|
||||
<package android:name="com.android.perftests.appenumeration21" />
|
||||
<package android:name="com.android.perftests.appenumeration22" />
|
||||
<package android:name="com.android.perftests.appenumeration23" />
|
||||
<package android:name="com.android.perftests.appenumeration24" />
|
||||
<package android:name="com.android.perftests.appenumeration25" />
|
||||
<package android:name="com.android.perftests.appenumeration26" />
|
||||
<package android:name="com.android.perftests.appenumeration27" />
|
||||
<package android:name="com.android.perftests.appenumeration28" />
|
||||
<package android:name="com.android.perftests.appenumeration29" />
|
||||
<package android:name="com.android.perftests.appenumeration30" />
|
||||
<package android:name="com.android.perftests.appenumeration31" />
|
||||
<package android:name="com.android.perftests.appenumeration32" />
|
||||
<package android:name="com.android.perftests.appenumeration33" />
|
||||
<package android:name="com.android.perftests.appenumeration34" />
|
||||
<package android:name="com.android.perftests.appenumeration35" />
|
||||
<package android:name="com.android.perftests.appenumeration36" />
|
||||
<package android:name="com.android.perftests.appenumeration37" />
|
||||
<package android:name="com.android.perftests.appenumeration38" />
|
||||
<package android:name="com.android.perftests.appenumeration39" />
|
||||
<package android:name="com.android.perftests.appenumeration40" />
|
||||
<package android:name="com.android.perftests.appenumeration41" />
|
||||
<package android:name="com.android.perftests.appenumeration42" />
|
||||
<package android:name="com.android.perftests.appenumeration43" />
|
||||
<package android:name="com.android.perftests.appenumeration44" />
|
||||
<package android:name="com.android.perftests.appenumeration45" />
|
||||
<package android:name="com.android.perftests.appenumeration46" />
|
||||
<package android:name="com.android.perftests.appenumeration47" />
|
||||
<package android:name="com.android.perftests.appenumeration48" />
|
||||
<package android:name="com.android.perftests.appenumeration49" />
|
||||
</queries>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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 libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
|
||||
import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
|
||||
|
||||
import android.compat.testing.PlatformCompatChangeRule;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class PackageManagerPerfTest {
|
||||
private static final String PERMISSION_NAME_EXISTS =
|
||||
"com.android.perftests.packagemanager.TestPermission";
|
||||
private static final String PERMISSION_NAME_DOESNT_EXIST =
|
||||
"com.android.perftests.packagemanager.TestBadPermission";
|
||||
private static final ComponentName TEST_ACTIVITY =
|
||||
new ComponentName("com.android.perftests.packagemanager",
|
||||
"android.perftests.utils.PerfTestActivity");
|
||||
|
||||
@Rule
|
||||
public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@Rule
|
||||
public final PlatformCompatChangeRule mPlatformCompatChangeRule =
|
||||
new PlatformCompatChangeRule();
|
||||
|
||||
public PackageManagerPerfTest() throws PackageManager.NameNotFoundException {
|
||||
final Context context = InstrumentationRegistry.getInstrumentation().getContext();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
PackageManager.disableApplicationInfoCache();
|
||||
PackageManager.disablePackageInfoCache();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testCheckPermissionExists() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm =
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
|
||||
final String packageName = TEST_ACTIVITY.getPackageName();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testCheckPermissionExistsWithFiltering() {
|
||||
testCheckPermissionExists();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testCheckPermissionDoesntExist() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm =
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
|
||||
final String packageName = TEST_ACTIVITY.getPackageName();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testCheckPermissionDoesntExistWithFiltering() {
|
||||
testCheckPermissionDoesntExist();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testQueryIntentActivities() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm =
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
|
||||
final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.queryIntentActivities(intent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testQueryIntentActivitiesWithFiltering() {
|
||||
testQueryIntentActivities();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testGetPackageInfo() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm =
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.getPackageInfo(TEST_ACTIVITY.getPackageName(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testGetPackageInfoWithFiltering() throws Exception {
|
||||
testGetPackageInfo();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testGetApplicationInfo() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm =
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.getApplicationInfo(TEST_ACTIVITY.getPackageName(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testGetApplicationInfoWithFiltering() throws Exception {
|
||||
testGetApplicationInfo();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testGetActivityInfo() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm =
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.getActivityInfo(TEST_ACTIVITY, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testGetActivityInfoWithFiltering() throws Exception {
|
||||
testGetActivityInfo();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testGetInstalledPackages() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm =
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.getInstalledPackages(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
|
||||
public void testGetInstalledPackagesWithFiltering() throws Exception {
|
||||
testGetInstalledPackages();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
set -e
|
||||
make TextClassifierPerfTests perf-setup.sh
|
||||
build/soong/soong_ui.bash --make-mode TextClassifierPerfTests perf-setup.sh
|
||||
adb install ${OUT}/testcases/TextClassifierPerfTests/arm64/TextClassifierPerfTests.apk
|
||||
adb shell cmd package compile -m speed -f com.android.perftests.textclassifier
|
||||
adb push ${OUT}/obj/EXECUTABLES/perf-setup.sh_intermediates/perf-setup.sh /data/local/tmp/
|
||||
|
||||
@@ -18,57 +18,82 @@ package android.view.textclassifier;
|
||||
import android.content.Context;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.SettingsHelper;
|
||||
import android.provider.Settings;
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.LargeTest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
@LargeTest
|
||||
public class TextClassificationManagerPerfTest {
|
||||
private static final String WRITE_DEVICE_CONFIG_PERMISSION =
|
||||
"android.permission.WRITE_DEVICE_CONFIG";
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private String mOriginalSystemTextclassifierStatus;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
InstrumentationRegistry.getInstrumentation().getUiAutomation()
|
||||
.adoptShellPermissionIdentity(
|
||||
WRITE_DEVICE_CONFIG_PERMISSION);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
InstrumentationRegistry
|
||||
.getInstrumentation()
|
||||
.getUiAutomation()
|
||||
.dropShellPermissionIdentity();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
// Saves config original value.
|
||||
mOriginalSystemTextclassifierStatus = DeviceConfig.getProperty(
|
||||
DeviceConfig.NAMESPACE_TEXTCLASSIFIER, "system_textclassifier_enabled");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
SettingsHelper.delete(
|
||||
SettingsHelper.NAMESPACE_GLOBAL, Settings.Global.TEXT_CLASSIFIER_CONSTANTS);
|
||||
// Restores config original value.
|
||||
enableSystemTextclassifier(mOriginalSystemTextclassifierStatus);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTextClassifier_systemTextClassifierDisabled() {
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
SettingsHelper.set(
|
||||
SettingsHelper.NAMESPACE_GLOBAL,
|
||||
Settings.Global.TEXT_CLASSIFIER_CONSTANTS,
|
||||
"system_textclassifier_enabled=false");
|
||||
enableSystemTextclassifier(String.valueOf(false));
|
||||
TextClassificationManager textClassificationManager =
|
||||
context.getSystemService(TextClassificationManager.class);
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
textClassificationManager.getTextClassifier();
|
||||
textClassificationManager.invalidateForTesting();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTextClassifier_systemTextClassifierEnabled() {
|
||||
Context context = InstrumentationRegistry.getTargetContext();
|
||||
SettingsHelper.set(
|
||||
SettingsHelper.NAMESPACE_GLOBAL,
|
||||
Settings.Global.TEXT_CLASSIFIER_CONSTANTS,
|
||||
"system_textclassifier_enabled=true");
|
||||
enableSystemTextclassifier(String.valueOf(true));
|
||||
TextClassificationManager textClassificationManager =
|
||||
context.getSystemService(TextClassificationManager.class);
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
textClassificationManager.getTextClassifier();
|
||||
textClassificationManager.invalidateForTesting();
|
||||
}
|
||||
}
|
||||
|
||||
private void enableSystemTextclassifier(String enabled) {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
|
||||
"system_textclassifier_enabled", enabled, /* makeDefault */ false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,22 @@
|
||||
|
||||
package android.perftests.utils;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
import android.os.Bundle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -53,6 +63,47 @@ import java.util.concurrent.TimeUnit;
|
||||
public final class ManualBenchmarkState {
|
||||
private static final String TAG = ManualBenchmarkState.class.getSimpleName();
|
||||
|
||||
@Target(ElementType.ANNOTATION_TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface StatsReport {
|
||||
int FLAG_MEDIAN = 0x00000001;
|
||||
int FLAG_MEAN = 0x00000002;
|
||||
int FLAG_MIN = 0x00000004;
|
||||
int FLAG_MAX = 0x00000008;
|
||||
int FLAG_STDDEV = 0x00000010;
|
||||
int FLAG_COEFFICIENT_VAR = 0x00000020;
|
||||
int FLAG_ITERATION = 0x00000040;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@IntDef(value = {
|
||||
FLAG_MEDIAN,
|
||||
FLAG_MEAN,
|
||||
FLAG_MIN,
|
||||
FLAG_MAX,
|
||||
FLAG_STDDEV,
|
||||
FLAG_COEFFICIENT_VAR,
|
||||
FLAG_ITERATION,
|
||||
})
|
||||
@interface Flag {}
|
||||
|
||||
/** Defines which type of statistics should output. */
|
||||
@Flag int flags() default -1;
|
||||
/** An array with value 0~100 to provide the percentiles. */
|
||||
int[] percentiles() default {};
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** It means the entire {@link StatsReport} is not given. */
|
||||
private static final int DEFAULT_STATS_REPORT = -2;
|
||||
|
||||
// TODO: Tune these values.
|
||||
// warm-up for duration
|
||||
private static final long WARMUP_DURATION_NS = TimeUnit.SECONDS.toNanos(5);
|
||||
@@ -67,24 +118,68 @@ public final class ManualBenchmarkState {
|
||||
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.
|
||||
|
||||
private long mWarmupDurationNs = WARMUP_DURATION_NS;
|
||||
private long mTargetTestDurationNs = TARGET_TEST_DURATION_NS;
|
||||
private long mWarmupStartTime = 0;
|
||||
private int mWarmupIterations = 0;
|
||||
|
||||
private int mMaxIterations = 0;
|
||||
|
||||
/**
|
||||
* Additinal 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;
|
||||
|
||||
// Individual duration in nano seconds.
|
||||
private ArrayList<Long> mResults = new ArrayList<>();
|
||||
|
||||
/** @see #addExtraResult(String, long) */
|
||||
private ArrayMap<String, ArrayList<Long>> mExtraResults;
|
||||
|
||||
private final List<Long> mTmpDurations = Arrays.asList(0L);
|
||||
|
||||
// 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;
|
||||
|
||||
private int mStatsReportFlags =
|
||||
StatsReport.FLAG_MEDIAN | StatsReport.FLAG_MEAN | StatsReport.FLAG_STDDEV;
|
||||
private int[] mStatsReportPercentiles = {90 , 95};
|
||||
|
||||
private boolean shouldReport(int statsReportFlag) {
|
||||
return (mStatsReportFlags & statsReportFlag) != 0;
|
||||
}
|
||||
|
||||
void configure(ManualBenchmarkTest testAnnotation) {
|
||||
if (testAnnotation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final long warmupDurationNs = testAnnotation.warmupDurationNs();
|
||||
if (warmupDurationNs >= 0) {
|
||||
mWarmupDurationNs = warmupDurationNs;
|
||||
}
|
||||
final long targetTestDurationNs = testAnnotation.targetTestDurationNs();
|
||||
if (targetTestDurationNs >= 0) {
|
||||
mTargetTestDurationNs = targetTestDurationNs;
|
||||
}
|
||||
final StatsReport statsReport = testAnnotation.statsReport();
|
||||
if (statsReport != null && statsReport.flags() != DEFAULT_STATS_REPORT) {
|
||||
mStatsReportFlags = statsReport.flags();
|
||||
mStatsReportPercentiles = statsReport.percentiles();
|
||||
}
|
||||
}
|
||||
|
||||
private void beginBenchmark(long warmupDuration, int iterations) {
|
||||
mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
|
||||
mMaxIterations = (int) (mTargetTestDurationNs / (warmupDuration / iterations));
|
||||
mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
|
||||
Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
|
||||
mState = RUNNING;
|
||||
@@ -99,29 +194,58 @@ public final class ManualBenchmarkState {
|
||||
if (duration < 0) {
|
||||
throw new RuntimeException("duration is negative: " + duration);
|
||||
}
|
||||
mTmpDurations.set(0, duration);
|
||||
return keepRunning(mTmpDurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the {@link #keepRunning(long)} but accepts a list of durations
|
||||
*/
|
||||
public boolean keepRunning(List<Long> durations) {
|
||||
switch (mState) {
|
||||
case NOT_STARTED:
|
||||
mState = WARMUP;
|
||||
mWarmupStartTime = System.nanoTime();
|
||||
return true;
|
||||
case WARMUP: {
|
||||
if (ArrayUtils.isEmpty(durations)) {
|
||||
return true;
|
||||
}
|
||||
final long timeSinceStartingWarmup = System.nanoTime() - mWarmupStartTime;
|
||||
++mWarmupIterations;
|
||||
mWarmupIterations += durations.size();
|
||||
if (mWarmupIterations >= WARMUP_MIN_ITERATIONS
|
||||
&& timeSinceStartingWarmup >= WARMUP_DURATION_NS) {
|
||||
&& timeSinceStartingWarmup >= mWarmupDurationNs) {
|
||||
beginBenchmark(timeSinceStartingWarmup, mWarmupIterations);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case RUNNING: {
|
||||
mResults.add(duration);
|
||||
if (ArrayUtils.isEmpty(durations)) {
|
||||
return true;
|
||||
}
|
||||
mResults.addAll(durations);
|
||||
final boolean keepRunning = mResults.size() < mMaxIterations;
|
||||
if (!keepRunning) {
|
||||
mStats = new Stats(mResults);
|
||||
if (mMaxCustomizedIterations > 0 && mCustomizedIterationListener != null) {
|
||||
mState = RUNNING_CUSTOMIZED;
|
||||
mCustomizedIterationListener.onStart(mCustomizedIterations);
|
||||
return true;
|
||||
}
|
||||
mState = FINISHED;
|
||||
}
|
||||
return keepRunning;
|
||||
}
|
||||
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:
|
||||
@@ -129,31 +253,112 @@ public final class ManualBenchmarkState {
|
||||
}
|
||||
}
|
||||
|
||||
private String summaryLine() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Summary: ");
|
||||
sb.append("median=").append(mStats.getMedian()).append("ns, ");
|
||||
sb.append("mean=").append(mStats.getMean()).append("ns, ");
|
||||
sb.append("min=").append(mStats.getMin()).append("ns, ");
|
||||
sb.append("max=").append(mStats.getMax()).append("ns, ");
|
||||
sb.append("sigma=").append(mStats.getStandardDeviation()).append(", ");
|
||||
sb.append("iteration=").append(mResults.size()).append(", ");
|
||||
sb.append("values=").append(mResults.toString());
|
||||
/**
|
||||
* @return {@code true} if the benchmark is in warmup state. It can be used to skip the
|
||||
* operations or measurements that are unnecessary while the test isn't running the
|
||||
* actual benchmark.
|
||||
*/
|
||||
public boolean isWarmingUp() {
|
||||
return mState == WARMUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds additional result while this benchmark isn't warming up or running in customized state.
|
||||
* It is used when a sequence of operations is executed consecutively, the duration of each
|
||||
* operation can also be recorded.
|
||||
*/
|
||||
public void addExtraResult(String key, long duration) {
|
||||
if (isWarmingUp() || mState == RUNNING_CUSTOMIZED) {
|
||||
return;
|
||||
}
|
||||
if (mExtraResults == null) {
|
||||
mExtraResults = new ArrayMap<>();
|
||||
}
|
||||
mExtraResults.computeIfAbsent(key, k -> new ArrayList<>()).add(duration);
|
||||
}
|
||||
|
||||
private static String summaryLine(String key, Stats stats, ArrayList<Long> results) {
|
||||
final StringBuilder sb = new StringBuilder(key);
|
||||
sb.append(" Summary: ");
|
||||
sb.append("median=").append(stats.getMedian()).append("ns, ");
|
||||
sb.append("mean=").append(stats.getMean()).append("ns, ");
|
||||
sb.append("min=").append(stats.getMin()).append("ns, ");
|
||||
sb.append("max=").append(stats.getMax()).append("ns, ");
|
||||
sb.append("sigma=").append(stats.getStandardDeviation()).append(", ");
|
||||
sb.append("iteration=").append(results.size()).append(", ");
|
||||
sb.append("values=");
|
||||
if (results.size() > 100) {
|
||||
sb.append(results.subList(0, 100)).append(" ...");
|
||||
} else {
|
||||
sb.append(results);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void fillStatus(Bundle status, String key, Stats stats) {
|
||||
if (shouldReport(StatsReport.FLAG_ITERATION)) {
|
||||
status.putLong(key + "_iteration", stats.getSize());
|
||||
}
|
||||
if (shouldReport(StatsReport.FLAG_MEDIAN)) {
|
||||
status.putLong(key + "_median", stats.getMedian());
|
||||
}
|
||||
if (shouldReport(StatsReport.FLAG_MEAN)) {
|
||||
status.putLong(key + "_mean", Math.round(stats.getMean()));
|
||||
}
|
||||
if (shouldReport(StatsReport.FLAG_MIN)) {
|
||||
status.putLong(key + "_min", stats.getMin());
|
||||
}
|
||||
if (shouldReport(StatsReport.FLAG_MAX)) {
|
||||
status.putLong(key + "_max", stats.getMax());
|
||||
}
|
||||
if (mStatsReportPercentiles != null) {
|
||||
for (int percentile : mStatsReportPercentiles) {
|
||||
status.putLong(key + "_percentile" + percentile, stats.getPercentile(percentile));
|
||||
}
|
||||
}
|
||||
if (shouldReport(StatsReport.FLAG_STDDEV)) {
|
||||
status.putLong(key + "_stddev", Math.round(stats.getStandardDeviation()));
|
||||
}
|
||||
if (shouldReport(StatsReport.FLAG_COEFFICIENT_VAR)) {
|
||||
status.putLong(key + "_cv",
|
||||
Math.round((100 * stats.getStandardDeviation() / stats.getMean())));
|
||||
}
|
||||
}
|
||||
|
||||
public void sendFullStatusReport(Instrumentation instrumentation, String key) {
|
||||
if (mState != FINISHED) {
|
||||
throw new IllegalStateException("The benchmark hasn't finished");
|
||||
}
|
||||
Log.i(TAG, key + summaryLine());
|
||||
Log.i(TAG, summaryLine(key, mStats, mResults));
|
||||
final Bundle status = new Bundle();
|
||||
status.putLong(key + "_median", mStats.getMedian());
|
||||
status.putLong(key + "_mean", (long) mStats.getMean());
|
||||
status.putLong(key + "_percentile90", mStats.getPercentile90());
|
||||
status.putLong(key + "_percentile95", mStats.getPercentile95());
|
||||
status.putLong(key + "_stddev", (long) mStats.getStandardDeviation());
|
||||
fillStatus(status, key, mStats);
|
||||
if (mExtraResults != null) {
|
||||
for (int i = 0; i < mExtraResults.size(); i++) {
|
||||
final String subKey = key + "_" + mExtraResults.keyAt(i);
|
||||
final ArrayList<Long> results = mExtraResults.valueAt(i);
|
||||
final Stats stats = new Stats(results);
|
||||
Log.i(TAG, summaryLine(subKey, stats, results));
|
||||
fillStatus(status, subKey, stats);
|
||||
}
|
||||
}
|
||||
instrumentation.sendStatus(Activity.RESULT_OK, status);
|
||||
}
|
||||
}
|
||||
|
||||
/** The annotation to customize the test, e.g. the duration of warm-up and target test. */
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ManualBenchmarkTest {
|
||||
long warmupDurationNs() default -1;
|
||||
long targetTestDurationNs() default -1;
|
||||
StatsReport statsReport() default @StatsReport(flags = DEFAULT_STATS_REPORT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package android.perftests.utils;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
@@ -59,15 +59,15 @@ public class PerfManualStatusReporter implements TestRule {
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
mState.configure(description.getAnnotation(ManualBenchmarkState.ManualBenchmarkTest.class));
|
||||
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
base.evaluate();
|
||||
|
||||
mState.sendFullStatusReport(InstrumentationRegistry.getInstrumentation(),
|
||||
description.getMethodName());
|
||||
mState.sendFullStatusReport(getInstrumentation(), description.getMethodName());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.perftests.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
* A simple activity used for testing, e.g. performance of activity switching, or as a base
|
||||
* container of testing view.
|
||||
*/
|
||||
public class PerfTestActivity extends Activity {
|
||||
public static final String INTENT_EXTRA_KEEP_SCREEN_ON = "keep_screen_on";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getIntent().getBooleanExtra(INTENT_EXTRA_KEEP_SCREEN_ON, false)) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
|
||||
public static Intent createLaunchIntent(Context context) {
|
||||
final Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setClass(context, PerfTestActivity.class);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
@@ -16,23 +16,27 @@
|
||||
|
||||
package android.perftests.utils;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Stats {
|
||||
private long mMedian, mMin, mMax, mPercentile90, mPercentile95;
|
||||
private long mMedian, mMin, mMax;
|
||||
private double mMean, mStandardDeviation;
|
||||
private final List<Long> mValues;
|
||||
|
||||
/* Calculate stats in constructor. */
|
||||
public Stats(List<Long> values) {
|
||||
// make a copy since we're modifying it
|
||||
values = new ArrayList<>(values);
|
||||
final int size = values.size();
|
||||
if (size < 2) {
|
||||
throw new IllegalArgumentException("At least two results are necessary.");
|
||||
}
|
||||
|
||||
// Make a copy since we're modifying it.
|
||||
mValues = values = new ArrayList<>(values);
|
||||
|
||||
Collections.sort(values);
|
||||
|
||||
mMin = values.get(0);
|
||||
@@ -40,8 +44,6 @@ public class Stats {
|
||||
|
||||
mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
|
||||
values.get(size / 2);
|
||||
mPercentile90 = getPercentile(values, 90);
|
||||
mPercentile95 = getPercentile(values, 95);
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
long result = values.get(i);
|
||||
@@ -56,6 +58,10 @@ public class Stats {
|
||||
mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1));
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return mValues.size();
|
||||
}
|
||||
|
||||
public double getMean() {
|
||||
return mMean;
|
||||
}
|
||||
@@ -76,12 +82,8 @@ public class Stats {
|
||||
return mStandardDeviation;
|
||||
}
|
||||
|
||||
public long getPercentile90() {
|
||||
return mPercentile90;
|
||||
}
|
||||
|
||||
public long getPercentile95() {
|
||||
return mPercentile95;
|
||||
public long getPercentile(@IntRange(from = 0, to = 100) int percentile) {
|
||||
return getPercentile(mValues, percentile);
|
||||
}
|
||||
|
||||
private static long getPercentile(List<Long> values, int percentile) {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.perftests.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class StubActivity extends Activity {
|
||||
public static Intent createLaunchIntent(Context context) {
|
||||
final Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setClass(context, StubActivity.class);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.perftests.utils;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* Installs packages included within the assets directory.
|
||||
*/
|
||||
public class TestPackageInstaller {
|
||||
private static final String LOG_TAG = "TestPackageInstaller";
|
||||
private static final String BROADCAST_ACTION =
|
||||
"com.android.perftests.core.ACTION_INSTALL_COMMIT";
|
||||
|
||||
private final Context mContext;
|
||||
public TestPackageInstaller(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Installs an APK located at the specified path in the assets directory.
|
||||
**/
|
||||
public InstalledPackage installPackage(String resourceName) throws IOException,
|
||||
InterruptedException {
|
||||
Log.d(LOG_TAG, "Installing resource APK '" + resourceName + "'");
|
||||
LocalBroadcastReceiver intentSender = new LocalBroadcastReceiver(mContext);
|
||||
|
||||
// Initialize the package install session.
|
||||
PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
|
||||
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
|
||||
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
|
||||
params.setInstallAsInstantApp(false);
|
||||
int sessionId = packageInstaller.createSession(params);
|
||||
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
|
||||
|
||||
// Copy the apk to the install session.
|
||||
try (OutputStream os = session.openWrite("TestPackageInstaller", 0, -1);
|
||||
InputStream is = mContext.getResources().getAssets().openNonAsset(resourceName)) {
|
||||
if (is == null) {
|
||||
throw new IOException("Resource " + resourceName + " not found");
|
||||
}
|
||||
byte[] buffer = new byte[4096];
|
||||
int n;
|
||||
while ((n = is.read(buffer)) >= 0) {
|
||||
os.write(buffer, 0, n);
|
||||
}
|
||||
}
|
||||
|
||||
session.commit(intentSender.getIntentSender(sessionId));
|
||||
session.close();
|
||||
|
||||
// Retrieve the results of the installation.
|
||||
Intent intent = intentSender.getIntentSenderResult();
|
||||
int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
|
||||
PackageInstaller.STATUS_FAILURE);
|
||||
Assert.assertEquals(PackageInstaller.STATUS_SUCCESS, status);
|
||||
String packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME);
|
||||
return new InstalledPackage(sessionId, packageName);
|
||||
}
|
||||
|
||||
public class InstalledPackage {
|
||||
private int mSessionId;
|
||||
private String mPackageName;
|
||||
|
||||
private InstalledPackage(int sessionId, String packageName) {
|
||||
mSessionId = sessionId;
|
||||
mPackageName = packageName;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
public void uninstall() throws Exception {
|
||||
Log.d(LOG_TAG, "Uninstalling package '" + mPackageName + "'");
|
||||
LocalBroadcastReceiver intentSender = new LocalBroadcastReceiver(mContext);
|
||||
PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
|
||||
packageInstaller.uninstall(mPackageName, intentSender.getIntentSender(mSessionId));
|
||||
int status = intentSender.getIntentSenderResult()
|
||||
.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
|
||||
Assert.assertEquals(PackageInstaller.STATUS_SUCCESS, status);
|
||||
}
|
||||
}
|
||||
|
||||
private class LocalBroadcastReceiver extends BroadcastReceiver {
|
||||
private final BlockingQueue<Intent> mIntentSenderResults = new LinkedBlockingQueue<>();
|
||||
private final Context mContext;
|
||||
|
||||
private LocalBroadcastReceiver(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
mIntentSenderResults.add(intent);
|
||||
}
|
||||
|
||||
IntentSender getIntentSender(int sessionId) {
|
||||
String action = BROADCAST_ACTION + "." + sessionId;
|
||||
IntentFilter filter = new IntentFilter(action);
|
||||
mContext.registerReceiver(this, filter);
|
||||
|
||||
Intent intent = new Intent(action);
|
||||
PendingIntent pending = PendingIntent.getBroadcast(mContext, sessionId, intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
return pending.getIntentSender();
|
||||
}
|
||||
|
||||
Intent getIntentSenderResult() throws InterruptedException {
|
||||
return mIntentSenderResults.take();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.perftests.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Utility to get the slice of tracing_mark_write S,F,B,E (Async: start, finish, Sync: begin, end).
|
||||
* Use {@link #visit(String)} to process the trace in text form. The filtered results can be
|
||||
* obtained by {@link #forAllSlices(BiConsumer)}.
|
||||
*
|
||||
* @see android.os.Trace
|
||||
*/
|
||||
public class TraceMarkParser {
|
||||
/** All slices by the name of {@link TraceMarkLine}. */
|
||||
private final Map<String, List<TraceMarkSlice>> mSlicesMap = new HashMap<>();
|
||||
/** The nested depth of each task-pid. */
|
||||
private final Map<String, Integer> mDepthMap = new HashMap<>();
|
||||
/** The start trace lines that haven't matched the corresponding end. */
|
||||
private final Map<String, TraceMarkLine> mPendingStarts = new HashMap<>();
|
||||
|
||||
private final Predicate<TraceMarkLine> mTraceLineFilter;
|
||||
|
||||
private static final long MICROS_PER_SECOND = 1000L * 1000L;
|
||||
|
||||
public TraceMarkParser(Predicate<TraceMarkLine> traceLineFilter) {
|
||||
mTraceLineFilter = traceLineFilter;
|
||||
}
|
||||
|
||||
/** Only accept the trace event with the given names. */
|
||||
public TraceMarkParser(String... traceNames) {
|
||||
this(line -> {
|
||||
for (String name : traceNames) {
|
||||
if (name.equals(line.name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/** Computes {@link TraceMarkSlice} by the given trace line. */
|
||||
public void visit(String textTraceLine) {
|
||||
final TraceMarkLine line = TraceMarkLine.parse(textTraceLine);
|
||||
if (line == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (line.isAsync) {
|
||||
// Async-trace contains name in the start and finish event.
|
||||
if (mTraceLineFilter.test(line)) {
|
||||
if (line.isBegin) {
|
||||
mPendingStarts.put(line.name, line);
|
||||
} else {
|
||||
final TraceMarkLine start = mPendingStarts.remove(line.name);
|
||||
if (start != null) {
|
||||
addSlice(start, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int depth = 1;
|
||||
if (line.isBegin) {
|
||||
final Integer existingDepth = mDepthMap.putIfAbsent(line.taskPid, 1);
|
||||
if (existingDepth != null) {
|
||||
mDepthMap.put(line.taskPid, depth = existingDepth + 1);
|
||||
}
|
||||
// Sync-trace only contains name in the begin event.
|
||||
if (mTraceLineFilter.test(line)) {
|
||||
mPendingStarts.put(getSyncPendingStartKey(line, depth), line);
|
||||
}
|
||||
} else {
|
||||
final Integer existingDepth = mDepthMap.get(line.taskPid);
|
||||
if (existingDepth != null) {
|
||||
depth = existingDepth;
|
||||
mDepthMap.put(line.taskPid, existingDepth - 1);
|
||||
}
|
||||
final TraceMarkLine begin = mPendingStarts.remove(getSyncPendingStartKey(line, depth));
|
||||
if (begin != null) {
|
||||
addSlice(begin, line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getSyncPendingStartKey(TraceMarkLine line, int depth) {
|
||||
return line.taskPid + "@" + depth;
|
||||
}
|
||||
|
||||
private void addSlice(TraceMarkLine begin, TraceMarkLine end) {
|
||||
mSlicesMap.computeIfAbsent(
|
||||
begin.name, k -> new ArrayList<>()).add(new TraceMarkSlice(begin, end));
|
||||
}
|
||||
|
||||
public void forAllSlices(BiConsumer<String, List<TraceMarkSlice>> consumer) {
|
||||
for (Map.Entry<String, List<TraceMarkSlice>> entry : mSlicesMap.entrySet()) {
|
||||
consumer.accept(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mSlicesMap.clear();
|
||||
mDepthMap.clear();
|
||||
mPendingStarts.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
forAllSlices((key, slices) -> {
|
||||
double totalMs = 0;
|
||||
for (TraceMarkSlice s : slices) {
|
||||
totalMs += s.getDurationInSeconds() * 1000;
|
||||
}
|
||||
sb.append(key).append(" count=").append(slices.size()).append(" avg=")
|
||||
.append(totalMs / slices.size()).append("ms\n");
|
||||
});
|
||||
|
||||
if (!mPendingStarts.isEmpty()) {
|
||||
sb.append("[Warning] Unresolved events:").append(mPendingStarts).append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static double microsecondToSeconds(long ms) {
|
||||
return (ms * 1.0d) / MICROS_PER_SECOND;
|
||||
}
|
||||
|
||||
public static class TraceMarkSlice {
|
||||
public final TraceMarkLine begin;
|
||||
public final TraceMarkLine end;
|
||||
|
||||
TraceMarkSlice(TraceMarkLine begin, TraceMarkLine end) {
|
||||
this.begin = begin;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public double getDurationInSeconds() {
|
||||
return microsecondToSeconds(end.timestamp - begin.timestamp);
|
||||
}
|
||||
|
||||
public long getDurationInMicroseconds() {
|
||||
return end.timestamp - begin.timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// taskPid timestamp name
|
||||
// # Async:
|
||||
// Binder:129_F-349 ( 1296) [003] ...1 12.2776: tracing_mark_write: S|1296|launching: a.test|0
|
||||
// android.anim-135 ( 1296) [005] ...1 12.3361: tracing_mark_write: F|1296|launching: a.test|0
|
||||
// # Normal:
|
||||
// Binder:129_6-315 ( 1296) [007] ...1 97.4576: tracing_mark_write: B|1296|relayoutWindow: xxx
|
||||
// ... there may have other nested begin/end
|
||||
// Binder:129_6-315 ( 1296) [007] ...1 97.4580: tracing_mark_write: E|1296
|
||||
public static class TraceMarkLine {
|
||||
static final String EVENT_KEYWORD = ": tracing_mark_write: ";
|
||||
static final char ASYNC_START = 'S';
|
||||
static final char ASYNC_FINISH = 'F';
|
||||
static final char SYNC_BEGIN = 'B';
|
||||
static final char SYNC_END = 'E';
|
||||
|
||||
public final String taskPid;
|
||||
public final long timestamp; // in microseconds
|
||||
public final String name;
|
||||
public final boolean isAsync;
|
||||
public final boolean isBegin;
|
||||
|
||||
TraceMarkLine(String rawLine, int typePos, int type) throws IllegalArgumentException {
|
||||
taskPid = rawLine.substring(0, rawLine.indexOf('(')).trim();
|
||||
final int timeEnd = rawLine.indexOf(':', taskPid.length());
|
||||
if (timeEnd < 0) {
|
||||
throw new IllegalArgumentException("Timestamp end not found");
|
||||
}
|
||||
final int timeBegin = rawLine.lastIndexOf(' ', timeEnd);
|
||||
if (timeBegin < 0) {
|
||||
throw new IllegalArgumentException("Timestamp start not found");
|
||||
}
|
||||
timestamp = parseMicroseconds(rawLine.substring(timeBegin, timeEnd));
|
||||
isAsync = type == ASYNC_START || type == ASYNC_FINISH;
|
||||
isBegin = type == ASYNC_START || type == SYNC_BEGIN;
|
||||
|
||||
if (!isAsync && !isBegin) {
|
||||
name = "";
|
||||
} else {
|
||||
// Get the position of the second '|' from "S|1234|name".
|
||||
final int nameBegin = rawLine.indexOf('|', typePos + 2) + 1;
|
||||
if (nameBegin == 0) {
|
||||
throw new IllegalArgumentException("Name begin not found");
|
||||
}
|
||||
if (isAsync) {
|
||||
// Get the name from "S|1234|name|0".
|
||||
name = rawLine.substring(nameBegin, rawLine.lastIndexOf('|'));
|
||||
} else {
|
||||
name = rawLine.substring(nameBegin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static TraceMarkLine parse(String rawLine) {
|
||||
final int eventPos = rawLine.indexOf(EVENT_KEYWORD);
|
||||
if (eventPos < 0) {
|
||||
return null;
|
||||
}
|
||||
final int typePos = eventPos + EVENT_KEYWORD.length();
|
||||
if (typePos >= rawLine.length()) {
|
||||
return null;
|
||||
}
|
||||
final int type = rawLine.charAt(typePos);
|
||||
if (type != ASYNC_START && type != ASYNC_FINISH
|
||||
&& type != SYNC_BEGIN && type != SYNC_END) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new TraceMarkLine(rawLine, typePos, type);
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the timestamp from atrace output, the format will be like:
|
||||
* 84962.920719 where the decimal part will be always exactly 6 digits.
|
||||
* ^^^^^ ^^^^^^
|
||||
* | |
|
||||
* sec microsec
|
||||
*/
|
||||
static long parseMicroseconds(String line) {
|
||||
int end = line.length();
|
||||
long t = 0;
|
||||
for (int i = 0; i < end; i++) {
|
||||
char c = line.charAt(i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
t = t * 10 + (c - '0');
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TraceMarkLine{pid=" + taskPid + " time="
|
||||
+ microsecondToSeconds(timestamp) + " name=" + name
|
||||
+ " async=" + isAsync + " begin=" + isBegin + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
26
apct-tests/perftests/windowmanager/Android.bp
Normal file
26
apct-tests/perftests/windowmanager/Android.bp
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
android_test {
|
||||
name: "WmPerfTests",
|
||||
srcs: ["src/**/*.java"],
|
||||
static_libs: [
|
||||
"androidx.test.rules",
|
||||
"androidx.annotation_annotation",
|
||||
"apct-perftests-utils",
|
||||
],
|
||||
test_suites: ["device-tests"],
|
||||
platform_apis: true,
|
||||
certificate: "platform",
|
||||
}
|
||||
32
apct-tests/perftests/windowmanager/AndroidManifest.xml
Normal file
32
apct-tests/perftests/windowmanager/AndroidManifest.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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.wm">
|
||||
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
<activity android:name="android.perftests.utils.PerfTestActivity">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.perftests.core.PERFTEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.perftests.wm"/>
|
||||
</manifest>
|
||||
33
apct-tests/perftests/windowmanager/AndroidTest.xml
Normal file
33
apct-tests/perftests/windowmanager/AndroidTest.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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 WmPerfTests 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="WmPerfTests.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||
<option name="package" value="com.android.perftests.wm" />
|
||||
<option name="hidden-api-checks" value="false"/>
|
||||
</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" />
|
||||
</metrics_collector>
|
||||
</configuration>
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.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;
|
||||
import android.perftests.utils.PerfManualStatusReporter;
|
||||
import android.perftests.utils.TraceMarkParser;
|
||||
import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.lifecycle.Stage;
|
||||
|
||||
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.concurrent.TimeUnit;
|
||||
|
||||
/** Measure the performance of internal methods in window manager service by trace tag. */
|
||||
@LargeTest
|
||||
public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
|
||||
private static final String TAG = InternalWindowOperationPerfTest.class.getSimpleName();
|
||||
|
||||
@Rule
|
||||
public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
|
||||
|
||||
@Rule
|
||||
public final PerfTestActivityRule mActivityRule = new PerfTestActivityRule();
|
||||
|
||||
private final TraceMarkParser mTraceMarkParser = new TraceMarkParser(
|
||||
"applyPostLayoutPolicy",
|
||||
"applySurfaceChanges",
|
||||
"AppTransitionReady",
|
||||
"closeSurfaceTransactiom",
|
||||
"openSurfaceTransaction",
|
||||
"performLayout",
|
||||
"performSurfacePlacement",
|
||||
"prepareSurfaces",
|
||||
"updateInputWindows",
|
||||
"WSA#startAnimation",
|
||||
"activityIdle",
|
||||
"activityPaused",
|
||||
"activityStopped",
|
||||
"activityDestroyed",
|
||||
"finishActivity",
|
||||
"startActivityInner");
|
||||
|
||||
@Test
|
||||
@ManualBenchmarkTest(
|
||||
targetTestDurationNs = 20 * TIME_1_S_IN_NS,
|
||||
statsReport = @StatsReport(
|
||||
flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
|
||||
| StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR))
|
||||
public void testLaunchAndFinishActivity() throws Throwable {
|
||||
final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
long measuredTimeNs = 0;
|
||||
boolean isTraceStarted = false;
|
||||
|
||||
while (state.keepRunning(measuredTimeNs)) {
|
||||
if (!isTraceStarted && !state.isWarmingUp()) {
|
||||
startAsyncAtrace();
|
||||
isTraceStarted = true;
|
||||
}
|
||||
final long startTime = SystemClock.elapsedRealtimeNanos();
|
||||
mActivityRule.launchActivity();
|
||||
mActivityRule.finishActivity();
|
||||
mActivityRule.waitForIdleSync(Stage.DESTROYED);
|
||||
measuredTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
}
|
||||
|
||||
stopAsyncAtrace();
|
||||
|
||||
mTraceMarkParser.forAllSlices((key, slices) -> {
|
||||
for (TraceMarkSlice slice : slices) {
|
||||
state.addExtraResult(key, (long) (slice.getDurationInSeconds() * NANOS_PER_S));
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
mTraceMarkParser.visit(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.wm;
|
||||
|
||||
import static android.perftests.utils.ManualBenchmarkState.StatsReport;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
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.app.IActivityTaskManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
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.util.Pair;
|
||||
import android.view.IRecentsAnimationController;
|
||||
import android.view.IRecentsAnimationRunner;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.runner.lifecycle.Stage;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
@LargeTest
|
||||
public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
|
||||
private static Intent sRecentsIntent;
|
||||
|
||||
@Rule
|
||||
public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
|
||||
|
||||
@Rule
|
||||
public final PerfTestActivityRule mActivityRule =
|
||||
new PerfTestActivityRule(true /* launchActivity */);
|
||||
|
||||
private long mMeasuredTimeNs;
|
||||
|
||||
/**
|
||||
* Used to skip each test method if there is error. It cannot be raised in static setup because
|
||||
* that will break the amount of target method count.
|
||||
*/
|
||||
private static Exception sSetUpClassException;
|
||||
|
||||
@Parameterized.Parameter(0)
|
||||
public int intervalBetweenOperations;
|
||||
|
||||
@Parameterized.Parameters(name = "interval{0}ms")
|
||||
public static Collection<Object[]> getParameters() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ 0 },
|
||||
{ 100 },
|
||||
{ 300 },
|
||||
});
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() {
|
||||
// Get the permission to invoke startRecentsActivity.
|
||||
sUiAutomation.adoptShellPermissionIdentity();
|
||||
|
||||
final Context context = getInstrumentation().getContext();
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
final ComponentName defaultHome = pm.getHomeActivities(new ArrayList<>());
|
||||
|
||||
try {
|
||||
final ComponentName recentsComponent =
|
||||
ComponentName.unflattenFromString(context.getResources().getString(
|
||||
com.android.internal.R.string.config_recentsComponentName));
|
||||
final int enabledState = pm.getComponentEnabledSetting(recentsComponent);
|
||||
Assume.assumeThat(enabledState, anyOf(
|
||||
is(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT),
|
||||
is(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)));
|
||||
|
||||
final boolean homeIsRecents =
|
||||
recentsComponent.getPackageName().equals(defaultHome.getPackageName());
|
||||
sRecentsIntent =
|
||||
new Intent().setComponent(homeIsRecents ? defaultHome : recentsComponent);
|
||||
} catch (Exception e) {
|
||||
sSetUpClassException = e;
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
sSetUpClassException = null;
|
||||
try {
|
||||
// Recents activity may stop app switches. Restore the state to avoid affecting
|
||||
// the next test.
|
||||
ActivityManager.resumeAppSwitches();
|
||||
} catch (RemoteException ignored) {
|
||||
}
|
||||
sUiAutomation.dropShellPermissionIdentity();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Assume.assumeNoException(sSetUpClassException);
|
||||
}
|
||||
|
||||
/** Simulate the timing of touch. */
|
||||
private void makeInterval() {
|
||||
SystemClock.sleep(intervalBetweenOperations);
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* Steps:
|
||||
* (1) Start recents activity (only make it visible).
|
||||
* (2) Finish animation, take turns to execute (a), (b).
|
||||
* (a) Move recents activity to top.
|
||||
* ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_TOP})
|
||||
* Move test app to top by startActivityFromRecents.
|
||||
* (b) Cancel (it is similar to swipe a little distance and give up to enter recents).
|
||||
* ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_ORIGINAL_POSITION})
|
||||
* (3) Loop (1).
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
@ManualBenchmarkTest(
|
||||
warmupDurationNs = TIME_1_S_IN_NS,
|
||||
targetTestDurationNs = TIME_5_S_IN_NS,
|
||||
statsReport = @StatsReport(flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
|
||||
| StatsReport.FLAG_COEFFICIENT_VAR))
|
||||
public void testRecentsAnimation() throws Throwable {
|
||||
final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final IActivityTaskManager atm = ActivityTaskManager.getService();
|
||||
|
||||
final ArrayList<Pair<String, Boolean>> finishCases = new ArrayList<>();
|
||||
// Real launch the recents activity.
|
||||
finishCases.add(new Pair<>("finishMoveToTop", true));
|
||||
// Return to the original top.
|
||||
finishCases.add(new Pair<>("finishCancel", false));
|
||||
|
||||
// Ensure startRecentsActivity won't be called before finishing the animation.
|
||||
final Semaphore recentsSemaphore = new Semaphore(1);
|
||||
|
||||
final int testActivityTaskId = mActivityRule.getActivity().getTaskId();
|
||||
final IRecentsAnimationRunner.Stub anim = new IRecentsAnimationRunner.Stub() {
|
||||
int mIteration;
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(IRecentsAnimationController controller,
|
||||
RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
|
||||
Rect homeContentInsets, Rect minimizedHomeBounds) throws RemoteException {
|
||||
final Pair<String, Boolean> finishCase = finishCases.get(mIteration++ % 2);
|
||||
final boolean moveRecentsToTop = finishCase.second;
|
||||
makeInterval();
|
||||
|
||||
long startTime = SystemClock.elapsedRealtimeNanos();
|
||||
controller.finish(moveRecentsToTop, false /* sendUserLeaveHint */);
|
||||
final long elapsedTimeNsOfFinish = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
mMeasuredTimeNs += elapsedTimeNsOfFinish;
|
||||
state.addExtraResult(finishCase.first, elapsedTimeNsOfFinish);
|
||||
|
||||
if (moveRecentsToTop) {
|
||||
mActivityRule.waitForIdleSync(Stage.STOPPED);
|
||||
|
||||
startTime = SystemClock.elapsedRealtimeNanos();
|
||||
atm.startActivityFromRecents(testActivityTaskId, null /* options */);
|
||||
final long elapsedTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
mMeasuredTimeNs += elapsedTimeNs;
|
||||
state.addExtraResult("startFromRecents", elapsedTimeNs);
|
||||
|
||||
mActivityRule.waitForIdleSync(Stage.RESUMED);
|
||||
}
|
||||
|
||||
makeInterval();
|
||||
recentsSemaphore.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCanceled(TaskSnapshot taskSnapshot) throws RemoteException {
|
||||
Assume.assumeNoException(
|
||||
new AssertionError("onAnimationCanceled should not be called"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskAppeared(RemoteAnimationTarget app) throws RemoteException {
|
||||
/* no-op */
|
||||
}
|
||||
};
|
||||
|
||||
recentsSemaphore.tryAcquire();
|
||||
while (state.keepRunning(mMeasuredTimeNs)) {
|
||||
mMeasuredTimeNs = 0;
|
||||
|
||||
final long startTime = SystemClock.elapsedRealtimeNanos();
|
||||
atm.startRecentsActivity(sRecentsIntent, null /* unused */, 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.wm;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
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.util.MergedConfiguration;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.IWindow;
|
||||
import android.view.IWindowSession;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.ActivityTestRule;
|
||||
|
||||
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.function.IntSupplier;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
@LargeTest
|
||||
public class RelayoutPerfTest extends WindowManagerPerfTestBase {
|
||||
private int mIteration;
|
||||
|
||||
@Rule
|
||||
public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@Rule
|
||||
public final ActivityTestRule<PerfTestActivity> mActivityRule =
|
||||
new ActivityTestRule<>(PerfTestActivity.class);
|
||||
|
||||
/** This is only a placement to match the input parameters from {@link #getParameters}. */
|
||||
@Parameterized.Parameter(0)
|
||||
public String testName;
|
||||
|
||||
/** The visibilities to loop for relayout. */
|
||||
@Parameterized.Parameter(1)
|
||||
public int[] visibilities;
|
||||
|
||||
/**
|
||||
* Each row will be mapped into {@link #testName} and {@link #visibilities} of a new test
|
||||
* instance according to the index of the parameter.
|
||||
*/
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
public static Collection<Object[]> getParameters() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ "Visible", new int[] { View.VISIBLE } },
|
||||
{ "Invisible~Visible", new int[] { View.INVISIBLE, View.VISIBLE } },
|
||||
{ "Gone~Visible", new int[] { View.GONE, View.VISIBLE } },
|
||||
{ "Gone~Invisible", new int[] { View.GONE, View.INVISIBLE } }
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelayout() throws Throwable {
|
||||
final Activity activity = mActivityRule.getActivity();
|
||||
final ContentView contentView = new ContentView(activity);
|
||||
mActivityRule.runOnUiThread(() -> activity.setContentView(contentView));
|
||||
getInstrumentation().waitForIdleSync();
|
||||
|
||||
final RelayoutRunner relayoutRunner = new RelayoutRunner(activity, contentView.getWindow(),
|
||||
() -> visibilities[mIteration++ % visibilities.length]);
|
||||
relayoutRunner.runBenchmark(mPerfStatusReporter.getBenchmarkState());
|
||||
}
|
||||
|
||||
/** A dummy view to get IWindow. */
|
||||
private static class ContentView extends LinearLayout {
|
||||
ContentView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IWindow getWindow() {
|
||||
return super.getWindow();
|
||||
}
|
||||
}
|
||||
|
||||
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 MergedConfiguration mOutMergedConfiguration = new MergedConfiguration();
|
||||
final InsetsState mOutInsetsState = new InsetsState();
|
||||
final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
|
||||
final IWindow mWindow;
|
||||
final View mView;
|
||||
final WindowManager.LayoutParams mParams;
|
||||
final int mWidth;
|
||||
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;
|
||||
|
||||
RelayoutRunner(Activity activity, IWindow window, IntSupplier visibilitySupplier) {
|
||||
mWindow = window;
|
||||
mView = activity.getWindow().getDecorView();
|
||||
mParams = (WindowManager.LayoutParams) mView.getLayoutParams();
|
||||
mWidth = mView.getMeasuredWidth();
|
||||
mHeight = mView.getMeasuredHeight();
|
||||
mOutSurfaceControl = mView.getViewRootImpl().getSurfaceControl();
|
||||
mViewVisibility = visibilitySupplier;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.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;
|
||||
import android.view.InsetsState;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
|
||||
import androidx.test.filters.LargeTest;
|
||||
|
||||
import com.android.internal.view.BaseIWindow;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
@LargeTest
|
||||
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();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() {
|
||||
sUiAutomation.dropShellPermissionIdentity();
|
||||
}
|
||||
|
||||
/** The last {@link #PROFILED_ITERATIONS} will provide the information of method profiling. */
|
||||
@Override
|
||||
public void onStart(int iteration) {
|
||||
startProfiling(WindowAddRemovePerfTest.class.getSimpleName()
|
||||
+ "_MethodTracing_" + iteration + ".trace");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinished(int iteration) {
|
||||
stopProfiling();
|
||||
}
|
||||
|
||||
@Test
|
||||
@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);
|
||||
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 mOutInsetsState = new InsetsState();
|
||||
final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
|
||||
|
||||
TestWindow() {
|
||||
mLayoutParams.setTitle(TestWindow.class.getName());
|
||||
mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
// Simulate as common phone window.
|
||||
mLayoutParams.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
|
||||
}
|
||||
|
||||
void runBenchmark(ManualBenchmarkState state) throws RemoteException {
|
||||
final IWindowSession session = WindowManagerGlobal.getWindowSession();
|
||||
long elapsedTimeNs = 0;
|
||||
while (state.keepRunning(elapsedTimeNs)) {
|
||||
// InputChannel cannot be reused.
|
||||
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);
|
||||
final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
state.addExtraResult("add", elapsedTimeNsOfAdd);
|
||||
|
||||
startTime = SystemClock.elapsedRealtimeNanos();
|
||||
session.remove(this);
|
||||
final long elapsedTimeNsOfRemove = SystemClock.elapsedRealtimeNanos() - startTime;
|
||||
state.addExtraResult("remove", elapsedTimeNsOfRemove);
|
||||
|
||||
elapsedTimeNs = elapsedTimeNsOfAdd + elapsedTimeNsOfRemove;
|
||||
inputChannel.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.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 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;
|
||||
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
|
||||
* trace to external storage.
|
||||
*/
|
||||
static final File BASE_OUT_PATH = new File("/data/local/CorePerfTests");
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes shell command with reading the output. It may also used to block until the current
|
||||
* command is completed.
|
||||
*/
|
||||
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);
|
||||
private final LifecycleListener mLifecycleListener = new LifecycleListener();
|
||||
|
||||
PerfTestActivityRule() {
|
||||
this(false /* launchActivity */);
|
||||
}
|
||||
|
||||
PerfTestActivityRule(boolean launchActivity) {
|
||||
super(PerfTestActivity.class, false /* initialTouchMode */, launchActivity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
final Statement wrappedStatement = new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
ActivityLifecycleMonitorRegistry.getInstance()
|
||||
.addLifecycleCallback(mLifecycleListener);
|
||||
base.evaluate();
|
||||
ActivityLifecycleMonitorRegistry.getInstance()
|
||||
.removeLifecycleCallback(mLifecycleListener);
|
||||
}
|
||||
};
|
||||
return super.apply(wrappedStatement, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Intent getActivityIntent() {
|
||||
return mStartIntent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PerfTestActivity launchActivity(Intent intent) {
|
||||
final PerfTestActivity activity = super.launchActivity(intent);
|
||||
mLifecycleListener.setTargetActivity(activity);
|
||||
return activity;
|
||||
}
|
||||
|
||||
PerfTestActivity launchActivity() {
|
||||
return launchActivity(mStartIntent);
|
||||
}
|
||||
|
||||
void waitForIdleSync(Stage state) {
|
||||
mLifecycleListener.waitForIdleSync(state);
|
||||
}
|
||||
}
|
||||
|
||||
static class LifecycleListener implements ActivityLifecycleCallback {
|
||||
private Activity mTargetActivity;
|
||||
private Stage mWaitingStage;
|
||||
private Stage mReceivedStage;
|
||||
|
||||
void setTargetActivity(Activity activity) {
|
||||
mTargetActivity = activity;
|
||||
mReceivedStage = mWaitingStage = null;
|
||||
}
|
||||
|
||||
void waitForIdleSync(Stage stage) {
|
||||
synchronized (this) {
|
||||
if (stage != mReceivedStage) {
|
||||
mWaitingStage = stage;
|
||||
try {
|
||||
wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS));
|
||||
} catch (InterruptedException impossible) { }
|
||||
}
|
||||
mWaitingStage = mReceivedStage = null;
|
||||
}
|
||||
getInstrumentation().waitForIdleSync();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityLifecycleChanged(Activity activity, Stage stage) {
|
||||
if (mTargetActivity != activity) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
mReceivedStage = stage;
|
||||
if (mWaitingStage == mReceivedStage) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,10 @@ java_defaults {
|
||||
stubs_library_visibility: ["//visibility:public"],
|
||||
|
||||
// Hide impl library and stub sources
|
||||
impl_library_visibility: [":__package__"],
|
||||
impl_library_visibility: [
|
||||
":__package__",
|
||||
"//frameworks/base", // For framework-all
|
||||
],
|
||||
stubs_source_visibility: ["//visibility:private"],
|
||||
|
||||
// Collates API usages from each module for further analysis.
|
||||
@@ -158,6 +161,9 @@ stubs_defaults {
|
||||
api_file: "api/current.txt",
|
||||
removed_api_file: "api/removed.txt",
|
||||
},
|
||||
api_lint: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
dist: {
|
||||
targets: ["sdk", "win_sdk"],
|
||||
@@ -181,6 +187,9 @@ stubs_defaults {
|
||||
api_file: "api/system-current.txt",
|
||||
removed_api_file: "api/system-removed.txt",
|
||||
},
|
||||
api_lint: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
dist: {
|
||||
targets: ["sdk", "win_sdk"],
|
||||
@@ -193,6 +202,7 @@ java_defaults {
|
||||
installable: false,
|
||||
sdk_version: "module_current",
|
||||
libs: [ "stub-annotations" ],
|
||||
java_version: "1.8",
|
||||
dist: {
|
||||
targets: ["sdk", "win_sdk"],
|
||||
dir: "apistubs/android/public",
|
||||
@@ -204,6 +214,7 @@ java_defaults {
|
||||
installable: false,
|
||||
sdk_version: "module_current",
|
||||
libs: [ "stub-annotations" ],
|
||||
java_version: "1.8",
|
||||
dist: {
|
||||
targets: ["sdk", "win_sdk"],
|
||||
dir: "apistubs/android/system",
|
||||
@@ -215,6 +226,7 @@ java_defaults {
|
||||
installable: false,
|
||||
sdk_version: "module_current",
|
||||
libs: [ "stub-annotations" ],
|
||||
java_version: "1.8",
|
||||
dist: {
|
||||
targets: ["sdk", "win_sdk"],
|
||||
dir: "apistubs/android/module-lib",
|
||||
@@ -246,6 +258,9 @@ stubs_defaults {
|
||||
api_file: "api/module-lib-current.txt",
|
||||
removed_api_file: "api/module-lib-removed.txt",
|
||||
},
|
||||
api_lint: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
dist: {
|
||||
targets: ["sdk", "win_sdk"],
|
||||
@@ -280,6 +295,9 @@ stubs_defaults {
|
||||
api_file: "api/current.txt",
|
||||
removed_api_file: "api/removed.txt",
|
||||
},
|
||||
api_lint: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
dist: {
|
||||
targets: ["sdk", "win_sdk"],
|
||||
|
||||
4
apex/blobstore/OWNERS
Normal file
4
apex/blobstore/OWNERS
Normal file
@@ -0,0 +1,4 @@
|
||||
set noparent
|
||||
|
||||
sudheersai@google.com
|
||||
yamasani@google.com
|
||||
18
apex/blobstore/TEST_MAPPING
Normal file
18
apex/blobstore/TEST_MAPPING
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "CtsBlobStoreTestCases"
|
||||
},
|
||||
{
|
||||
"name": "CtsBlobStoreHostTestCases"
|
||||
},
|
||||
{
|
||||
"name": "FrameworksMockingServicesTests",
|
||||
"options": [
|
||||
{
|
||||
"include-filter": "com.android.server.blob"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
40
apex/blobstore/framework/Android.bp
Normal file
40
apex/blobstore/framework/Android.bp
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2019 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
filegroup {
|
||||
name: "framework-blobstore-sources",
|
||||
srcs: [
|
||||
"java/**/*.java",
|
||||
"java/**/*.aidl"
|
||||
],
|
||||
path: "java",
|
||||
}
|
||||
|
||||
java_library {
|
||||
name: "blobstore-framework",
|
||||
installable: false,
|
||||
compile_dex: true,
|
||||
sdk_version: "core_platform",
|
||||
srcs: [
|
||||
":framework-blobstore-sources",
|
||||
],
|
||||
aidl: {
|
||||
export_include_dirs: [
|
||||
"java",
|
||||
],
|
||||
},
|
||||
libs: [
|
||||
"framework-minus-apex",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app.blob;
|
||||
|
||||
/** {@hide} */
|
||||
parcelable BlobHandle;
|
||||
305
apex/blobstore/framework/java/android/app/blob/BlobHandle.java
Normal file
305
apex/blobstore/framework/java/android/app/blob/BlobHandle.java
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app.blob;
|
||||
|
||||
import static android.app.blob.XmlTags.ATTR_ALGO;
|
||||
import static android.app.blob.XmlTags.ATTR_DIGEST;
|
||||
import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
|
||||
import static android.app.blob.XmlTags.ATTR_LABEL;
|
||||
import static android.app.blob.XmlTags.ATTR_TAG;
|
||||
|
||||
import android.annotation.CurrentTimeMillisLong;
|
||||
import android.annotation.NonNull;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Base64;
|
||||
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* An identifier to represent a blob.
|
||||
*/
|
||||
// TODO: use datagen tool?
|
||||
public final class BlobHandle implements Parcelable {
|
||||
/** @hide */
|
||||
public static final String ALGO_SHA_256 = "SHA-256";
|
||||
|
||||
private static final String[] SUPPORTED_ALGOS = {
|
||||
ALGO_SHA_256
|
||||
};
|
||||
|
||||
private static final int LIMIT_BLOB_TAG_LENGTH = 128; // characters
|
||||
private static final int LIMIT_BLOB_LABEL_LENGTH = 100; // characters
|
||||
|
||||
/**
|
||||
* Cyrptographically secure hash algorithm used to generate hash of the blob this handle is
|
||||
* representing.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull public final String algorithm;
|
||||
|
||||
/**
|
||||
* Hash of the blob this handle is representing using {@link #algorithm}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull public final byte[] digest;
|
||||
|
||||
/**
|
||||
* Label of the blob that can be surfaced to the user.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull public final CharSequence label;
|
||||
|
||||
/**
|
||||
* Time in milliseconds after which the blob should be invalidated and not
|
||||
* allowed to be accessed by any other app, in {@link System#currentTimeMillis()} timebase.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@CurrentTimeMillisLong public final long expiryTimeMillis;
|
||||
|
||||
/**
|
||||
* An opaque {@link String} associated with the blob.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull public final String tag;
|
||||
|
||||
private BlobHandle(String algorithm, byte[] digest, CharSequence label, long expiryTimeMillis,
|
||||
String tag) {
|
||||
this.algorithm = algorithm;
|
||||
this.digest = digest;
|
||||
this.label = label;
|
||||
this.expiryTimeMillis = expiryTimeMillis;
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
private BlobHandle(Parcel in) {
|
||||
this.algorithm = in.readString();
|
||||
this.digest = in.createByteArray();
|
||||
this.label = in.readCharSequence();
|
||||
this.expiryTimeMillis = in.readLong();
|
||||
this.tag = in.readString();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static @NonNull BlobHandle create(@NonNull String algorithm, @NonNull byte[] digest,
|
||||
@NonNull CharSequence label, @CurrentTimeMillisLong long expiryTimeMillis,
|
||||
@NonNull String tag) {
|
||||
final BlobHandle handle = new BlobHandle(algorithm, digest, label, expiryTimeMillis, tag);
|
||||
handle.assertIsValid();
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new blob identifier.
|
||||
*
|
||||
* <p> For two objects of {@link BlobHandle} to be considered equal, the following arguments
|
||||
* must be equal:
|
||||
* <ul>
|
||||
* <li> {@code digest}
|
||||
* <li> {@code label}
|
||||
* <li> {@code expiryTimeMillis}
|
||||
* <li> {@code tag}
|
||||
* </ul>
|
||||
*
|
||||
* @param digest the SHA-256 hash of the blob this is representing.
|
||||
* @param label a label indicating what the blob is, that can be surfaced to the user.
|
||||
* The length of the label cannot be more than 100 characters. It is recommended
|
||||
* to keep this brief. This may be truncated and ellipsized if it is too long
|
||||
* to be displayed to the user.
|
||||
* @param expiryTimeMillis the time in secs after which the blob should be invalidated and not
|
||||
* allowed to be accessed by any other app,
|
||||
* in {@link System#currentTimeMillis()} timebase or {@code 0} to
|
||||
* indicate that there is no expiry time associated with this blob.
|
||||
* @param tag an opaque {@link String} associated with the blob. The length of the tag
|
||||
* cannot be more than 128 characters.
|
||||
*
|
||||
* @return a new instance of {@link BlobHandle} object.
|
||||
*/
|
||||
public static @NonNull BlobHandle createWithSha256(@NonNull byte[] digest,
|
||||
@NonNull CharSequence label, @CurrentTimeMillisLong long expiryTimeMillis,
|
||||
@NonNull String tag) {
|
||||
return create(ALGO_SHA_256, digest, label, expiryTimeMillis, tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SHA-256 hash of the blob that this object is representing.
|
||||
*
|
||||
* @see #createWithSha256(byte[], CharSequence, long, String)
|
||||
*/
|
||||
public @NonNull byte[] getSha256Digest() {
|
||||
return digest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label associated with the blob that this object is representing.
|
||||
*
|
||||
* @see #createWithSha256(byte[], CharSequence, long, String)
|
||||
*/
|
||||
public @NonNull CharSequence getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expiry time in milliseconds of the blob that this object is representing, in
|
||||
* {@link System#currentTimeMillis()} timebase.
|
||||
*
|
||||
* @see #createWithSha256(byte[], CharSequence, long, String)
|
||||
*/
|
||||
public @CurrentTimeMillisLong long getExpiryTimeMillis() {
|
||||
return expiryTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the opaque {@link String} associated with the blob this object is representing.
|
||||
*
|
||||
* @see #createWithSha256(byte[], CharSequence, long, String)
|
||||
*/
|
||||
public @NonNull String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString(algorithm);
|
||||
dest.writeByteArray(digest);
|
||||
dest.writeCharSequence(label);
|
||||
dest.writeLong(expiryTimeMillis);
|
||||
dest.writeString(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || !(obj instanceof BlobHandle)) {
|
||||
return false;
|
||||
}
|
||||
final BlobHandle other = (BlobHandle) obj;
|
||||
return this.algorithm.equals(other.algorithm)
|
||||
&& Arrays.equals(this.digest, other.digest)
|
||||
&& this.label.toString().equals(other.label.toString())
|
||||
&& this.expiryTimeMillis == other.expiryTimeMillis
|
||||
&& this.tag.equals(other.tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(algorithm, Arrays.hashCode(digest), label, expiryTimeMillis, tag);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void dump(IndentingPrintWriter fout, boolean dumpFull) {
|
||||
if (dumpFull) {
|
||||
fout.println("algo: " + algorithm);
|
||||
fout.println("digest: " + (dumpFull ? encodeDigest(digest) : safeDigest(digest)));
|
||||
fout.println("label: " + label);
|
||||
fout.println("expiryMs: " + expiryTimeMillis);
|
||||
fout.println("tag: " + tag);
|
||||
} else {
|
||||
fout.println(toString());
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void assertIsValid() {
|
||||
Preconditions.checkArgumentIsSupported(SUPPORTED_ALGOS, algorithm);
|
||||
Preconditions.checkByteArrayNotEmpty(digest, "digest");
|
||||
Preconditions.checkStringNotEmpty(label, "label must not be null");
|
||||
Preconditions.checkArgument(label.length() <= LIMIT_BLOB_LABEL_LENGTH, "label too long");
|
||||
Preconditions.checkArgumentNonnegative(expiryTimeMillis,
|
||||
"expiryTimeMillis must not be negative");
|
||||
Preconditions.checkStringNotEmpty(tag, "tag must not be null");
|
||||
Preconditions.checkArgument(tag.length() <= LIMIT_BLOB_TAG_LENGTH, "tag too long");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BlobHandle {"
|
||||
+ "algo:" + algorithm + ","
|
||||
+ "digest:" + safeDigest(digest) + ","
|
||||
+ "label:" + label + ","
|
||||
+ "expiryMs:" + expiryTimeMillis + ","
|
||||
+ "tag:" + tag
|
||||
+ "}";
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static String safeDigest(@NonNull byte[] digest) {
|
||||
final String digestStr = encodeDigest(digest);
|
||||
return digestStr.substring(0, 2) + ".." + digestStr.substring(digestStr.length() - 2);
|
||||
}
|
||||
|
||||
private static String encodeDigest(@NonNull byte[] digest) {
|
||||
return Base64.encodeToString(digest, Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean isExpired() {
|
||||
return expiryTimeMillis != 0 && expiryTimeMillis < System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() {
|
||||
@Override
|
||||
public @NonNull BlobHandle createFromParcel(@NonNull Parcel source) {
|
||||
return new BlobHandle(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull BlobHandle[] newArray(int size) {
|
||||
return new BlobHandle[size];
|
||||
}
|
||||
};
|
||||
|
||||
/** @hide */
|
||||
public void writeToXml(@NonNull XmlSerializer out) throws IOException {
|
||||
XmlUtils.writeStringAttribute(out, ATTR_ALGO, algorithm);
|
||||
XmlUtils.writeByteArrayAttribute(out, ATTR_DIGEST, digest);
|
||||
XmlUtils.writeStringAttribute(out, ATTR_LABEL, label);
|
||||
XmlUtils.writeLongAttribute(out, ATTR_EXPIRY_TIME, expiryTimeMillis);
|
||||
XmlUtils.writeStringAttribute(out, ATTR_TAG, tag);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
public static BlobHandle createFromXml(@NonNull XmlPullParser in) throws IOException {
|
||||
final String algo = XmlUtils.readStringAttribute(in, ATTR_ALGO);
|
||||
final byte[] digest = XmlUtils.readByteArrayAttribute(in, ATTR_DIGEST);
|
||||
final CharSequence label = XmlUtils.readStringAttribute(in, ATTR_LABEL);
|
||||
final long expiryTimeMs = XmlUtils.readLongAttribute(in, ATTR_EXPIRY_TIME);
|
||||
final String tag = XmlUtils.readStringAttribute(in, ATTR_TAG);
|
||||
|
||||
return BlobHandle.create(algo, digest, label, expiryTimeMs, tag);
|
||||
}
|
||||
}
|
||||
19
apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl
Normal file
19
apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app.blob;
|
||||
|
||||
/** {@hide} */
|
||||
parcelable BlobInfo;
|
||||
127
apex/blobstore/framework/java/android/app/blob/BlobInfo.java
Normal file
127
apex/blobstore/framework/java/android/app/blob/BlobInfo.java
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.blob;
|
||||
|
||||
import static android.text.format.Formatter.FLAG_IEC_UNITS;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.AppGlobals;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.format.Formatter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class to provide information about a shared blob.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class BlobInfo implements Parcelable {
|
||||
private final long mId;
|
||||
private final long mExpiryTimeMs;
|
||||
private final CharSequence mLabel;
|
||||
private final long mSizeBytes;
|
||||
private final List<LeaseInfo> mLeaseInfos;
|
||||
|
||||
public BlobInfo(long id, long expiryTimeMs, CharSequence label, long sizeBytes,
|
||||
List<LeaseInfo> leaseInfos) {
|
||||
mId = id;
|
||||
mExpiryTimeMs = expiryTimeMs;
|
||||
mLabel = label;
|
||||
mSizeBytes = sizeBytes;
|
||||
mLeaseInfos = leaseInfos;
|
||||
}
|
||||
|
||||
private BlobInfo(Parcel in) {
|
||||
mId = in.readLong();
|
||||
mExpiryTimeMs = in.readLong();
|
||||
mLabel = in.readCharSequence();
|
||||
mSizeBytes = in.readLong();
|
||||
mLeaseInfos = in.readArrayList(null /* classloader */);
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public long getExpiryTimeMs() {
|
||||
return mExpiryTimeMs;
|
||||
}
|
||||
|
||||
public CharSequence getLabel() {
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public long getSizeBytes() {
|
||||
return mSizeBytes;
|
||||
}
|
||||
|
||||
public List<LeaseInfo> getLeases() {
|
||||
return Collections.unmodifiableList(mLeaseInfos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeLong(mId);
|
||||
dest.writeLong(mExpiryTimeMs);
|
||||
dest.writeCharSequence(mLabel);
|
||||
dest.writeLong(mSizeBytes);
|
||||
dest.writeList(mLeaseInfos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toShortString();
|
||||
}
|
||||
|
||||
private String toShortString() {
|
||||
return "BlobInfo {"
|
||||
+ "id: " + mId + ","
|
||||
+ "expiryMs: " + mExpiryTimeMs + ","
|
||||
+ "label: " + mLabel + ","
|
||||
+ "size: " + formatBlobSize(mSizeBytes) + ","
|
||||
+ "leases: " + LeaseInfo.toShortString(mLeaseInfos) + ","
|
||||
+ "}";
|
||||
}
|
||||
|
||||
private static String formatBlobSize(long sizeBytes) {
|
||||
return Formatter.formatFileSize(AppGlobals.getInitialApplication(),
|
||||
sizeBytes, FLAG_IEC_UNITS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<BlobInfo> CREATOR = new Creator<BlobInfo>() {
|
||||
@Override
|
||||
@NonNull
|
||||
public BlobInfo createFromParcel(Parcel source) {
|
||||
return new BlobInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public BlobInfo[] newArray(int size) {
|
||||
return new BlobInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,943 @@
|
||||
/*
|
||||
* Copyright 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app.blob;
|
||||
|
||||
import android.annotation.BytesLong;
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.CurrentTimeMillisLong;
|
||||
import android.annotation.IdRes;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemService;
|
||||
import android.annotation.TestApi;
|
||||
import android.content.Context;
|
||||
import android.os.LimitExceededException;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ParcelableException;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* This class provides access to the blob store managed by the system.
|
||||
*
|
||||
* <p> Apps can publish and access a data blob using a {@link BlobHandle} object which can
|
||||
* be created with {@link BlobHandle#createWithSha256(byte[], CharSequence, long, String)}.
|
||||
* This {@link BlobHandle} object encapsulates the following pieces of information used for
|
||||
* identifying the blobs:
|
||||
* <ul>
|
||||
* <li> {@link BlobHandle#getSha256Digest()}
|
||||
* <li> {@link BlobHandle#getLabel()}
|
||||
* <li> {@link BlobHandle#getExpiryTimeMillis()}
|
||||
* <li> {@link BlobHandle#getTag()}
|
||||
* </ul>
|
||||
* For two {@link BlobHandle} objects to be considered identical, all these pieces of information
|
||||
* must be equal.
|
||||
*
|
||||
* <p> For contributing a new data blob, an app needs to create a session using
|
||||
* {@link BlobStoreManager#createSession(BlobHandle)} and then open this session for writing using
|
||||
* {@link BlobStoreManager#openSession(long)}.
|
||||
*
|
||||
* <p> The following code snippet shows how to create and open a session for writing:
|
||||
* <pre class="prettyprint">
|
||||
* final long sessionId = blobStoreManager.createSession(blobHandle);
|
||||
* try (BlobStoreManager.Session session = blobStoreManager.openSession(sessionId)) {
|
||||
* try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
|
||||
* session.openWrite(offsetBytes, lengthBytes))) {
|
||||
* writeData(out);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p> If all the data could not be written in a single attempt, apps can close this session
|
||||
* and re-open it again using the session id obtained via
|
||||
* {@link BlobStoreManager#createSession(BlobHandle)}. Note that the session data is persisted
|
||||
* and can be re-opened for completing the data contribution, even across device reboots.
|
||||
*
|
||||
* <p> After the data is written to the session, it can be committed using
|
||||
* {@link Session#commit(Executor, Consumer)}. Until the session is committed, data written
|
||||
* to the session will not be shared with any app.
|
||||
*
|
||||
* <p class="note"> Once a session is committed using {@link Session#commit(Executor, Consumer)},
|
||||
* any data written as part of this session is sealed and cannot be modified anymore.
|
||||
*
|
||||
* <p> Before committing the session, apps can indicate which apps are allowed to access the
|
||||
* contributed data using one or more of the following access modes:
|
||||
* <ul>
|
||||
* <li> {@link Session#allowPackageAccess(String, byte[])} which will allow whitelisting
|
||||
* specific packages to access the blobs.
|
||||
* <li> {@link Session#allowSameSignatureAccess()} which will allow only apps which are signed
|
||||
* with the same certificate as the app which contributed the blob to access it.
|
||||
* <li> {@link Session#allowPublicAccess()} which will allow any app on the device to access
|
||||
* the blob.
|
||||
* </ul>
|
||||
*
|
||||
* <p> The following code snippet shows how to specify the access mode and commit the session:
|
||||
* <pre class="prettyprint">
|
||||
* try (BlobStoreManager.Session session = blobStoreManager.openSession(sessionId)) {
|
||||
* try (OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
|
||||
* session.openWrite(offsetBytes, lengthBytes))) {
|
||||
* writeData(out);
|
||||
* }
|
||||
* session.allowSameSignatureAccess();
|
||||
* session.allowPackageAccess(packageName, certificate);
|
||||
* session.commit(executor, callback);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p> Apps that satisfy at least one of the access mode constraints specified by the publisher
|
||||
* of the data blob will be able to access it.
|
||||
*
|
||||
* <p> A data blob published without specifying any of
|
||||
* these access modes will be considered private and only the app that contributed the data
|
||||
* blob will be allowed to access it. This is still useful for overall device system health as
|
||||
* the System can try to keep one copy of data blob on disk when multiple apps contribute the
|
||||
* same data.
|
||||
*
|
||||
* <p class="note"> It is strongly recommended that apps use one of
|
||||
* {@link Session#allowPackageAccess(String, byte[])} or {@link Session#allowSameSignatureAccess()}
|
||||
* when they know, ahead of time, the set of apps they would like to share the blobs with.
|
||||
* {@link Session#allowPublicAccess()} is meant for publicly available data committed from
|
||||
* libraries and SDKs.
|
||||
*
|
||||
* <p> Once a data blob is committed with {@link Session#commit(Executor, Consumer)}, it
|
||||
* can be accessed using {@link BlobStoreManager#openBlob(BlobHandle)}, assuming the caller
|
||||
* satisfies constraints of any of the access modes associated with that data blob. An app may
|
||||
* acquire a lease on a blob with {@link BlobStoreManager#acquireLease(BlobHandle, int)} and
|
||||
* release the lease with {@link BlobStoreManager#releaseLease(BlobHandle)}. A blob will not be
|
||||
* deleted from the system while there is at least one app leasing it.
|
||||
*
|
||||
* <p> The following code snippet shows how to access the data blob:
|
||||
* <pre class="prettyprint">
|
||||
* try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
|
||||
* blobStoreManager.openBlob(blobHandle)) {
|
||||
* useData(in);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
@SystemService(Context.BLOB_STORE_SERVICE)
|
||||
public class BlobStoreManager {
|
||||
/** @hide */
|
||||
public static final int COMMIT_RESULT_SUCCESS = 0;
|
||||
/** @hide */
|
||||
public static final int COMMIT_RESULT_ERROR = 1;
|
||||
|
||||
/** @hide */
|
||||
public static final int INVALID_RES_ID = -1;
|
||||
|
||||
private final Context mContext;
|
||||
private final IBlobStoreManager mService;
|
||||
|
||||
/** @hide */
|
||||
public BlobStoreManager(@NonNull Context context, @NonNull IBlobStoreManager service) {
|
||||
mContext = context;
|
||||
mService = service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new session using the given {@link BlobHandle}, returning a unique id
|
||||
* that represents the session. Once created, the session can be opened
|
||||
* multiple times across multiple device boots.
|
||||
*
|
||||
* <p> The system may automatically destroy sessions that have not been
|
||||
* finalized (either committed or abandoned) within a reasonable period of
|
||||
* time, typically about a week.
|
||||
*
|
||||
* <p> If an app is planning to acquire a lease on this data (using
|
||||
* {@link #acquireLease(BlobHandle, int)} or one of it's other variants) after committing
|
||||
* this data (using {@link Session#commit(Executor, Consumer)}), it is recommended that
|
||||
* the app checks the remaining quota for acquiring a lease first using
|
||||
* {@link #getRemainingLeaseQuotaBytes()} and can skip contributing this data if needed.
|
||||
*
|
||||
* @param blobHandle the {@link BlobHandle} identifier for which a new session
|
||||
* needs to be created.
|
||||
* @return positive, non-zero unique id that represents the created session.
|
||||
* This id remains consistent across device reboots until the
|
||||
* session is finalized. IDs are not reused during a given boot.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while creating the session.
|
||||
* @throws SecurityException when the caller is not allowed to create a session, such
|
||||
* as when called from an Instant app.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
|
||||
* @throws LimitExceededException when a new session could not be created, such as when the
|
||||
* caller is trying to create too many sessions.
|
||||
*/
|
||||
public @IntRange(from = 1) long createSession(@NonNull BlobHandle blobHandle)
|
||||
throws IOException {
|
||||
try {
|
||||
return mService.createSession(blobHandle, mContext.getOpPackageName());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
e.maybeRethrow(LimitExceededException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an existing session to actively perform work.
|
||||
*
|
||||
* @param sessionId a unique id obtained via {@link #createSession(BlobHandle)} that
|
||||
* represents a particular session.
|
||||
* @return the {@link Session} object corresponding to the {@code sessionId}.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while opening the session.
|
||||
* @throws SecurityException when the caller does not own the session, or
|
||||
* the session does not exist or is invalid.
|
||||
*/
|
||||
public @NonNull Session openSession(@IntRange(from = 1) long sessionId) throws IOException {
|
||||
try {
|
||||
return new Session(mService.openSession(sessionId, mContext.getOpPackageName()));
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abandons an existing session and deletes any data that was written to that session so far.
|
||||
*
|
||||
* @param sessionId a unique id obtained via {@link #createSession(BlobHandle)} that
|
||||
* represents a particular session.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while deleting the session.
|
||||
* @throws SecurityException when the caller does not own the session, or
|
||||
* the session does not exist or is invalid.
|
||||
*/
|
||||
public void abandonSession(@IntRange(from = 1) long sessionId) throws IOException {
|
||||
try {
|
||||
mService.abandonSession(sessionId, mContext.getOpPackageName());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an existing blob for reading from the blob store managed by the system.
|
||||
*
|
||||
* @param blobHandle the {@link BlobHandle} representing the blob that the caller
|
||||
* wants to access.
|
||||
* @return a {@link ParcelFileDescriptor} that can be used to read the blob content.
|
||||
*
|
||||
* @throws IOException when there is an I/O while opening the blob for read.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
|
||||
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
|
||||
* exist or the caller does not have access to it.
|
||||
*/
|
||||
public @NonNull ParcelFileDescriptor openBlob(@NonNull BlobHandle blobHandle)
|
||||
throws IOException {
|
||||
try {
|
||||
return mService.openBlob(blobHandle, mContext.getOpPackageName());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
|
||||
* system that the caller wants the blob to be kept around.
|
||||
*
|
||||
* <p> Any active leases will be automatically released when the blob's expiry time
|
||||
* ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed.
|
||||
*
|
||||
* <p> This lease information is persisted and calling this more than once will result in
|
||||
* latest lease overriding any previous lease.
|
||||
*
|
||||
* <p> When an app acquires a lease on a blob, the System will try to keep this
|
||||
* blob around but note that it can still be deleted if it was requested by the user.
|
||||
*
|
||||
* <p> In case the resource name for the {@code descriptionResId} is modified as part of
|
||||
* an app update, apps should re-acquire the lease with the new resource id.
|
||||
*
|
||||
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
|
||||
* acquire a lease for.
|
||||
* @param descriptionResId the resource id for a short description string that can be surfaced
|
||||
* to the user explaining what the blob is used for.
|
||||
* @param leaseExpiryTimeMillis the time in milliseconds after which the lease can be
|
||||
* automatically released, in {@link System#currentTimeMillis()}
|
||||
* timebase. If its value is {@code 0}, then the behavior of this
|
||||
* API is identical to {@link #acquireLease(BlobHandle, int)}
|
||||
* where clients have to explicitly call
|
||||
* {@link #releaseLease(BlobHandle)} when they don't
|
||||
* need the blob anymore.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while acquiring a lease to the blob.
|
||||
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
|
||||
* exist or the caller does not have access to it.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid or
|
||||
* if the {@code leaseExpiryTimeMillis} is greater than the
|
||||
* {@link BlobHandle#getExpiryTimeMillis()}.
|
||||
* @throws LimitExceededException when a lease could not be acquired, such as when the
|
||||
* caller is trying to acquire too many leases or acquire
|
||||
* leases on too much data. Apps can avoid this by checking
|
||||
* the remaining quota using
|
||||
* {@link #getRemainingLeaseQuotaBytes()} before trying to
|
||||
* acquire a lease.
|
||||
*
|
||||
* @see #acquireLease(BlobHandle, int)
|
||||
* @see #acquireLease(BlobHandle, CharSequence)
|
||||
*/
|
||||
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
|
||||
@CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
|
||||
try {
|
||||
mService.acquireLease(blobHandle, descriptionResId, null, leaseExpiryTimeMillis,
|
||||
mContext.getOpPackageName());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
e.maybeRethrow(LimitExceededException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
|
||||
* system that the caller wants the blob to be kept around.
|
||||
*
|
||||
* <p> This is a variant of {@link #acquireLease(BlobHandle, int, long)} taking a
|
||||
* {@link CharSequence} for {@code description}. It is highly recommended that callers only
|
||||
* use this when a valid resource ID for {@code description} could not be provided. Otherwise,
|
||||
* apps should prefer using {@link #acquireLease(BlobHandle, int)} which will allow
|
||||
* {@code description} to be localized.
|
||||
*
|
||||
* <p> Any active leases will be automatically released when the blob's expiry time
|
||||
* ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed.
|
||||
*
|
||||
* <p> This lease information is persisted and calling this more than once will result in
|
||||
* latest lease overriding any previous lease.
|
||||
*
|
||||
* <p> When an app acquires a lease on a blob, the System will try to keep this
|
||||
* blob around but note that it can still be deleted if it was requested by the user.
|
||||
*
|
||||
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
|
||||
* acquire a lease for.
|
||||
* @param description a short description string that can be surfaced
|
||||
* to the user explaining what the blob is used for. It is recommended to
|
||||
* keep this description brief. This may be truncated and ellipsized
|
||||
* if it is too long to be displayed to the user.
|
||||
* @param leaseExpiryTimeMillis the time in milliseconds after which the lease can be
|
||||
* automatically released, in {@link System#currentTimeMillis()}
|
||||
* timebase. If its value is {@code 0}, then the behavior of this
|
||||
* API is identical to {@link #acquireLease(BlobHandle, int)}
|
||||
* where clients have to explicitly call
|
||||
* {@link #releaseLease(BlobHandle)} when they don't
|
||||
* need the blob anymore.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while acquiring a lease to the blob.
|
||||
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
|
||||
* exist or the caller does not have access to it.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid or
|
||||
* if the {@code leaseExpiryTimeMillis} is greater than the
|
||||
* {@link BlobHandle#getExpiryTimeMillis()}.
|
||||
* @throws LimitExceededException when a lease could not be acquired, such as when the
|
||||
* caller is trying to acquire too many leases or acquire
|
||||
* leases on too much data. Apps can avoid this by checking
|
||||
* the remaining quota using
|
||||
* {@link #getRemainingLeaseQuotaBytes()} before trying to
|
||||
* acquire a lease.
|
||||
*
|
||||
* @see #acquireLease(BlobHandle, int, long)
|
||||
* @see #acquireLease(BlobHandle, CharSequence)
|
||||
*/
|
||||
public void acquireLease(@NonNull BlobHandle blobHandle, @NonNull CharSequence description,
|
||||
@CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
|
||||
try {
|
||||
mService.acquireLease(blobHandle, INVALID_RES_ID, description, leaseExpiryTimeMillis,
|
||||
mContext.getOpPackageName());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
e.maybeRethrow(LimitExceededException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
|
||||
* system that the caller wants the blob to be kept around.
|
||||
*
|
||||
* <p> This is similar to {@link #acquireLease(BlobHandle, int, long)} except clients don't
|
||||
* have to specify the lease expiry time upfront using this API and need to explicitly
|
||||
* release the lease using {@link #releaseLease(BlobHandle)} when they no longer like to keep
|
||||
* a blob around.
|
||||
*
|
||||
* <p> Any active leases will be automatically released when the blob's expiry time
|
||||
* ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed.
|
||||
*
|
||||
* <p> This lease information is persisted and calling this more than once will result in
|
||||
* latest lease overriding any previous lease.
|
||||
*
|
||||
* <p> When an app acquires a lease on a blob, the System will try to keep this
|
||||
* blob around but note that it can still be deleted if it was requested by the user.
|
||||
*
|
||||
* <p> In case the resource name for the {@code descriptionResId} is modified as part of
|
||||
* an app update, apps should re-acquire the lease with the new resource id.
|
||||
*
|
||||
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
|
||||
* acquire a lease for.
|
||||
* @param descriptionResId the resource id for a short description string that can be surfaced
|
||||
* to the user explaining what the blob is used for.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while acquiring a lease to the blob.
|
||||
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
|
||||
* exist or the caller does not have access to it.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
|
||||
* @throws LimitExceededException when a lease could not be acquired, such as when the
|
||||
* caller is trying to acquire too many leases or acquire
|
||||
* leases on too much data. Apps can avoid this by checking
|
||||
* the remaining quota using
|
||||
* {@link #getRemainingLeaseQuotaBytes()} before trying to
|
||||
* acquire a lease.
|
||||
*
|
||||
* @see #acquireLease(BlobHandle, int, long)
|
||||
* @see #acquireLease(BlobHandle, CharSequence, long)
|
||||
*/
|
||||
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId)
|
||||
throws IOException {
|
||||
acquireLease(blobHandle, descriptionResId, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
|
||||
* system that the caller wants the blob to be kept around.
|
||||
*
|
||||
* <p> This is a variant of {@link #acquireLease(BlobHandle, int)} taking a {@link CharSequence}
|
||||
* for {@code description}. It is highly recommended that callers only use this when a valid
|
||||
* resource ID for {@code description} could not be provided. Otherwise, apps should prefer
|
||||
* using {@link #acquireLease(BlobHandle, int)} which will allow {@code description} to be
|
||||
* localized.
|
||||
*
|
||||
* <p> This is similar to {@link #acquireLease(BlobHandle, CharSequence, long)} except clients
|
||||
* don't have to specify the lease expiry time upfront using this API and need to explicitly
|
||||
* release the lease using {@link #releaseLease(BlobHandle)} when they no longer like to keep
|
||||
* a blob around.
|
||||
*
|
||||
* <p> Any active leases will be automatically released when the blob's expiry time
|
||||
* ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed.
|
||||
*
|
||||
* <p> This lease information is persisted and calling this more than once will result in
|
||||
* latest lease overriding any previous lease.
|
||||
*
|
||||
* <p> When an app acquires a lease on a blob, the System will try to keep this
|
||||
* blob around but note that it can still be deleted if it was requested by the user.
|
||||
*
|
||||
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
|
||||
* acquire a lease for.
|
||||
* @param description a short description string that can be surfaced
|
||||
* to the user explaining what the blob is used for. It is recommended to
|
||||
* keep this description brief. This may be truncated and
|
||||
* ellipsized if it is too long to be displayed to the user.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while acquiring a lease to the blob.
|
||||
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
|
||||
* exist or the caller does not have access to it.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
|
||||
* @throws LimitExceededException when a lease could not be acquired, such as when the
|
||||
* caller is trying to acquire too many leases or acquire
|
||||
* leases on too much data. Apps can avoid this by checking
|
||||
* the remaining quota using
|
||||
* {@link #getRemainingLeaseQuotaBytes()} before trying to
|
||||
* acquire a lease.
|
||||
*
|
||||
* @see #acquireLease(BlobHandle, int)
|
||||
* @see #acquireLease(BlobHandle, CharSequence, long)
|
||||
*/
|
||||
public void acquireLease(@NonNull BlobHandle blobHandle, @NonNull CharSequence description)
|
||||
throws IOException {
|
||||
acquireLease(blobHandle, description, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release any active lease to the blob represented by {@code blobHandle} which is
|
||||
* currently held by the caller.
|
||||
*
|
||||
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
|
||||
* release the lease for.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while releasing the release to the blob.
|
||||
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
|
||||
* exist or the caller does not have access to it.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
|
||||
*/
|
||||
public void releaseLease(@NonNull BlobHandle blobHandle) throws IOException {
|
||||
try {
|
||||
mService.releaseLease(blobHandle, mContext.getOpPackageName());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the remaining quota size for acquiring a lease (in bytes) which indicates the
|
||||
* remaining amount of data that an app can acquire a lease on before the System starts
|
||||
* rejecting lease requests.
|
||||
*
|
||||
* If an app wants to acquire a lease on a blob but the remaining quota size is not sufficient,
|
||||
* then it can try releasing leases on any older blobs which are not needed anymore.
|
||||
*
|
||||
* @return the remaining quota size for acquiring a lease.
|
||||
*/
|
||||
public @IntRange(from = 0) long getRemainingLeaseQuotaBytes() {
|
||||
try {
|
||||
return mService.getRemainingLeaseQuotaBytes(mContext.getOpPackageName());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until any pending tasks (like persisting data to disk) have finished.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public void waitForIdle(long timeoutMillis) throws InterruptedException, TimeoutException {
|
||||
try {
|
||||
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||
mService.waitForIdle(new RemoteCallback((result) -> countDownLatch.countDown()));
|
||||
if (!countDownLatch.await(timeoutMillis, TimeUnit.MILLISECONDS)) {
|
||||
throw new TimeoutException("Timed out waiting for service to become idle");
|
||||
}
|
||||
} catch (ParcelableException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
public List<BlobInfo> queryBlobsForUser(@NonNull UserHandle user) throws IOException {
|
||||
try {
|
||||
return mService.queryBlobsForUser(user.getIdentifier());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void deleteBlob(@NonNull BlobInfo blobInfo) throws IOException {
|
||||
try {
|
||||
mService.deleteBlob(blobInfo.getId());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link BlobHandle BlobHandles} corresponding to the data blobs that
|
||||
* the calling app currently has a lease on.
|
||||
*
|
||||
* @return a list of {@link BlobHandle BlobHandles} that the caller has a lease on.
|
||||
*/
|
||||
@NonNull
|
||||
public List<BlobHandle> getLeasedBlobs() throws IOException {
|
||||
try {
|
||||
return mService.getLeasedBlobs(mContext.getOpPackageName());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link LeaseInfo} representing a lease acquired using
|
||||
* {@link #acquireLease(BlobHandle, int)} or one of it's other variants,
|
||||
* or {@code null} if there is no lease acquired.
|
||||
*
|
||||
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
|
||||
* exist or the caller does not have access to it.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
@Nullable
|
||||
public LeaseInfo getLeaseInfo(@NonNull BlobHandle blobHandle) throws IOException {
|
||||
try {
|
||||
return mService.getLeaseInfo(blobHandle, mContext.getOpPackageName());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an ongoing session of a blob's contribution to the blob store managed by the
|
||||
* system.
|
||||
*
|
||||
* <p> Clients that want to contribute a blob need to first create a {@link Session} using
|
||||
* {@link #createSession(BlobHandle)} and once the session is created, clients can open and
|
||||
* close this session multiple times using {@link #openSession(long)} and
|
||||
* {@link Session#close()} before committing it using
|
||||
* {@link Session#commit(Executor, Consumer)}, at which point system will take
|
||||
* ownership of the blob and the client can no longer make any modifications to the blob's
|
||||
* content.
|
||||
*/
|
||||
public static class Session implements Closeable {
|
||||
private final IBlobStoreSession mSession;
|
||||
|
||||
private Session(@NonNull IBlobStoreSession session) {
|
||||
mSession = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a file descriptor to write a blob into the session.
|
||||
*
|
||||
* <p> The returned file descriptor will start writing data at the requested offset
|
||||
* in the underlying file, which can be used to resume a partially
|
||||
* written file. If a valid file length is specified, the system will
|
||||
* preallocate the underlying disk space to optimize placement on disk.
|
||||
* It is strongly recommended to provide a valid file length when known.
|
||||
*
|
||||
* @param offsetBytes offset into the file to begin writing at, or 0 to
|
||||
* start at the beginning of the file.
|
||||
* @param lengthBytes total size of the file being written, used to
|
||||
* preallocate the underlying disk space, or -1 if unknown.
|
||||
* The system may clear various caches as needed to allocate
|
||||
* this space.
|
||||
*
|
||||
* @return a {@link ParcelFileDescriptor} for writing to the blob file.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while opening the file to write.
|
||||
* @throws SecurityException when the caller is not the owner of the session.
|
||||
* @throws IllegalStateException when the caller tries to write to the file after it is
|
||||
* abandoned (using {@link #abandon()})
|
||||
* or committed (using {@link #commit})
|
||||
* or closed (using {@link #close()}).
|
||||
*/
|
||||
public @NonNull ParcelFileDescriptor openWrite(@BytesLong long offsetBytes,
|
||||
@BytesLong long lengthBytes) throws IOException {
|
||||
try {
|
||||
final ParcelFileDescriptor pfd = mSession.openWrite(offsetBytes, lengthBytes);
|
||||
pfd.seekTo(offsetBytes);
|
||||
return pfd;
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a file descriptor to read the blob content already written into this session.
|
||||
*
|
||||
* @return a {@link ParcelFileDescriptor} for reading from the blob file.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while opening the file to read.
|
||||
* @throws SecurityException when the caller is not the owner of the session.
|
||||
* @throws IllegalStateException when the caller tries to read the file after it is
|
||||
* abandoned (using {@link #abandon()})
|
||||
* or closed (using {@link #close()}).
|
||||
*/
|
||||
public @NonNull ParcelFileDescriptor openRead() throws IOException {
|
||||
try {
|
||||
return mSession.openRead();
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the blob file that was written to the session so far.
|
||||
*
|
||||
* @return the size of the blob file so far.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while opening the file to read.
|
||||
* @throws SecurityException when the caller is not the owner of the session.
|
||||
* @throws IllegalStateException when the caller tries to get the file size after it is
|
||||
* abandoned (using {@link #abandon()})
|
||||
* or closed (using {@link #close()}).
|
||||
*/
|
||||
public @BytesLong long getSize() throws IOException {
|
||||
try {
|
||||
return mSession.getSize();
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this session. It can be re-opened for writing/reading if it has not been
|
||||
* abandoned (using {@link #abandon}) or committed (using {@link #commit}).
|
||||
*
|
||||
* @throws IOException when there is an I/O error while closing the session.
|
||||
* @throws SecurityException when the caller is not the owner of the session.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
mSession.close();
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abandon this session and delete any data that was written to this session so far.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while abandoning the session.
|
||||
* @throws SecurityException when the caller is not the owner of the session.
|
||||
* @throws IllegalStateException when the caller tries to abandon a session which was
|
||||
* already finalized.
|
||||
*/
|
||||
public void abandon() throws IOException {
|
||||
try {
|
||||
mSession.abandon();
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow {@code packageName} with a particular signing certificate to access this blob
|
||||
* data once it is committed using a {@link BlobHandle} representing the blob.
|
||||
*
|
||||
* <p> This needs to be called before committing the blob using
|
||||
* {@link #commit(Executor, Consumer)}.
|
||||
*
|
||||
* @param packageName the name of the package which should be allowed to access the blob.
|
||||
* @param certificate the input bytes representing a certificate of type
|
||||
* {@link android.content.pm.PackageManager#CERT_INPUT_SHA256}.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while changing the access.
|
||||
* @throws SecurityException when the caller is not the owner of the session.
|
||||
* @throws IllegalStateException when the caller tries to change access for a blob which is
|
||||
* already committed.
|
||||
* @throws LimitExceededException when the caller tries to explicitly allow too
|
||||
* many packages using this API.
|
||||
*/
|
||||
public void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate)
|
||||
throws IOException {
|
||||
try {
|
||||
mSession.allowPackageAccess(packageName, certificate);
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
e.maybeRethrow(LimitExceededException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if access has been allowed for a {@code packageName} using either
|
||||
* {@link #allowPackageAccess(String, byte[])}.
|
||||
* Otherwise, {@code false}.
|
||||
*
|
||||
* @param packageName the name of the package to check the access for.
|
||||
* @param certificate the input bytes representing a certificate of type
|
||||
* {@link android.content.pm.PackageManager#CERT_INPUT_SHA256}.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while getting the access type.
|
||||
* @throws IllegalStateException when the caller tries to get access type from a session
|
||||
* which is closed or abandoned.
|
||||
*/
|
||||
public boolean isPackageAccessAllowed(@NonNull String packageName,
|
||||
@NonNull byte[] certificate) throws IOException {
|
||||
try {
|
||||
return mSession.isPackageAccessAllowed(packageName, certificate);
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow packages which are signed with the same certificate as the caller to access this
|
||||
* blob data once it is committed using a {@link BlobHandle} representing the blob.
|
||||
*
|
||||
* <p> This needs to be called before committing the blob using
|
||||
* {@link #commit(Executor, Consumer)}.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while changing the access.
|
||||
* @throws SecurityException when the caller is not the owner of the session.
|
||||
* @throws IllegalStateException when the caller tries to change access for a blob which is
|
||||
* already committed.
|
||||
*/
|
||||
public void allowSameSignatureAccess() throws IOException {
|
||||
try {
|
||||
mSession.allowSameSignatureAccess();
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if access has been allowed for packages signed with the same
|
||||
* certificate as the caller by using {@link #allowSameSignatureAccess()}.
|
||||
* Otherwise, {@code false}.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while getting the access type.
|
||||
* @throws IllegalStateException when the caller tries to get access type from a session
|
||||
* which is closed or abandoned.
|
||||
*/
|
||||
public boolean isSameSignatureAccessAllowed() throws IOException {
|
||||
try {
|
||||
return mSession.isSameSignatureAccessAllowed();
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow any app on the device to access this blob data once it is committed using
|
||||
* a {@link BlobHandle} representing the blob.
|
||||
*
|
||||
* <p><strong>Note:</strong> This is only meant to be used from libraries and SDKs where
|
||||
* the apps which we want to allow access is not known ahead of time.
|
||||
* If a blob is being committed to be shared with a particular set of apps, it is highly
|
||||
* recommended to use {@link #allowPackageAccess(String, byte[])} instead.
|
||||
*
|
||||
* <p> This needs to be called before committing the blob using
|
||||
* {@link #commit(Executor, Consumer)}.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while changing the access.
|
||||
* @throws SecurityException when the caller is not the owner of the session.
|
||||
* @throws IllegalStateException when the caller tries to change access for a blob which is
|
||||
* already committed.
|
||||
*/
|
||||
public void allowPublicAccess() throws IOException {
|
||||
try {
|
||||
mSession.allowPublicAccess();
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if public access has been allowed by using
|
||||
* {@link #allowPublicAccess()}. Otherwise, {@code false}.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while getting the access type.
|
||||
* @throws IllegalStateException when the caller tries to get access type from a session
|
||||
* which is closed or abandoned.
|
||||
*/
|
||||
public boolean isPublicAccessAllowed() throws IOException {
|
||||
try {
|
||||
return mSession.isPublicAccessAllowed();
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the file that was written so far to this session to the blob store maintained by
|
||||
* the system.
|
||||
*
|
||||
* <p> Once this method is called, the session is finalized and no additional
|
||||
* mutations can be performed on the session. If the device reboots
|
||||
* before the session has been finalized, you may commit the session again.
|
||||
*
|
||||
* <p> Note that this commit operation will fail if the hash of the data written so far
|
||||
* to this session does not match with the one used for
|
||||
* {@link BlobHandle#createWithSha256(byte[], CharSequence, long, String)} BlobHandle}
|
||||
* associated with this session.
|
||||
*
|
||||
* <p> Committing the same data more than once will result in replacing the corresponding
|
||||
* access mode (via calling one of {@link #allowPackageAccess(String, byte[])},
|
||||
* {@link #allowSameSignatureAccess()}, etc) with the latest one.
|
||||
*
|
||||
* @param executor the executor on which result callback will be invoked.
|
||||
* @param resultCallback a callback to receive the commit result. when the result is
|
||||
* {@code 0}, it indicates success. Otherwise, failure.
|
||||
*
|
||||
* @throws IOException when there is an I/O error while committing the session.
|
||||
* @throws SecurityException when the caller is not the owner of the session.
|
||||
* @throws IllegalArgumentException when the passed parameters are not valid.
|
||||
* @throws IllegalStateException when the caller tries to commit a session which was
|
||||
* already finalized.
|
||||
*/
|
||||
public void commit(@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull Consumer<Integer> resultCallback) throws IOException {
|
||||
try {
|
||||
mSession.commit(new IBlobCommitCallback.Stub() {
|
||||
public void onResult(int result) {
|
||||
executor.execute(PooledLambda.obtainRunnable(
|
||||
Consumer::accept, resultCallback, result));
|
||||
}
|
||||
});
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app.blob;
|
||||
|
||||
import android.app.SystemServiceRegistry;
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* This is where the BlobStoreManagerService wrapper is registered.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class BlobStoreManagerFrameworkInitializer {
|
||||
/** Register the BlobStoreManager wrapper class */
|
||||
public static void initialize() {
|
||||
SystemServiceRegistry.registerContextAwareService(
|
||||
Context.BLOB_STORE_SERVICE, BlobStoreManager.class,
|
||||
(context, service) ->
|
||||
new BlobStoreManager(context, IBlobStoreManager.Stub.asInterface(service)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app.blob;
|
||||
|
||||
/** {@hide} */
|
||||
oneway interface IBlobCommitCallback {
|
||||
void onResult(int result);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright 2019, The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app.blob;
|
||||
|
||||
import android.app.blob.BlobHandle;
|
||||
import android.app.blob.BlobInfo;
|
||||
import android.app.blob.IBlobStoreSession;
|
||||
import android.app.blob.LeaseInfo;
|
||||
import android.os.RemoteCallback;
|
||||
|
||||
/** {@hide} */
|
||||
interface IBlobStoreManager {
|
||||
long createSession(in BlobHandle handle, in String packageName);
|
||||
IBlobStoreSession openSession(long sessionId, in String packageName);
|
||||
ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName);
|
||||
void abandonSession(long sessionId, in String packageName);
|
||||
|
||||
void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description,
|
||||
long leaseTimeoutMillis, in String packageName);
|
||||
void releaseLease(in BlobHandle handle, in String packageName);
|
||||
long getRemainingLeaseQuotaBytes(String packageName);
|
||||
|
||||
void waitForIdle(in RemoteCallback callback);
|
||||
|
||||
List<BlobInfo> queryBlobsForUser(int userId);
|
||||
void deleteBlob(long blobId);
|
||||
|
||||
List<BlobHandle> getLeasedBlobs(in String packageName);
|
||||
LeaseInfo getLeaseInfo(in BlobHandle blobHandle, in String packageName);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app.blob;
|
||||
|
||||
import android.app.blob.IBlobCommitCallback;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
/** {@hide} */
|
||||
interface IBlobStoreSession {
|
||||
ParcelFileDescriptor openWrite(long offsetBytes, long lengthBytes);
|
||||
ParcelFileDescriptor openRead();
|
||||
|
||||
void allowPackageAccess(in String packageName, in byte[] certificate);
|
||||
void allowSameSignatureAccess();
|
||||
void allowPublicAccess();
|
||||
|
||||
boolean isPackageAccessAllowed(in String packageName, in byte[] certificate);
|
||||
boolean isSameSignatureAccessAllowed();
|
||||
boolean isPublicAccessAllowed();
|
||||
|
||||
long getSize();
|
||||
void close();
|
||||
void abandon();
|
||||
|
||||
void commit(in IBlobCommitCallback callback);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app.blob;
|
||||
|
||||
/** {@hide} */
|
||||
parcelable LeaseInfo;
|
||||
130
apex/blobstore/framework/java/android/app/blob/LeaseInfo.java
Normal file
130
apex/blobstore/framework/java/android/app/blob/LeaseInfo.java
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.blob;
|
||||
|
||||
import android.annotation.CurrentTimeMillisLong;
|
||||
import android.annotation.IdRes;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TestApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class to provide information about a lease (acquired using
|
||||
* {@link BlobStoreManager#acquireLease(BlobHandle, int)} or one of it's variants)
|
||||
* for a shared blob.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public final class LeaseInfo implements Parcelable {
|
||||
private final String mPackageName;
|
||||
private final long mExpiryTimeMillis;
|
||||
private final int mDescriptionResId;
|
||||
private final CharSequence mDescription;
|
||||
|
||||
public LeaseInfo(@NonNull String packageName, @CurrentTimeMillisLong long expiryTimeMs,
|
||||
@IdRes int descriptionResId, @Nullable CharSequence description) {
|
||||
mPackageName = packageName;
|
||||
mExpiryTimeMillis = expiryTimeMs;
|
||||
mDescriptionResId = descriptionResId;
|
||||
mDescription = description;
|
||||
}
|
||||
|
||||
private LeaseInfo(Parcel in) {
|
||||
mPackageName = in.readString();
|
||||
mExpiryTimeMillis = in.readLong();
|
||||
mDescriptionResId = in.readInt();
|
||||
mDescription = in.readCharSequence();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
@CurrentTimeMillisLong
|
||||
public long getExpiryTimeMillis() {
|
||||
return mExpiryTimeMillis;
|
||||
}
|
||||
|
||||
@IdRes
|
||||
public int getDescriptionResId() {
|
||||
return mDescriptionResId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CharSequence getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeString(mPackageName);
|
||||
dest.writeLong(mExpiryTimeMillis);
|
||||
dest.writeInt(mDescriptionResId);
|
||||
dest.writeCharSequence(mDescription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LeaseInfo {"
|
||||
+ "package: " + mPackageName + ","
|
||||
+ "expiryMs: " + mExpiryTimeMillis + ","
|
||||
+ "descriptionResId: " + mDescriptionResId + ","
|
||||
+ "description: " + mDescription + ","
|
||||
+ "}";
|
||||
}
|
||||
|
||||
private String toShortString() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
static String toShortString(List<LeaseInfo> leaseInfos) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
for (int i = 0, size = leaseInfos.size(); i < size; ++i) {
|
||||
sb.append(leaseInfos.get(i).toShortString());
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<LeaseInfo> CREATOR = new Creator<LeaseInfo>() {
|
||||
@Override
|
||||
@NonNull
|
||||
public LeaseInfo createFromParcel(Parcel source) {
|
||||
return new LeaseInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public LeaseInfo[] newArray(int size) {
|
||||
return new LeaseInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
58
apex/blobstore/framework/java/android/app/blob/XmlTags.java
Normal file
58
apex/blobstore/framework/java/android/app/blob/XmlTags.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app.blob;
|
||||
|
||||
/** @hide */
|
||||
public final class XmlTags {
|
||||
public static final String ATTR_VERSION = "v";
|
||||
|
||||
public static final String TAG_SESSIONS = "ss";
|
||||
public static final String TAG_BLOBS = "bs";
|
||||
|
||||
// For BlobStoreSession
|
||||
public static final String TAG_SESSION = "s";
|
||||
public static final String ATTR_ID = "id";
|
||||
public static final String ATTR_PACKAGE = "p";
|
||||
public static final String ATTR_UID = "u";
|
||||
public static final String ATTR_CREATION_TIME_MS = "crt";
|
||||
|
||||
// For BlobMetadata
|
||||
public static final String TAG_BLOB = "b";
|
||||
public static final String ATTR_USER_ID = "us";
|
||||
|
||||
// For BlobAccessMode
|
||||
public static final String TAG_ACCESS_MODE = "am";
|
||||
public static final String ATTR_TYPE = "t";
|
||||
public static final String TAG_WHITELISTED_PACKAGE = "wl";
|
||||
public static final String ATTR_CERTIFICATE = "ct";
|
||||
|
||||
// For BlobHandle
|
||||
public static final String TAG_BLOB_HANDLE = "bh";
|
||||
public static final String ATTR_ALGO = "al";
|
||||
public static final String ATTR_DIGEST = "dg";
|
||||
public static final String ATTR_LABEL = "lbl";
|
||||
public static final String ATTR_EXPIRY_TIME = "ex";
|
||||
public static final String ATTR_TAG = "tg";
|
||||
|
||||
// For committer
|
||||
public static final String TAG_COMMITTER = "c";
|
||||
public static final String ATTR_COMMIT_TIME_MS = "cmt";
|
||||
|
||||
// For leasee
|
||||
public static final String TAG_LEASEE = "l";
|
||||
public static final String ATTR_DESCRIPTION_RES_NAME = "rn";
|
||||
public static final String ATTR_DESCRIPTION = "d";
|
||||
}
|
||||
28
apex/blobstore/service/Android.bp
Normal file
28
apex/blobstore/service/Android.bp
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2019 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
java_library {
|
||||
name: "service-blobstore",
|
||||
installable: true,
|
||||
|
||||
srcs: [
|
||||
"java/**/*.java",
|
||||
],
|
||||
|
||||
libs: [
|
||||
"framework",
|
||||
"services.core",
|
||||
"services.usage",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.server.blob;
|
||||
|
||||
import static android.app.blob.XmlTags.ATTR_CERTIFICATE;
|
||||
import static android.app.blob.XmlTags.ATTR_PACKAGE;
|
||||
import static android.app.blob.XmlTags.ATTR_TYPE;
|
||||
import static android.app.blob.XmlTags.TAG_WHITELISTED_PACKAGE;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Base64;
|
||||
import android.util.DebugUtils;
|
||||
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Class for representing how a blob can be shared.
|
||||
*
|
||||
* Note that this class is not thread-safe, callers need to take care of synchronizing access.
|
||||
*/
|
||||
class BlobAccessMode {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(flag = true, value = {
|
||||
ACCESS_TYPE_PRIVATE,
|
||||
ACCESS_TYPE_PUBLIC,
|
||||
ACCESS_TYPE_SAME_SIGNATURE,
|
||||
ACCESS_TYPE_WHITELIST,
|
||||
})
|
||||
@interface AccessType {}
|
||||
public static final int ACCESS_TYPE_PRIVATE = 1 << 0;
|
||||
public static final int ACCESS_TYPE_PUBLIC = 1 << 1;
|
||||
public static final int ACCESS_TYPE_SAME_SIGNATURE = 1 << 2;
|
||||
public static final int ACCESS_TYPE_WHITELIST = 1 << 3;
|
||||
|
||||
private int mAccessType = ACCESS_TYPE_PRIVATE;
|
||||
|
||||
private final ArraySet<PackageIdentifier> mWhitelistedPackages = new ArraySet<>();
|
||||
|
||||
void allow(BlobAccessMode other) {
|
||||
if ((other.mAccessType & ACCESS_TYPE_WHITELIST) != 0) {
|
||||
mWhitelistedPackages.addAll(other.mWhitelistedPackages);
|
||||
}
|
||||
mAccessType |= other.mAccessType;
|
||||
}
|
||||
|
||||
void allowPublicAccess() {
|
||||
mAccessType |= ACCESS_TYPE_PUBLIC;
|
||||
}
|
||||
|
||||
void allowSameSignatureAccess() {
|
||||
mAccessType |= ACCESS_TYPE_SAME_SIGNATURE;
|
||||
}
|
||||
|
||||
void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate) {
|
||||
mAccessType |= ACCESS_TYPE_WHITELIST;
|
||||
mWhitelistedPackages.add(PackageIdentifier.create(packageName, certificate));
|
||||
}
|
||||
|
||||
boolean isPublicAccessAllowed() {
|
||||
return (mAccessType & ACCESS_TYPE_PUBLIC) != 0;
|
||||
}
|
||||
|
||||
boolean isSameSignatureAccessAllowed() {
|
||||
return (mAccessType & ACCESS_TYPE_SAME_SIGNATURE) != 0;
|
||||
}
|
||||
|
||||
boolean isPackageAccessAllowed(@NonNull String packageName, @NonNull byte[] certificate) {
|
||||
if ((mAccessType & ACCESS_TYPE_WHITELIST) == 0) {
|
||||
return false;
|
||||
}
|
||||
return mWhitelistedPackages.contains(PackageIdentifier.create(packageName, certificate));
|
||||
}
|
||||
|
||||
boolean isAccessAllowedForCaller(Context context,
|
||||
@NonNull String callingPackage, @NonNull String committerPackage) {
|
||||
if ((mAccessType & ACCESS_TYPE_PUBLIC) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
if ((mAccessType & ACCESS_TYPE_SAME_SIGNATURE) != 0) {
|
||||
if (pm.checkSignatures(committerPackage, callingPackage)
|
||||
== PackageManager.SIGNATURE_MATCH) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((mAccessType & ACCESS_TYPE_WHITELIST) != 0) {
|
||||
for (int i = 0; i < mWhitelistedPackages.size(); ++i) {
|
||||
final PackageIdentifier packageIdentifier = mWhitelistedPackages.valueAt(i);
|
||||
if (packageIdentifier.packageName.equals(callingPackage)
|
||||
&& pm.hasSigningCertificate(callingPackage, packageIdentifier.certificate,
|
||||
PackageManager.CERT_INPUT_SHA256)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int getAccessType() {
|
||||
return mAccessType;
|
||||
}
|
||||
|
||||
int getNumWhitelistedPackages() {
|
||||
return mWhitelistedPackages.size();
|
||||
}
|
||||
|
||||
void dump(IndentingPrintWriter fout) {
|
||||
fout.println("accessType: " + DebugUtils.flagsToString(
|
||||
BlobAccessMode.class, "ACCESS_TYPE_", mAccessType));
|
||||
fout.print("Whitelisted pkgs:");
|
||||
if (mWhitelistedPackages.isEmpty()) {
|
||||
fout.println(" (Empty)");
|
||||
} else {
|
||||
fout.increaseIndent();
|
||||
for (int i = 0, count = mWhitelistedPackages.size(); i < count; ++i) {
|
||||
fout.println(mWhitelistedPackages.valueAt(i).toString());
|
||||
}
|
||||
fout.decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
||||
void writeToXml(@NonNull XmlSerializer out) throws IOException {
|
||||
XmlUtils.writeIntAttribute(out, ATTR_TYPE, mAccessType);
|
||||
for (int i = 0, count = mWhitelistedPackages.size(); i < count; ++i) {
|
||||
out.startTag(null, TAG_WHITELISTED_PACKAGE);
|
||||
final PackageIdentifier packageIdentifier = mWhitelistedPackages.valueAt(i);
|
||||
XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageIdentifier.packageName);
|
||||
XmlUtils.writeByteArrayAttribute(out, ATTR_CERTIFICATE, packageIdentifier.certificate);
|
||||
out.endTag(null, TAG_WHITELISTED_PACKAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static BlobAccessMode createFromXml(@NonNull XmlPullParser in)
|
||||
throws IOException, XmlPullParserException {
|
||||
final BlobAccessMode blobAccessMode = new BlobAccessMode();
|
||||
|
||||
final int accessType = XmlUtils.readIntAttribute(in, ATTR_TYPE);
|
||||
blobAccessMode.mAccessType = accessType;
|
||||
|
||||
final int depth = in.getDepth();
|
||||
while (XmlUtils.nextElementWithin(in, depth)) {
|
||||
if (TAG_WHITELISTED_PACKAGE.equals(in.getName())) {
|
||||
final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
|
||||
final byte[] certificate = XmlUtils.readByteArrayAttribute(in, ATTR_CERTIFICATE);
|
||||
blobAccessMode.allowPackageAccess(packageName, certificate);
|
||||
}
|
||||
}
|
||||
return blobAccessMode;
|
||||
}
|
||||
|
||||
private static final class PackageIdentifier {
|
||||
public final String packageName;
|
||||
public final byte[] certificate;
|
||||
|
||||
private PackageIdentifier(@NonNull String packageName, @NonNull byte[] certificate) {
|
||||
this.packageName = packageName;
|
||||
this.certificate = certificate;
|
||||
}
|
||||
|
||||
public static PackageIdentifier create(@NonNull String packageName,
|
||||
@NonNull byte[] certificate) {
|
||||
return new PackageIdentifier(packageName, certificate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || !(obj instanceof PackageIdentifier)) {
|
||||
return false;
|
||||
}
|
||||
final PackageIdentifier other = (PackageIdentifier) obj;
|
||||
return this.packageName.equals(other.packageName)
|
||||
&& Arrays.equals(this.certificate, other.certificate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(packageName, Arrays.hashCode(certificate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + packageName + ", "
|
||||
+ Base64.encodeToString(certificate, Base64.NO_WRAP) + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,824 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.server.blob;
|
||||
|
||||
import static android.app.blob.XmlTags.ATTR_COMMIT_TIME_MS;
|
||||
import static android.app.blob.XmlTags.ATTR_DESCRIPTION;
|
||||
import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME;
|
||||
import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
|
||||
import static android.app.blob.XmlTags.ATTR_ID;
|
||||
import static android.app.blob.XmlTags.ATTR_PACKAGE;
|
||||
import static android.app.blob.XmlTags.ATTR_UID;
|
||||
import static android.app.blob.XmlTags.ATTR_USER_ID;
|
||||
import static android.app.blob.XmlTags.TAG_ACCESS_MODE;
|
||||
import static android.app.blob.XmlTags.TAG_BLOB_HANDLE;
|
||||
import static android.app.blob.XmlTags.TAG_COMMITTER;
|
||||
import static android.app.blob.XmlTags.TAG_LEASEE;
|
||||
import static android.os.Process.INVALID_UID;
|
||||
import static android.system.OsConstants.O_RDONLY;
|
||||
import static android.text.format.Formatter.FLAG_IEC_UNITS;
|
||||
import static android.text.format.Formatter.formatFileSize;
|
||||
|
||||
import static com.android.server.blob.BlobStoreConfig.TAG;
|
||||
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_COMMIT_TIME;
|
||||
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_DESC_RES_NAME;
|
||||
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC;
|
||||
import static com.android.server.blob.BlobStoreConfig.hasLeaseWaitTimeElapsed;
|
||||
import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId;
|
||||
import static com.android.server.blob.BlobStoreUtils.getPackageResources;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.blob.BlobHandle;
|
||||
import android.app.blob.LeaseInfo;
|
||||
import android.content.Context;
|
||||
import android.content.res.ResourceId;
|
||||
import android.content.res.Resources;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RevocableFileDescriptor;
|
||||
import android.os.UserHandle;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.StatsEvent;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class BlobMetadata {
|
||||
private final Object mMetadataLock = new Object();
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private final long mBlobId;
|
||||
private final BlobHandle mBlobHandle;
|
||||
private final int mUserId;
|
||||
|
||||
@GuardedBy("mMetadataLock")
|
||||
private final ArraySet<Committer> mCommitters = new ArraySet<>();
|
||||
|
||||
@GuardedBy("mMetadataLock")
|
||||
private final ArraySet<Leasee> mLeasees = new ArraySet<>();
|
||||
|
||||
/**
|
||||
* Contains packageName -> {RevocableFileDescriptors}.
|
||||
*
|
||||
* Keep track of RevocableFileDescriptors given to clients which are not yet revoked/closed so
|
||||
* that when clients access is revoked or the blob gets deleted, we can be sure that clients
|
||||
* do not have any reference to the blob and the space occupied by the blob can be freed.
|
||||
*/
|
||||
@GuardedBy("mRevocableFds")
|
||||
private final ArrayMap<String, ArraySet<RevocableFileDescriptor>> mRevocableFds =
|
||||
new ArrayMap<>();
|
||||
|
||||
// Do not access this directly, instead use #getBlobFile().
|
||||
private File mBlobFile;
|
||||
|
||||
BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) {
|
||||
mContext = context;
|
||||
this.mBlobId = blobId;
|
||||
this.mBlobHandle = blobHandle;
|
||||
this.mUserId = userId;
|
||||
}
|
||||
|
||||
long getBlobId() {
|
||||
return mBlobId;
|
||||
}
|
||||
|
||||
BlobHandle getBlobHandle() {
|
||||
return mBlobHandle;
|
||||
}
|
||||
|
||||
int getUserId() {
|
||||
return mUserId;
|
||||
}
|
||||
|
||||
void addOrReplaceCommitter(@NonNull Committer committer) {
|
||||
synchronized (mMetadataLock) {
|
||||
// We need to override the committer data, so first remove any existing
|
||||
// committer before adding the new one.
|
||||
mCommitters.remove(committer);
|
||||
mCommitters.add(committer);
|
||||
}
|
||||
}
|
||||
|
||||
void setCommitters(ArraySet<Committer> committers) {
|
||||
synchronized (mMetadataLock) {
|
||||
mCommitters.clear();
|
||||
mCommitters.addAll(committers);
|
||||
}
|
||||
}
|
||||
|
||||
void removeCommitter(@NonNull String packageName, int uid) {
|
||||
synchronized (mMetadataLock) {
|
||||
mCommitters.removeIf((committer) ->
|
||||
committer.uid == uid && committer.packageName.equals(packageName));
|
||||
}
|
||||
}
|
||||
|
||||
void removeCommitter(@NonNull Committer committer) {
|
||||
synchronized (mMetadataLock) {
|
||||
mCommitters.remove(committer);
|
||||
}
|
||||
}
|
||||
|
||||
void removeCommittersFromUnknownPkgs(SparseArray<String> knownPackages) {
|
||||
synchronized (mMetadataLock) {
|
||||
mCommitters.removeIf(committer ->
|
||||
!committer.packageName.equals(knownPackages.get(committer.uid)));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Committer getExistingCommitter(@NonNull String packageName, int uid) {
|
||||
synchronized (mCommitters) {
|
||||
for (int i = 0, size = mCommitters.size(); i < size; ++i) {
|
||||
final Committer committer = mCommitters.valueAt(i);
|
||||
if (committer.uid == uid && committer.packageName.equals(packageName)) {
|
||||
return committer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void addOrReplaceLeasee(String callingPackage, int callingUid, int descriptionResId,
|
||||
CharSequence description, long leaseExpiryTimeMillis) {
|
||||
synchronized (mMetadataLock) {
|
||||
// We need to override the leasee data, so first remove any existing
|
||||
// leasee before adding the new one.
|
||||
final Leasee leasee = new Leasee(mContext, callingPackage, callingUid,
|
||||
descriptionResId, description, leaseExpiryTimeMillis);
|
||||
mLeasees.remove(leasee);
|
||||
mLeasees.add(leasee);
|
||||
}
|
||||
}
|
||||
|
||||
void setLeasees(ArraySet<Leasee> leasees) {
|
||||
synchronized (mMetadataLock) {
|
||||
mLeasees.clear();
|
||||
mLeasees.addAll(leasees);
|
||||
}
|
||||
}
|
||||
|
||||
void removeLeasee(String packageName, int uid) {
|
||||
synchronized (mMetadataLock) {
|
||||
mLeasees.removeIf((leasee) ->
|
||||
leasee.uid == uid && leasee.packageName.equals(packageName));
|
||||
}
|
||||
}
|
||||
|
||||
void removeLeaseesFromUnknownPkgs(SparseArray<String> knownPackages) {
|
||||
synchronized (mMetadataLock) {
|
||||
mLeasees.removeIf(leasee ->
|
||||
!leasee.packageName.equals(knownPackages.get(leasee.uid)));
|
||||
}
|
||||
}
|
||||
|
||||
void removeExpiredLeases() {
|
||||
synchronized (mMetadataLock) {
|
||||
mLeasees.removeIf(leasee -> !leasee.isStillValid());
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasValidLeases() {
|
||||
synchronized (mMetadataLock) {
|
||||
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
|
||||
if (mLeasees.valueAt(i).isStillValid()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
long getSize() {
|
||||
return getBlobFile().length();
|
||||
}
|
||||
|
||||
boolean isAccessAllowedForCaller(@NonNull String callingPackage, int callingUid) {
|
||||
// Don't allow the blob to be accessed after it's expiry time has passed.
|
||||
if (getBlobHandle().isExpired()) {
|
||||
return false;
|
||||
}
|
||||
synchronized (mMetadataLock) {
|
||||
// Check if packageName already holds a lease on the blob.
|
||||
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
|
||||
final Leasee leasee = mLeasees.valueAt(i);
|
||||
if (leasee.isStillValid() && leasee.equals(callingPackage, callingUid)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, size = mCommitters.size(); i < size; ++i) {
|
||||
final Committer committer = mCommitters.valueAt(i);
|
||||
|
||||
// Check if the caller is the same package that committed the blob.
|
||||
if (committer.equals(callingPackage, callingUid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the caller is allowed access as per the access mode specified
|
||||
// by the committer.
|
||||
if (committer.blobAccessMode.isAccessAllowedForCaller(mContext,
|
||||
callingPackage, committer.packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isACommitter(@NonNull String packageName, int uid) {
|
||||
synchronized (mMetadataLock) {
|
||||
return isAnAccessor(mCommitters, packageName, uid);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isALeasee(@Nullable String packageName, int uid) {
|
||||
synchronized (mMetadataLock) {
|
||||
final Leasee leasee = getAccessor(mLeasees, packageName, uid);
|
||||
return leasee != null && leasee.isStillValid();
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends Accessor> boolean isAnAccessor(@NonNull ArraySet<T> accessors,
|
||||
@Nullable String packageName, int uid) {
|
||||
// Check if the package is an accessor of the data blob.
|
||||
return getAccessor(accessors, packageName, uid) != null;
|
||||
}
|
||||
|
||||
private static <T extends Accessor> T getAccessor(@NonNull ArraySet<T> accessors,
|
||||
@Nullable String packageName, int uid) {
|
||||
// Check if the package is an accessor of the data blob.
|
||||
for (int i = 0, size = accessors.size(); i < size; ++i) {
|
||||
final Accessor accessor = accessors.valueAt(i);
|
||||
if (packageName != null && uid != INVALID_UID
|
||||
&& accessor.equals(packageName, uid)) {
|
||||
return (T) accessor;
|
||||
} else if (packageName != null && accessor.packageName.equals(packageName)) {
|
||||
return (T) accessor;
|
||||
} else if (uid != INVALID_UID && accessor.uid == uid) {
|
||||
return (T) accessor;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean isALeasee(@NonNull String packageName) {
|
||||
return isALeasee(packageName, INVALID_UID);
|
||||
}
|
||||
|
||||
boolean isALeasee(int uid) {
|
||||
return isALeasee(null, uid);
|
||||
}
|
||||
|
||||
boolean hasOtherLeasees(@NonNull String packageName) {
|
||||
return hasOtherLeasees(packageName, INVALID_UID);
|
||||
}
|
||||
|
||||
boolean hasOtherLeasees(int uid) {
|
||||
return hasOtherLeasees(null, uid);
|
||||
}
|
||||
|
||||
private boolean hasOtherLeasees(@Nullable String packageName, int uid) {
|
||||
synchronized (mMetadataLock) {
|
||||
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
|
||||
final Leasee leasee = mLeasees.valueAt(i);
|
||||
if (!leasee.isStillValid()) {
|
||||
continue;
|
||||
}
|
||||
// TODO: Also exclude packages which are signed with same cert?
|
||||
if (packageName != null && uid != INVALID_UID
|
||||
&& !leasee.equals(packageName, uid)) {
|
||||
return true;
|
||||
} else if (packageName != null && !leasee.packageName.equals(packageName)) {
|
||||
return true;
|
||||
} else if (uid != INVALID_UID && leasee.uid != uid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
LeaseInfo getLeaseInfo(@NonNull String packageName, int uid) {
|
||||
synchronized (mMetadataLock) {
|
||||
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
|
||||
final Leasee leasee = mLeasees.valueAt(i);
|
||||
if (!leasee.isStillValid()) {
|
||||
continue;
|
||||
}
|
||||
if (leasee.uid == uid && leasee.packageName.equals(packageName)) {
|
||||
final int descriptionResId = leasee.descriptionResEntryName == null
|
||||
? Resources.ID_NULL
|
||||
: BlobStoreUtils.getDescriptionResourceId(
|
||||
mContext, leasee.descriptionResEntryName, leasee.packageName,
|
||||
UserHandle.getUserId(leasee.uid));
|
||||
return new LeaseInfo(packageName, leasee.expiryTimeMillis,
|
||||
descriptionResId, leasee.description);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void forEachLeasee(Consumer<Leasee> consumer) {
|
||||
synchronized (mMetadataLock) {
|
||||
mLeasees.forEach(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
File getBlobFile() {
|
||||
if (mBlobFile == null) {
|
||||
mBlobFile = BlobStoreConfig.getBlobFile(mBlobId);
|
||||
}
|
||||
return mBlobFile;
|
||||
}
|
||||
|
||||
ParcelFileDescriptor openForRead(String callingPackage) throws IOException {
|
||||
// TODO: Add limit on opened fds
|
||||
FileDescriptor fd;
|
||||
try {
|
||||
fd = Os.open(getBlobFile().getPath(), O_RDONLY, 0);
|
||||
} catch (ErrnoException e) {
|
||||
throw e.rethrowAsIOException();
|
||||
}
|
||||
try {
|
||||
if (BlobStoreConfig.shouldUseRevocableFdForReads()) {
|
||||
return createRevocableFd(fd, callingPackage);
|
||||
} else {
|
||||
return new ParcelFileDescriptor(fd);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
IoUtils.closeQuietly(fd);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ParcelFileDescriptor createRevocableFd(FileDescriptor fd,
|
||||
String callingPackage) throws IOException {
|
||||
final RevocableFileDescriptor revocableFd =
|
||||
new RevocableFileDescriptor(mContext, fd);
|
||||
synchronized (mRevocableFds) {
|
||||
ArraySet<RevocableFileDescriptor> revocableFdsForPkg =
|
||||
mRevocableFds.get(callingPackage);
|
||||
if (revocableFdsForPkg == null) {
|
||||
revocableFdsForPkg = new ArraySet<>();
|
||||
mRevocableFds.put(callingPackage, revocableFdsForPkg);
|
||||
}
|
||||
revocableFdsForPkg.add(revocableFd);
|
||||
}
|
||||
revocableFd.addOnCloseListener((e) -> {
|
||||
synchronized (mRevocableFds) {
|
||||
final ArraySet<RevocableFileDescriptor> revocableFdsForPkg =
|
||||
mRevocableFds.get(callingPackage);
|
||||
if (revocableFdsForPkg != null) {
|
||||
revocableFdsForPkg.remove(revocableFd);
|
||||
if (revocableFdsForPkg.isEmpty()) {
|
||||
mRevocableFds.remove(callingPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return revocableFd.getRevocableFileDescriptor();
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
revokeAllFds();
|
||||
getBlobFile().delete();
|
||||
}
|
||||
|
||||
private void revokeAllFds() {
|
||||
synchronized (mRevocableFds) {
|
||||
for (int i = 0, pkgCount = mRevocableFds.size(); i < pkgCount; ++i) {
|
||||
final ArraySet<RevocableFileDescriptor> packageFds =
|
||||
mRevocableFds.valueAt(i);
|
||||
if (packageFds == null) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0, fdCount = packageFds.size(); j < fdCount; ++j) {
|
||||
packageFds.valueAt(j).revoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean shouldBeDeleted(boolean respectLeaseWaitTime) {
|
||||
// Expired data blobs
|
||||
if (getBlobHandle().isExpired()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Blobs with no active leases
|
||||
if ((!respectLeaseWaitTime || hasLeaseWaitTimeElapsedForAll())
|
||||
&& !hasValidLeases()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean hasLeaseWaitTimeElapsedForAll() {
|
||||
for (int i = 0, size = mCommitters.size(); i < size; ++i) {
|
||||
final Committer committer = mCommitters.valueAt(i);
|
||||
if (!hasLeaseWaitTimeElapsed(committer.getCommitTimeMs())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
StatsEvent dumpAsStatsEvent(int atomTag) {
|
||||
synchronized (mMetadataLock) {
|
||||
ProtoOutputStream proto = new ProtoOutputStream();
|
||||
// Write Committer data to proto format
|
||||
for (int i = 0, size = mCommitters.size(); i < size; ++i) {
|
||||
final Committer committer = mCommitters.valueAt(i);
|
||||
final long token = proto.start(
|
||||
BlobStatsEventProto.BlobCommitterListProto.COMMITTER);
|
||||
proto.write(BlobStatsEventProto.BlobCommitterProto.UID, committer.uid);
|
||||
proto.write(BlobStatsEventProto.BlobCommitterProto.COMMIT_TIMESTAMP_MILLIS,
|
||||
committer.commitTimeMs);
|
||||
proto.write(BlobStatsEventProto.BlobCommitterProto.ACCESS_MODE,
|
||||
committer.blobAccessMode.getAccessType());
|
||||
proto.write(BlobStatsEventProto.BlobCommitterProto.NUM_WHITELISTED_PACKAGE,
|
||||
committer.blobAccessMode.getNumWhitelistedPackages());
|
||||
proto.end(token);
|
||||
}
|
||||
final byte[] committersBytes = proto.getBytes();
|
||||
|
||||
proto = new ProtoOutputStream();
|
||||
// Write Leasee data to proto format
|
||||
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
|
||||
final Leasee leasee = mLeasees.valueAt(i);
|
||||
final long token = proto.start(BlobStatsEventProto.BlobLeaseeListProto.LEASEE);
|
||||
proto.write(BlobStatsEventProto.BlobLeaseeProto.UID, leasee.uid);
|
||||
proto.write(BlobStatsEventProto.BlobLeaseeProto.LEASE_EXPIRY_TIMESTAMP_MILLIS,
|
||||
leasee.expiryTimeMillis);
|
||||
proto.end(token);
|
||||
}
|
||||
final byte[] leaseesBytes = proto.getBytes();
|
||||
|
||||
// Construct the StatsEvent to represent this Blob
|
||||
return StatsEvent.newBuilder()
|
||||
.setAtomId(atomTag)
|
||||
.writeLong(mBlobId)
|
||||
.writeLong(getSize())
|
||||
.writeLong(mBlobHandle.getExpiryTimeMillis())
|
||||
.writeByteArray(committersBytes)
|
||||
.writeByteArray(leaseesBytes)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
|
||||
synchronized (mMetadataLock) {
|
||||
fout.println("blobHandle:");
|
||||
fout.increaseIndent();
|
||||
mBlobHandle.dump(fout, dumpArgs.shouldDumpFull());
|
||||
fout.decreaseIndent();
|
||||
fout.println("size: " + formatFileSize(mContext, getSize(), FLAG_IEC_UNITS));
|
||||
|
||||
fout.println("Committers:");
|
||||
fout.increaseIndent();
|
||||
if (mCommitters.isEmpty()) {
|
||||
fout.println("<empty>");
|
||||
} else {
|
||||
for (int i = 0, count = mCommitters.size(); i < count; ++i) {
|
||||
final Committer committer = mCommitters.valueAt(i);
|
||||
fout.println("committer " + committer.toString());
|
||||
fout.increaseIndent();
|
||||
committer.dump(fout);
|
||||
fout.decreaseIndent();
|
||||
}
|
||||
}
|
||||
fout.decreaseIndent();
|
||||
|
||||
fout.println("Leasees:");
|
||||
fout.increaseIndent();
|
||||
if (mLeasees.isEmpty()) {
|
||||
fout.println("<empty>");
|
||||
} else {
|
||||
for (int i = 0, count = mLeasees.size(); i < count; ++i) {
|
||||
final Leasee leasee = mLeasees.valueAt(i);
|
||||
fout.println("leasee " + leasee.toString());
|
||||
fout.increaseIndent();
|
||||
leasee.dump(mContext, fout);
|
||||
fout.decreaseIndent();
|
||||
}
|
||||
}
|
||||
fout.decreaseIndent();
|
||||
|
||||
fout.println("Open fds:");
|
||||
fout.increaseIndent();
|
||||
if (mRevocableFds.isEmpty()) {
|
||||
fout.println("<empty>");
|
||||
} else {
|
||||
for (int i = 0, count = mRevocableFds.size(); i < count; ++i) {
|
||||
final String packageName = mRevocableFds.keyAt(i);
|
||||
final ArraySet<RevocableFileDescriptor> packageFds =
|
||||
mRevocableFds.valueAt(i);
|
||||
fout.println(packageName + "#" + packageFds.size());
|
||||
}
|
||||
}
|
||||
fout.decreaseIndent();
|
||||
}
|
||||
}
|
||||
|
||||
void writeToXml(XmlSerializer out) throws IOException {
|
||||
synchronized (mMetadataLock) {
|
||||
XmlUtils.writeLongAttribute(out, ATTR_ID, mBlobId);
|
||||
XmlUtils.writeIntAttribute(out, ATTR_USER_ID, mUserId);
|
||||
|
||||
out.startTag(null, TAG_BLOB_HANDLE);
|
||||
mBlobHandle.writeToXml(out);
|
||||
out.endTag(null, TAG_BLOB_HANDLE);
|
||||
|
||||
for (int i = 0, count = mCommitters.size(); i < count; ++i) {
|
||||
out.startTag(null, TAG_COMMITTER);
|
||||
mCommitters.valueAt(i).writeToXml(out);
|
||||
out.endTag(null, TAG_COMMITTER);
|
||||
}
|
||||
|
||||
for (int i = 0, count = mLeasees.size(); i < count; ++i) {
|
||||
out.startTag(null, TAG_LEASEE);
|
||||
mLeasees.valueAt(i).writeToXml(out);
|
||||
out.endTag(null, TAG_LEASEE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static BlobMetadata createFromXml(XmlPullParser in, int version, Context context)
|
||||
throws XmlPullParserException, IOException {
|
||||
final long blobId = XmlUtils.readLongAttribute(in, ATTR_ID);
|
||||
final int userId = XmlUtils.readIntAttribute(in, ATTR_USER_ID);
|
||||
|
||||
BlobHandle blobHandle = null;
|
||||
final ArraySet<Committer> committers = new ArraySet<>();
|
||||
final ArraySet<Leasee> leasees = new ArraySet<>();
|
||||
final int depth = in.getDepth();
|
||||
while (XmlUtils.nextElementWithin(in, depth)) {
|
||||
if (TAG_BLOB_HANDLE.equals(in.getName())) {
|
||||
blobHandle = BlobHandle.createFromXml(in);
|
||||
} else if (TAG_COMMITTER.equals(in.getName())) {
|
||||
final Committer committer = Committer.createFromXml(in, version);
|
||||
if (committer != null) {
|
||||
committers.add(committer);
|
||||
}
|
||||
} else if (TAG_LEASEE.equals(in.getName())) {
|
||||
leasees.add(Leasee.createFromXml(in, version));
|
||||
}
|
||||
}
|
||||
|
||||
if (blobHandle == null) {
|
||||
Slog.wtf(TAG, "blobHandle should be available");
|
||||
return null;
|
||||
}
|
||||
|
||||
final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle, userId);
|
||||
blobMetadata.setCommitters(committers);
|
||||
blobMetadata.setLeasees(leasees);
|
||||
return blobMetadata;
|
||||
}
|
||||
|
||||
static final class Committer extends Accessor {
|
||||
public final BlobAccessMode blobAccessMode;
|
||||
public final long commitTimeMs;
|
||||
|
||||
Committer(String packageName, int uid, BlobAccessMode blobAccessMode, long commitTimeMs) {
|
||||
super(packageName, uid);
|
||||
this.blobAccessMode = blobAccessMode;
|
||||
this.commitTimeMs = commitTimeMs;
|
||||
}
|
||||
|
||||
long getCommitTimeMs() {
|
||||
return commitTimeMs;
|
||||
}
|
||||
|
||||
void dump(IndentingPrintWriter fout) {
|
||||
fout.println("commit time: "
|
||||
+ (commitTimeMs == 0 ? "<null>" : BlobStoreUtils.formatTime(commitTimeMs)));
|
||||
fout.println("accessMode:");
|
||||
fout.increaseIndent();
|
||||
blobAccessMode.dump(fout);
|
||||
fout.decreaseIndent();
|
||||
}
|
||||
|
||||
void writeToXml(@NonNull XmlSerializer out) throws IOException {
|
||||
XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
|
||||
XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
|
||||
XmlUtils.writeLongAttribute(out, ATTR_COMMIT_TIME_MS, commitTimeMs);
|
||||
|
||||
out.startTag(null, TAG_ACCESS_MODE);
|
||||
blobAccessMode.writeToXml(out);
|
||||
out.endTag(null, TAG_ACCESS_MODE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Committer createFromXml(@NonNull XmlPullParser in, int version)
|
||||
throws XmlPullParserException, IOException {
|
||||
final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
|
||||
final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
|
||||
final long commitTimeMs = version >= XML_VERSION_ADD_COMMIT_TIME
|
||||
? XmlUtils.readLongAttribute(in, ATTR_COMMIT_TIME_MS)
|
||||
: 0;
|
||||
|
||||
final int depth = in.getDepth();
|
||||
BlobAccessMode blobAccessMode = null;
|
||||
while (XmlUtils.nextElementWithin(in, depth)) {
|
||||
if (TAG_ACCESS_MODE.equals(in.getName())) {
|
||||
blobAccessMode = BlobAccessMode.createFromXml(in);
|
||||
}
|
||||
}
|
||||
if (blobAccessMode == null) {
|
||||
Slog.wtf(TAG, "blobAccessMode should be available");
|
||||
return null;
|
||||
}
|
||||
return new Committer(packageName, uid, blobAccessMode, commitTimeMs);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Leasee extends Accessor {
|
||||
public final String descriptionResEntryName;
|
||||
public final CharSequence description;
|
||||
public final long expiryTimeMillis;
|
||||
|
||||
Leasee(@NonNull Context context, @NonNull String packageName,
|
||||
int uid, int descriptionResId,
|
||||
@Nullable CharSequence description, long expiryTimeMillis) {
|
||||
super(packageName, uid);
|
||||
final Resources packageResources = getPackageResources(context, packageName,
|
||||
UserHandle.getUserId(uid));
|
||||
this.descriptionResEntryName = getResourceEntryName(packageResources, descriptionResId);
|
||||
this.expiryTimeMillis = expiryTimeMillis;
|
||||
this.description = description == null
|
||||
? getDescription(packageResources, descriptionResId)
|
||||
: description;
|
||||
}
|
||||
|
||||
Leasee(String packageName, int uid, @Nullable String descriptionResEntryName,
|
||||
@Nullable CharSequence description, long expiryTimeMillis) {
|
||||
super(packageName, uid);
|
||||
this.descriptionResEntryName = descriptionResEntryName;
|
||||
this.expiryTimeMillis = expiryTimeMillis;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getResourceEntryName(@Nullable Resources packageResources,
|
||||
int resId) {
|
||||
if (!ResourceId.isValid(resId) || packageResources == null) {
|
||||
return null;
|
||||
}
|
||||
return packageResources.getResourceEntryName(resId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getDescription(@NonNull Context context,
|
||||
@NonNull String descriptionResEntryName, @NonNull String packageName, int userId) {
|
||||
if (descriptionResEntryName == null || descriptionResEntryName.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final Resources resources = getPackageResources(context, packageName, userId);
|
||||
if (resources == null) {
|
||||
return null;
|
||||
}
|
||||
final int resId = getDescriptionResourceId(resources, descriptionResEntryName,
|
||||
packageName);
|
||||
return resId == Resources.ID_NULL ? null : resources.getString(resId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getDescription(@Nullable Resources packageResources,
|
||||
int descriptionResId) {
|
||||
if (!ResourceId.isValid(descriptionResId) || packageResources == null) {
|
||||
return null;
|
||||
}
|
||||
return packageResources.getString(descriptionResId);
|
||||
}
|
||||
|
||||
boolean isStillValid() {
|
||||
return expiryTimeMillis == 0 || expiryTimeMillis >= System.currentTimeMillis();
|
||||
}
|
||||
|
||||
void dump(@NonNull Context context, @NonNull IndentingPrintWriter fout) {
|
||||
fout.println("desc: " + getDescriptionToDump(context));
|
||||
fout.println("expiryMs: " + expiryTimeMillis);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String getDescriptionToDump(@NonNull Context context) {
|
||||
String desc = getDescription(context, descriptionResEntryName, packageName,
|
||||
UserHandle.getUserId(uid));
|
||||
if (desc == null) {
|
||||
desc = description.toString();
|
||||
}
|
||||
return desc == null ? "<none>" : desc;
|
||||
}
|
||||
|
||||
void writeToXml(@NonNull XmlSerializer out) throws IOException {
|
||||
XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
|
||||
XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
|
||||
XmlUtils.writeStringAttribute(out, ATTR_DESCRIPTION_RES_NAME, descriptionResEntryName);
|
||||
XmlUtils.writeLongAttribute(out, ATTR_EXPIRY_TIME, expiryTimeMillis);
|
||||
XmlUtils.writeStringAttribute(out, ATTR_DESCRIPTION, description);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static Leasee createFromXml(@NonNull XmlPullParser in, int version)
|
||||
throws IOException {
|
||||
final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
|
||||
final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
|
||||
final String descriptionResEntryName;
|
||||
if (version >= XML_VERSION_ADD_DESC_RES_NAME) {
|
||||
descriptionResEntryName = XmlUtils.readStringAttribute(
|
||||
in, ATTR_DESCRIPTION_RES_NAME);
|
||||
} else {
|
||||
descriptionResEntryName = null;
|
||||
}
|
||||
final long expiryTimeMillis = XmlUtils.readLongAttribute(in, ATTR_EXPIRY_TIME);
|
||||
final CharSequence description;
|
||||
if (version >= XML_VERSION_ADD_STRING_DESC) {
|
||||
description = XmlUtils.readStringAttribute(in, ATTR_DESCRIPTION);
|
||||
} else {
|
||||
description = null;
|
||||
}
|
||||
|
||||
return new Leasee(packageName, uid, descriptionResEntryName,
|
||||
description, expiryTimeMillis);
|
||||
}
|
||||
}
|
||||
|
||||
static class Accessor {
|
||||
public final String packageName;
|
||||
public final int uid;
|
||||
|
||||
Accessor(String packageName, int uid) {
|
||||
this.packageName = packageName;
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
public boolean equals(String packageName, int uid) {
|
||||
return this.uid == uid && this.packageName.equals(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || !(obj instanceof Accessor)) {
|
||||
return false;
|
||||
}
|
||||
final Accessor other = (Accessor) obj;
|
||||
return this.uid == other.uid && this.packageName.equals(other.packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(packageName, uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + packageName + ", " + uid + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.server.blob;
|
||||
|
||||
import static android.provider.DeviceConfig.NAMESPACE_BLOBSTORE;
|
||||
import static android.text.format.Formatter.FLAG_IEC_UNITS;
|
||||
import static android.text.format.Formatter.formatFileSize;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.DeviceConfig.Properties;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DataUnit;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.TimeUtils;
|
||||
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
class BlobStoreConfig {
|
||||
public static final String TAG = "BlobStore";
|
||||
public static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
|
||||
|
||||
// Initial version.
|
||||
public static final int XML_VERSION_INIT = 1;
|
||||
// Added a string variant of lease description.
|
||||
public static final int XML_VERSION_ADD_STRING_DESC = 2;
|
||||
public static final int XML_VERSION_ADD_DESC_RES_NAME = 3;
|
||||
public static final int XML_VERSION_ADD_COMMIT_TIME = 4;
|
||||
public static final int XML_VERSION_ADD_SESSION_CREATION_TIME = 5;
|
||||
|
||||
public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_SESSION_CREATION_TIME;
|
||||
|
||||
public static final long INVALID_BLOB_ID = 0;
|
||||
public static final long INVALID_BLOB_SIZE = 0;
|
||||
|
||||
private static final String ROOT_DIR_NAME = "blobstore";
|
||||
private static final String BLOBS_DIR_NAME = "blobs";
|
||||
private static final String SESSIONS_INDEX_FILE_NAME = "sessions_index.xml";
|
||||
private static final String BLOBS_INDEX_FILE_NAME = "blobs_index.xml";
|
||||
|
||||
/**
|
||||
* Job Id for idle maintenance job ({@link BlobStoreIdleJobService}).
|
||||
*/
|
||||
public static final int IDLE_JOB_ID = 0xB70B1D7; // 191934935L
|
||||
|
||||
public static class DeviceConfigProperties {
|
||||
/**
|
||||
* Denotes the max time period (in millis) between each idle maintenance job run.
|
||||
*/
|
||||
public static final String KEY_IDLE_JOB_PERIOD_MS = "idle_job_period_ms";
|
||||
public static final long DEFAULT_IDLE_JOB_PERIOD_MS = TimeUnit.DAYS.toMillis(1);
|
||||
public static long IDLE_JOB_PERIOD_MS = DEFAULT_IDLE_JOB_PERIOD_MS;
|
||||
|
||||
/**
|
||||
* Denotes the timeout in millis after which sessions with no updates will be deleted.
|
||||
*/
|
||||
public static final String KEY_SESSION_EXPIRY_TIMEOUT_MS =
|
||||
"session_expiry_timeout_ms";
|
||||
public static final long DEFAULT_SESSION_EXPIRY_TIMEOUT_MS = TimeUnit.DAYS.toMillis(7);
|
||||
public static long SESSION_EXPIRY_TIMEOUT_MS = DEFAULT_SESSION_EXPIRY_TIMEOUT_MS;
|
||||
|
||||
/**
|
||||
* Denotes how low the limit for the amount of data, that an app will be allowed to acquire
|
||||
* a lease on, can be.
|
||||
*/
|
||||
public static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
|
||||
"total_bytes_per_app_limit_floor";
|
||||
public static final long DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
|
||||
DataUnit.MEBIBYTES.toBytes(300); // 300 MiB
|
||||
public static long TOTAL_BYTES_PER_APP_LIMIT_FLOOR =
|
||||
DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR;
|
||||
|
||||
/**
|
||||
* Denotes the maximum amount of data an app can acquire a lease on, in terms of fraction
|
||||
* of total disk space.
|
||||
*/
|
||||
public static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION =
|
||||
"total_bytes_per_app_limit_fraction";
|
||||
public static final float DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION = 0.01f;
|
||||
public static float TOTAL_BYTES_PER_APP_LIMIT_FRACTION =
|
||||
DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION;
|
||||
|
||||
/**
|
||||
* Denotes the duration from the time a blob is committed that we wait for a lease to
|
||||
* be acquired before deciding to delete the blob for having no leases.
|
||||
*/
|
||||
public static final String KEY_LEASE_ACQUISITION_WAIT_DURATION_MS =
|
||||
"lease_acquisition_wait_time_ms";
|
||||
public static final long DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS =
|
||||
TimeUnit.HOURS.toMillis(6);
|
||||
public static long LEASE_ACQUISITION_WAIT_DURATION_MS =
|
||||
DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS;
|
||||
|
||||
/**
|
||||
* Denotes the duration from the time a blob is committed that any new commits of the same
|
||||
* data blob from the same committer will be treated as if they occurred at the earlier
|
||||
* commit time.
|
||||
*/
|
||||
public static final String KEY_COMMIT_COOL_OFF_DURATION_MS =
|
||||
"commit_cool_off_duration_ms";
|
||||
public static final long DEFAULT_COMMIT_COOL_OFF_DURATION_MS =
|
||||
TimeUnit.HOURS.toMillis(48);
|
||||
public static long COMMIT_COOL_OFF_DURATION_MS =
|
||||
DEFAULT_COMMIT_COOL_OFF_DURATION_MS;
|
||||
|
||||
/**
|
||||
* Denotes whether to use RevocableFileDescriptor when apps try to read session/blob data.
|
||||
*/
|
||||
public static final String KEY_USE_REVOCABLE_FD_FOR_READS =
|
||||
"use_revocable_fd_for_reads";
|
||||
public static final boolean DEFAULT_USE_REVOCABLE_FD_FOR_READS = true;
|
||||
public static boolean USE_REVOCABLE_FD_FOR_READS =
|
||||
DEFAULT_USE_REVOCABLE_FD_FOR_READS;
|
||||
|
||||
/**
|
||||
* Denotes how long before a blob is deleted, once the last lease on it is released.
|
||||
*/
|
||||
public static final String KEY_DELETE_ON_LAST_LEASE_DELAY_MS =
|
||||
"delete_on_last_lease_delay_ms";
|
||||
public static final long DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS =
|
||||
TimeUnit.HOURS.toMillis(6);
|
||||
public static long DELETE_ON_LAST_LEASE_DELAY_MS =
|
||||
DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS;
|
||||
|
||||
/**
|
||||
* Denotes the maximum number of active sessions per app at any time.
|
||||
*/
|
||||
public static final String KEY_MAX_ACTIVE_SESSIONS = "max_active_sessions";
|
||||
public static int DEFAULT_MAX_ACTIVE_SESSIONS = 250;
|
||||
public static int MAX_ACTIVE_SESSIONS = DEFAULT_MAX_ACTIVE_SESSIONS;
|
||||
|
||||
/**
|
||||
* Denotes the maximum number of committed blobs per app at any time.
|
||||
*/
|
||||
public static final String KEY_MAX_COMMITTED_BLOBS = "max_committed_blobs";
|
||||
public static int DEFAULT_MAX_COMMITTED_BLOBS = 1000;
|
||||
public static int MAX_COMMITTED_BLOBS = DEFAULT_MAX_COMMITTED_BLOBS;
|
||||
|
||||
/**
|
||||
* Denotes the maximum number of leased blobs per app at any time.
|
||||
*/
|
||||
public static final String KEY_MAX_LEASED_BLOBS = "max_leased_blobs";
|
||||
public static int DEFAULT_MAX_LEASED_BLOBS = 500;
|
||||
public static int MAX_LEASED_BLOBS = DEFAULT_MAX_LEASED_BLOBS;
|
||||
|
||||
/**
|
||||
* Denotes the maximum number of packages explicitly permitted to access a blob
|
||||
* (permitted as part of creating a {@link BlobAccessMode}).
|
||||
*/
|
||||
public static final String KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = "max_permitted_pks";
|
||||
public static int DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = 300;
|
||||
public static int MAX_BLOB_ACCESS_PERMITTED_PACKAGES =
|
||||
DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
|
||||
|
||||
/**
|
||||
* Denotes the maximum number of characters that a lease description can have.
|
||||
*/
|
||||
public static final String KEY_LEASE_DESC_CHAR_LIMIT = "lease_desc_char_limit";
|
||||
public static int DEFAULT_LEASE_DESC_CHAR_LIMIT = 300;
|
||||
public static int LEASE_DESC_CHAR_LIMIT = DEFAULT_LEASE_DESC_CHAR_LIMIT;
|
||||
|
||||
static void refresh(Properties properties) {
|
||||
if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) {
|
||||
return;
|
||||
}
|
||||
properties.getKeyset().forEach(key -> {
|
||||
switch (key) {
|
||||
case KEY_IDLE_JOB_PERIOD_MS:
|
||||
IDLE_JOB_PERIOD_MS = properties.getLong(key, DEFAULT_IDLE_JOB_PERIOD_MS);
|
||||
break;
|
||||
case KEY_SESSION_EXPIRY_TIMEOUT_MS:
|
||||
SESSION_EXPIRY_TIMEOUT_MS = properties.getLong(key,
|
||||
DEFAULT_SESSION_EXPIRY_TIMEOUT_MS);
|
||||
break;
|
||||
case KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR:
|
||||
TOTAL_BYTES_PER_APP_LIMIT_FLOOR = properties.getLong(key,
|
||||
DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR);
|
||||
break;
|
||||
case KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION:
|
||||
TOTAL_BYTES_PER_APP_LIMIT_FRACTION = properties.getFloat(key,
|
||||
DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION);
|
||||
break;
|
||||
case KEY_LEASE_ACQUISITION_WAIT_DURATION_MS:
|
||||
LEASE_ACQUISITION_WAIT_DURATION_MS = properties.getLong(key,
|
||||
DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS);
|
||||
break;
|
||||
case KEY_COMMIT_COOL_OFF_DURATION_MS:
|
||||
COMMIT_COOL_OFF_DURATION_MS = properties.getLong(key,
|
||||
DEFAULT_COMMIT_COOL_OFF_DURATION_MS);
|
||||
break;
|
||||
case KEY_USE_REVOCABLE_FD_FOR_READS:
|
||||
USE_REVOCABLE_FD_FOR_READS = properties.getBoolean(key,
|
||||
DEFAULT_USE_REVOCABLE_FD_FOR_READS);
|
||||
break;
|
||||
case KEY_DELETE_ON_LAST_LEASE_DELAY_MS:
|
||||
DELETE_ON_LAST_LEASE_DELAY_MS = properties.getLong(key,
|
||||
DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS);
|
||||
break;
|
||||
case KEY_MAX_ACTIVE_SESSIONS:
|
||||
MAX_ACTIVE_SESSIONS = properties.getInt(key, DEFAULT_MAX_ACTIVE_SESSIONS);
|
||||
break;
|
||||
case KEY_MAX_COMMITTED_BLOBS:
|
||||
MAX_COMMITTED_BLOBS = properties.getInt(key, DEFAULT_MAX_COMMITTED_BLOBS);
|
||||
break;
|
||||
case KEY_MAX_LEASED_BLOBS:
|
||||
MAX_LEASED_BLOBS = properties.getInt(key, DEFAULT_MAX_LEASED_BLOBS);
|
||||
break;
|
||||
case KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES:
|
||||
MAX_BLOB_ACCESS_PERMITTED_PACKAGES = properties.getInt(key,
|
||||
DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES);
|
||||
break;
|
||||
case KEY_LEASE_DESC_CHAR_LIMIT:
|
||||
LEASE_DESC_CHAR_LIMIT = properties.getInt(key,
|
||||
DEFAULT_LEASE_DESC_CHAR_LIMIT);
|
||||
break;
|
||||
default:
|
||||
Slog.wtf(TAG, "Unknown key in device config properties: " + key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void dump(IndentingPrintWriter fout, Context context) {
|
||||
final String dumpFormat = "%s: [cur: %s, def: %s]";
|
||||
fout.println(String.format(dumpFormat, KEY_IDLE_JOB_PERIOD_MS,
|
||||
TimeUtils.formatDuration(IDLE_JOB_PERIOD_MS),
|
||||
TimeUtils.formatDuration(DEFAULT_IDLE_JOB_PERIOD_MS)));
|
||||
fout.println(String.format(dumpFormat, KEY_SESSION_EXPIRY_TIMEOUT_MS,
|
||||
TimeUtils.formatDuration(SESSION_EXPIRY_TIMEOUT_MS),
|
||||
TimeUtils.formatDuration(DEFAULT_SESSION_EXPIRY_TIMEOUT_MS)));
|
||||
fout.println(String.format(dumpFormat, KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR,
|
||||
formatFileSize(context, TOTAL_BYTES_PER_APP_LIMIT_FLOOR, FLAG_IEC_UNITS),
|
||||
formatFileSize(context, DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR,
|
||||
FLAG_IEC_UNITS)));
|
||||
fout.println(String.format(dumpFormat, KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION,
|
||||
TOTAL_BYTES_PER_APP_LIMIT_FRACTION,
|
||||
DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION));
|
||||
fout.println(String.format(dumpFormat, KEY_LEASE_ACQUISITION_WAIT_DURATION_MS,
|
||||
TimeUtils.formatDuration(LEASE_ACQUISITION_WAIT_DURATION_MS),
|
||||
TimeUtils.formatDuration(DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS)));
|
||||
fout.println(String.format(dumpFormat, KEY_COMMIT_COOL_OFF_DURATION_MS,
|
||||
TimeUtils.formatDuration(COMMIT_COOL_OFF_DURATION_MS),
|
||||
TimeUtils.formatDuration(DEFAULT_COMMIT_COOL_OFF_DURATION_MS)));
|
||||
fout.println(String.format(dumpFormat, KEY_USE_REVOCABLE_FD_FOR_READS,
|
||||
USE_REVOCABLE_FD_FOR_READS, DEFAULT_USE_REVOCABLE_FD_FOR_READS));
|
||||
fout.println(String.format(dumpFormat, KEY_DELETE_ON_LAST_LEASE_DELAY_MS,
|
||||
TimeUtils.formatDuration(DELETE_ON_LAST_LEASE_DELAY_MS),
|
||||
TimeUtils.formatDuration(DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS)));
|
||||
fout.println(String.format(dumpFormat, KEY_MAX_ACTIVE_SESSIONS,
|
||||
MAX_ACTIVE_SESSIONS, DEFAULT_MAX_ACTIVE_SESSIONS));
|
||||
fout.println(String.format(dumpFormat, KEY_MAX_COMMITTED_BLOBS,
|
||||
MAX_COMMITTED_BLOBS, DEFAULT_MAX_COMMITTED_BLOBS));
|
||||
fout.println(String.format(dumpFormat, KEY_MAX_LEASED_BLOBS,
|
||||
MAX_LEASED_BLOBS, DEFAULT_MAX_LEASED_BLOBS));
|
||||
fout.println(String.format(dumpFormat, KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
|
||||
MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
|
||||
DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES));
|
||||
fout.println(String.format(dumpFormat, KEY_LEASE_DESC_CHAR_LIMIT,
|
||||
LEASE_DESC_CHAR_LIMIT, DEFAULT_LEASE_DESC_CHAR_LIMIT));
|
||||
}
|
||||
}
|
||||
|
||||
public static void initialize(Context context) {
|
||||
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_BLOBSTORE,
|
||||
context.getMainExecutor(),
|
||||
properties -> DeviceConfigProperties.refresh(properties));
|
||||
DeviceConfigProperties.refresh(DeviceConfig.getProperties(NAMESPACE_BLOBSTORE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max time period (in millis) between each idle maintenance job run.
|
||||
*/
|
||||
public static long getIdleJobPeriodMs() {
|
||||
return DeviceConfigProperties.IDLE_JOB_PERIOD_MS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a session is expired or not. A session is considered expired if the session
|
||||
* has not been modified in a while (i.e. SESSION_EXPIRY_TIMEOUT_MS).
|
||||
*/
|
||||
public static boolean hasSessionExpired(long sessionLastModifiedMs) {
|
||||
return sessionLastModifiedMs
|
||||
< System.currentTimeMillis() - DeviceConfigProperties.SESSION_EXPIRY_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum amount of data that an app can acquire a lease on.
|
||||
*/
|
||||
public static long getAppDataBytesLimit() {
|
||||
final long totalBytesLimit = (long) (Environment.getDataSystemDirectory().getTotalSpace()
|
||||
* DeviceConfigProperties.TOTAL_BYTES_PER_APP_LIMIT_FRACTION);
|
||||
return Math.max(DeviceConfigProperties.TOTAL_BYTES_PER_APP_LIMIT_FLOOR, totalBytesLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the wait time for lease acquisition for a blob has elapsed.
|
||||
*/
|
||||
public static boolean hasLeaseWaitTimeElapsed(long commitTimeMs) {
|
||||
return commitTimeMs + DeviceConfigProperties.LEASE_ACQUISITION_WAIT_DURATION_MS
|
||||
< System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an adjusted commit time depending on whether commit cool-off period has elapsed.
|
||||
*
|
||||
* If this is the initial commit or the earlier commit cool-off period has elapsed, then
|
||||
* the new commit time is used. Otherwise, the earlier commit time is used.
|
||||
*/
|
||||
public static long getAdjustedCommitTimeMs(long oldCommitTimeMs, long newCommitTimeMs) {
|
||||
if (oldCommitTimeMs == 0 || hasCommitCoolOffPeriodElapsed(oldCommitTimeMs)) {
|
||||
return newCommitTimeMs;
|
||||
}
|
||||
return oldCommitTimeMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the commit cool-off period has elapsed.
|
||||
*/
|
||||
private static boolean hasCommitCoolOffPeriodElapsed(long commitTimeMs) {
|
||||
return commitTimeMs + DeviceConfigProperties.COMMIT_COOL_OFF_DURATION_MS
|
||||
< System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to use RevocableFileDescriptor when apps try to read session/blob data.
|
||||
*/
|
||||
public static boolean shouldUseRevocableFdForReads() {
|
||||
return DeviceConfigProperties.USE_REVOCABLE_FD_FOR_READS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the duration to wait before a blob is deleted, once the last lease on it is released.
|
||||
*/
|
||||
public static long getDeletionOnLastLeaseDelayMs() {
|
||||
return DeviceConfigProperties.DELETE_ON_LAST_LEASE_DELAY_MS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of active sessions per app.
|
||||
*/
|
||||
public static int getMaxActiveSessions() {
|
||||
return DeviceConfigProperties.MAX_ACTIVE_SESSIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of committed blobs per app.
|
||||
*/
|
||||
public static int getMaxCommittedBlobs() {
|
||||
return DeviceConfigProperties.MAX_COMMITTED_BLOBS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of leased blobs per app.
|
||||
*/
|
||||
public static int getMaxLeasedBlobs() {
|
||||
return DeviceConfigProperties.MAX_LEASED_BLOBS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of packages explicitly permitted to access a blob.
|
||||
*/
|
||||
public static int getMaxPermittedPackages() {
|
||||
return DeviceConfigProperties.MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lease description truncated to
|
||||
* {@link DeviceConfigProperties#LEASE_DESC_CHAR_LIMIT} characters.
|
||||
*/
|
||||
public static CharSequence getTruncatedLeaseDescription(CharSequence description) {
|
||||
if (TextUtils.isEmpty(description)) {
|
||||
return description;
|
||||
}
|
||||
return TextUtils.trimToLengthWithEllipsis(description,
|
||||
DeviceConfigProperties.LEASE_DESC_CHAR_LIMIT);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File prepareBlobFile(long sessionId) {
|
||||
final File blobsDir = prepareBlobsDir();
|
||||
return blobsDir == null ? null : getBlobFile(blobsDir, sessionId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static File getBlobFile(long sessionId) {
|
||||
return getBlobFile(getBlobsDir(), sessionId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static File getBlobFile(File blobsDir, long sessionId) {
|
||||
return new File(blobsDir, String.valueOf(sessionId));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File prepareBlobsDir() {
|
||||
final File blobsDir = getBlobsDir(prepareBlobStoreRootDir());
|
||||
if (!blobsDir.exists() && !blobsDir.mkdir()) {
|
||||
Slog.e(TAG, "Failed to mkdir(): " + blobsDir);
|
||||
return null;
|
||||
}
|
||||
return blobsDir;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static File getBlobsDir() {
|
||||
return getBlobsDir(getBlobStoreRootDir());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static File getBlobsDir(File blobsRootDir) {
|
||||
return new File(blobsRootDir, BLOBS_DIR_NAME);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File prepareSessionIndexFile() {
|
||||
final File blobStoreRootDir = prepareBlobStoreRootDir();
|
||||
if (blobStoreRootDir == null) {
|
||||
return null;
|
||||
}
|
||||
return new File(blobStoreRootDir, SESSIONS_INDEX_FILE_NAME);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File prepareBlobsIndexFile() {
|
||||
final File blobsStoreRootDir = prepareBlobStoreRootDir();
|
||||
if (blobsStoreRootDir == null) {
|
||||
return null;
|
||||
}
|
||||
return new File(blobsStoreRootDir, BLOBS_INDEX_FILE_NAME);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File prepareBlobStoreRootDir() {
|
||||
final File blobStoreRootDir = getBlobStoreRootDir();
|
||||
if (!blobStoreRootDir.exists() && !blobStoreRootDir.mkdir()) {
|
||||
Slog.e(TAG, "Failed to mkdir(): " + blobStoreRootDir);
|
||||
return null;
|
||||
}
|
||||
return blobStoreRootDir;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static File getBlobStoreRootDir() {
|
||||
return new File(Environment.getDataSystemDirectory(), ROOT_DIR_NAME);
|
||||
}
|
||||
|
||||
public static void dump(IndentingPrintWriter fout, Context context) {
|
||||
fout.println("XML current version: " + XML_VERSION_CURRENT);
|
||||
|
||||
fout.println("Idle job ID: " + IDLE_JOB_ID);
|
||||
|
||||
fout.println("Total bytes per app limit: " + formatFileSize(context,
|
||||
getAppDataBytesLimit(), FLAG_IEC_UNITS));
|
||||
|
||||
fout.println("Device config properties:");
|
||||
fout.increaseIndent();
|
||||
DeviceConfigProperties.dump(fout, context);
|
||||
fout.decreaseIndent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.server.blob;
|
||||
|
||||
import static com.android.server.blob.BlobStoreConfig.IDLE_JOB_ID;
|
||||
import static com.android.server.blob.BlobStoreConfig.LOGV;
|
||||
import static com.android.server.blob.BlobStoreConfig.TAG;
|
||||
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.app.job.JobService;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
/**
|
||||
* Maintenance job to clean up stale sessions and blobs.
|
||||
*/
|
||||
public class BlobStoreIdleJobService extends JobService {
|
||||
@Override
|
||||
public boolean onStartJob(final JobParameters params) {
|
||||
AsyncTask.execute(() -> {
|
||||
final BlobStoreManagerInternal blobStoreManagerInternal = LocalServices.getService(
|
||||
BlobStoreManagerInternal.class);
|
||||
blobStoreManagerInternal.onIdleMaintenance();
|
||||
jobFinished(params, false);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(final JobParameters params) {
|
||||
Slog.d(TAG, "Idle maintenance job is stopped; id=" + params.getJobId()
|
||||
+ ", reason=" + JobParameters.getReasonCodeDescription(params.getStopReason()));
|
||||
return false;
|
||||
}
|
||||
|
||||
static void schedule(Context context) {
|
||||
final JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
|
||||
Context.JOB_SCHEDULER_SERVICE);
|
||||
final JobInfo job = new JobInfo.Builder(IDLE_JOB_ID,
|
||||
new ComponentName(context, BlobStoreIdleJobService.class))
|
||||
.setRequiresDeviceIdle(true)
|
||||
.setRequiresCharging(true)
|
||||
.setPeriodic(BlobStoreConfig.getIdleJobPeriodMs())
|
||||
.build();
|
||||
jobScheduler.schedule(job);
|
||||
if (LOGV) {
|
||||
Slog.v(TAG, "Scheduling the idle maintenance job");
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user