Merge "Use the standard class name for dumpable keys" into main
diff --git a/ACTIVITY_SECURITY_OWNERS b/ACTIVITY_SECURITY_OWNERS
new file mode 100644
index 0000000..c39842e
--- /dev/null
+++ b/ACTIVITY_SECURITY_OWNERS
@@ -0,0 +1,2 @@
[email protected]
[email protected]
\ No newline at end of file
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 3191896..e71ded9 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -112,7 +112,7 @@
filegroup {
name: "framework-minus-apex-aconfig-srcjars",
- srcs: [
+ device_common_srcs: [
":framework-minus-apex-aconfig-declarations{.srcjars}",
],
}
diff --git a/Android.bp b/Android.bp
index b114898..d2e8003 100644
--- a/Android.bp
+++ b/Android.bp
@@ -61,7 +61,7 @@
filegroup {
name: "framework-non-updatable-sources",
- srcs: [
+ device_common_srcs: [
// Java/AIDL sources under frameworks/base
":framework-annotations",
":framework-blobstore-sources",
diff --git a/INTENT_OWNERS b/INTENT_OWNERS
index 58b5f2a..c828215 100644
--- a/INTENT_OWNERS
+++ b/INTENT_OWNERS
@@ -1,3 +1,4 @@
include /PACKAGE_MANAGER_OWNERS
include /services/core/java/com/android/server/wm/OWNERS
include /services/core/java/com/android/server/am/OWNERS
+include /ACTIVITY_SECURITY_OWNERS
\ No newline at end of file
diff --git a/OWNERS b/OWNERS
index eb2bfcf..d0a634e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -47,3 +47,4 @@
per-file BROADCASTS_OWNERS = file:/BROADCASTS_OWNERS
per-file ADPF_OWNERS = file:/ADPF_OWNERS
per-file GAME_MANAGER_OWNERS = file:/GAME_MANAGER_OWNERS
+per-file SDK_OWNERS = file:/SDK_OWNERS
diff --git a/SDK_OWNERS b/SDK_OWNERS
new file mode 100644
index 0000000..c9ca47a
--- /dev/null
+++ b/SDK_OWNERS
@@ -0,0 +1,6 @@
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp
index 96b9d6a..a95e1175 100644
--- a/apct-tests/perftests/core/apps/reources_manager/Android.bp
+++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp
@@ -27,7 +27,7 @@
static_libs: ["androidx.appcompat_appcompat"],
}
-genrule {
+java_genrule {
name: "LargeResourcesUncompressed",
srcs: [":LargeResourcesCompressed"],
out: ["LargeResourcesUncompressed.apk"],
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
index 3577fcd..f20b170 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -194,7 +194,7 @@
/**
* Simple benchmark for the amount of time to send a given number of messages
*/
- // @Test Temporarily disabled
+ @Test
@Parameters(method = "getParams")
public void time(Config config) throws Exception {
reset();
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
index ac57100..af3c405 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -198,7 +198,7 @@
executor.awaitTermination(5, TimeUnit.SECONDS);
}
- // @Test Temporarily disabled
+ @Test
@Parameters(method = "getParams")
public void throughput(Config config) throws Exception {
setup(config);
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 33f6899..ecb9a73 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -444,8 +444,13 @@
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- switch (intent.getAction()) {
+ switch (action) {
case Intent.ACTION_USER_REMOVED:
if (userId > 0) {
mHandler.doUserRemoved(userId);
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index a37779e..3e650da 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -666,7 +666,12 @@
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
- switch (intent.getAction()) {
+ final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
+ switch (action) {
case ConnectivityManager.CONNECTIVITY_ACTION: {
updateConnectivityState(intent);
} break;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index e3ac780..7a21697 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -84,9 +84,13 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
final String pkgName = getPackageName(intent);
final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- final String action = intent.getAction();
if (pkgUid == -1) {
Slog.e(TAG, "Didn't get package UID in intent (" + action + ")");
return;
diff --git a/api/Android.bp b/api/Android.bp
index 3f2316f..3c92cb2 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -124,7 +124,7 @@
}),
}
-genrule {
+java_genrule {
name: "frameworks-base-api-current-compat",
srcs: [
":android.api.public.latest",
@@ -140,7 +140,7 @@
"$(location :frameworks-base-api-current.txt)",
}
-genrule {
+java_genrule {
name: "frameworks-base-api-system-current-compat",
srcs: [
":android.api.public.latest",
@@ -160,7 +160,7 @@
"$(location :frameworks-base-api-system-current.txt)",
}
-genrule {
+java_genrule {
name: "frameworks-base-api-module-lib-current-compat",
srcs: [
":android.api.public.latest",
@@ -184,7 +184,7 @@
"$(location :frameworks-base-api-module-lib-current.txt)",
}
-genrule {
+java_genrule {
name: "frameworks-base-api-current.srcjar",
tools: ["merge_zips"],
out: ["current.srcjar"],
@@ -209,7 +209,7 @@
"$(location soong_zip) -o $(out) -C $(genDir)/out -D $(genDir)/out",
}
-genrule {
+java_genrule {
name: "sdk-annotations.zip",
defaults: ["sdk-annotations-defaults"],
srcs: [
@@ -218,7 +218,7 @@
],
}
-genrule {
+java_genrule {
name: "sdk-annotations-system.zip",
defaults: ["sdk-annotations-defaults"],
srcs: [
@@ -227,7 +227,7 @@
],
}
-genrule {
+java_genrule {
name: "sdk-annotations-module-lib.zip",
defaults: ["sdk-annotations-defaults"],
srcs: [
@@ -236,7 +236,7 @@
],
}
-genrule {
+java_genrule {
name: "sdk-annotations-system-server.zip",
defaults: ["sdk-annotations-defaults"],
srcs: [
@@ -245,7 +245,7 @@
],
}
-genrule {
+java_genrule {
name: "combined-removed-dex",
visibility: [
"//frameworks/base/boot",
@@ -460,7 +460,7 @@
tools: ["extract-flagged-apis"],
}
-genrule {
+java_genrule {
name: "flag-api-mapping-PublicApi",
defaults: ["flag-api-mapping-generation-defaults"],
srcs: [":frameworks-base-api-current.txt"],
@@ -470,7 +470,7 @@
},
}
-genrule {
+java_genrule {
name: "flag-api-mapping-SystemApi",
defaults: ["flag-api-mapping-generation-defaults"],
srcs: [":frameworks-base-api-system-current.txt"],
@@ -480,7 +480,7 @@
},
}
-genrule {
+java_genrule {
name: "flag-api-mapping-ModuleLibApi",
defaults: ["flag-api-mapping-generation-defaults"],
srcs: [":frameworks-base-api-module-lib-current.txt"],
@@ -490,7 +490,7 @@
},
}
-genrule {
+java_genrule {
name: "flag-api-mapping-SystemServerApi",
defaults: ["flag-api-mapping-generation-defaults"],
srcs: [":frameworks-base-api-system-server-current.txt"],
diff --git a/api/api.go b/api/api.go
index 1bbf370..29083df 100644
--- a/api/api.go
+++ b/api/api.go
@@ -20,7 +20,6 @@
"github.com/google/blueprint/proptools"
"android/soong/android"
- "android/soong/genrule"
"android/soong/java"
)
@@ -138,9 +137,10 @@
}
type fgProps struct {
- Name *string
- Srcs proptools.Configurable[[]string]
- Visibility []string
+ Name *string
+ Srcs proptools.Configurable[[]string]
+ Device_common_srcs proptools.Configurable[[]string]
+ Visibility []string
}
type defaultsProps struct {
@@ -201,7 +201,7 @@
}
}
props.Visibility = []string{"//visibility:public"}
- ctx.CreateModule(genrule.GenRuleFactory, &props)
+ ctx.CreateModule(java.GenRuleFactory, &props)
}
func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules proptools.Configurable[[]string]) {
@@ -230,7 +230,7 @@
} {
props := fgProps{}
props.Name = proptools.StringPtr(i.name)
- props.Srcs = createSrcs(i.modules, i.tag)
+ props.Device_common_srcs = createSrcs(i.modules, i.tag)
ctx.CreateModule(android.FileGroupFactory, &props)
}
}
@@ -429,7 +429,7 @@
func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
props := fgProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs-source")
- props.Srcs = createSrcs(modules, "{.public.stubs.source}")
+ props.Device_common_srcs = createSrcs(modules, "{.public.stubs.source}")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(android.FileGroupFactory, &props)
}
diff --git a/api/api_test.go b/api/api_test.go
index fb26f821..166f053 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -253,7 +253,7 @@
`)
subModuleDependsOnSelectAppendedModule := java.CheckModuleHasDependency(t,
- result.TestContext, "foo-current.txt", "", "framework-foo")
+ result.TestContext, "foo-current.txt", "android_common", "framework-foo")
android.AssertBoolEquals(t, "Submodule expected to depend on the select-appended module",
true, subModuleDependsOnSelectAppendedModule)
}
diff --git a/boot/Android.bp b/boot/Android.bp
index f60bb9e..6eead42 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -234,7 +234,7 @@
],
}
-genrule { // This module exists to make the srcjar output available to Make.
+java_genrule { // This module exists to make the srcjar output available to Make.
name: "platform-bootclasspath.srcjar",
srcs: [":platform-bootclasspath{.srcjar}"],
out: ["platform-bootclasspath.srcjar"],
diff --git a/core/api/Android.bp b/core/api/Android.bp
index 77594b7..06eea52 100644
--- a/core/api/Android.bp
+++ b/core/api/Android.bp
@@ -100,50 +100,50 @@
// Exportable stub artifacts
filegroup {
name: "non-updatable-exportable-current.txt",
- srcs: [":api-stubs-docs-non-updatable{.exportable.api.txt}"],
+ device_common_srcs: [":api-stubs-docs-non-updatable{.exportable.api.txt}"],
}
filegroup {
name: "non-updatable-exportable-removed.txt",
- srcs: [":api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+ device_common_srcs: [":api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
}
filegroup {
name: "non-updatable-exportable-system-current.txt",
- srcs: [":system-api-stubs-docs-non-updatable{.exportable.api.txt}"],
+ device_common_srcs: [":system-api-stubs-docs-non-updatable{.exportable.api.txt}"],
}
filegroup {
name: "non-updatable-exportable-system-removed.txt",
- srcs: [":system-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+ device_common_srcs: [":system-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
}
filegroup {
name: "non-updatable-exportable-module-lib-current.txt",
- srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.api.txt}"],
+ device_common_srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.api.txt}"],
}
filegroup {
name: "non-updatable-exportable-module-lib-removed.txt",
- srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+ device_common_srcs: [":module-lib-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
}
filegroup {
name: "non-updatable-exportable-test-current.txt",
- srcs: [":test-api-stubs-docs-non-updatable{.exportable.api.txt}"],
+ device_common_srcs: [":test-api-stubs-docs-non-updatable{.exportable.api.txt}"],
}
filegroup {
name: "non-updatable-exportable-test-removed.txt",
- srcs: [":test-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
+ device_common_srcs: [":test-api-stubs-docs-non-updatable{.exportable.removed-api.txt}"],
}
filegroup {
name: "non-updatable-exportable-system-server-current.txt",
- srcs: [":services-non-updatable-stubs{.exportable.api.txt}"],
+ device_common_srcs: [":services-non-updatable-stubs{.exportable.api.txt}"],
}
filegroup {
name: "non-updatable-exportable-system-server-removed.txt",
- srcs: [":services-non-updatable-stubs{.exportable.removed-api.txt}"],
+ device_common_srcs: [":services-non-updatable-stubs{.exportable.removed-api.txt}"],
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 2c4c146..6d9fe44 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -16232,6 +16232,7 @@
field public static final int UNKNOWN = 0; // 0x0
field public static final int Y8 = 538982489; // 0x20203859
field public static final int YCBCR_P010 = 54; // 0x36
+ field @FlaggedApi("android.media.codec.p210_format_support") public static final int YCBCR_P210 = 60; // 0x3c
field public static final int YUV_420_888 = 35; // 0x23
field public static final int YUV_422_888 = 39; // 0x27
field public static final int YUV_444_888 = 40; // 0x28
@@ -18580,6 +18581,7 @@
field public static final long USAGE_VIDEO_ENCODE = 65536L; // 0x10000L
field public static final int YCBCR_420_888 = 35; // 0x23
field public static final int YCBCR_P010 = 54; // 0x36
+ field @FlaggedApi("android.media.codec.p210_format_support") public static final int YCBCR_P210 = 60; // 0x3c
}
@FlaggedApi("android.hardware.flags.overlayproperties_class_api") public final class OverlayProperties implements android.os.Parcelable {
@@ -22871,6 +22873,7 @@
field public static final int COLOR_FormatYUV444Flexible = 2135181448; // 0x7f444888
field @Deprecated public static final int COLOR_FormatYUV444Interleaved = 29; // 0x1d
field public static final int COLOR_FormatYUVP010 = 54; // 0x36
+ field @FlaggedApi("android.media.codec.p210_format_support") public static final int COLOR_FormatYUVP210 = 60; // 0x3c
field @Deprecated public static final int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00
field @Deprecated public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final String FEATURE_AdaptivePlayback = "adaptive-playback";
@@ -22917,6 +22920,65 @@
field public static final int AC4Profile11 = 514; // 0x202
field public static final int AC4Profile21 = 1026; // 0x402
field public static final int AC4Profile22 = 1028; // 0x404
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band0 = 513; // 0x201
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band1 = 514; // 0x202
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band2 = 516; // 0x204
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band3 = 520; // 0x208
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band0 = 257; // 0x101
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band1 = 258; // 0x102
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band2 = 260; // 0x104
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band3 = 264; // 0x108
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band0 = 2049; // 0x801
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band1 = 2050; // 0x802
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band2 = 2052; // 0x804
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band3 = 2056; // 0x808
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band0 = 1025; // 0x401
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band1 = 1026; // 0x402
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band2 = 1028; // 0x404
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band3 = 1032; // 0x408
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band0 = 8193; // 0x2001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band1 = 8194; // 0x2002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band2 = 8196; // 0x2004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band3 = 8200; // 0x2008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band0 = 4097; // 0x1001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band1 = 4098; // 0x1002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band2 = 4100; // 0x1004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band3 = 4104; // 0x1008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band0 = 32769; // 0x8001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band1 = 32770; // 0x8002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band2 = 32772; // 0x8004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band3 = 32776; // 0x8008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band0 = 16385; // 0x4001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band1 = 16386; // 0x4002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band2 = 16388; // 0x4004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band3 = 16392; // 0x4008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band0 = 131073; // 0x20001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band1 = 131074; // 0x20002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band2 = 131076; // 0x20004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band3 = 131080; // 0x20008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band0 = 65537; // 0x10001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band1 = 65538; // 0x10002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band2 = 65540; // 0x10004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band3 = 65544; // 0x10008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band0 = 524289; // 0x80001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band1 = 524290; // 0x80002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band2 = 524292; // 0x80004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band3 = 524296; // 0x80008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band0 = 262145; // 0x40001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band1 = 262146; // 0x40002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band2 = 262148; // 0x40004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band3 = 262152; // 0x40008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band0 = 2097153; // 0x200001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band1 = 2097154; // 0x200002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band2 = 2097156; // 0x200004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band3 = 2097160; // 0x200008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band0 = 1048577; // 0x100001
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band1 = 1048578; // 0x100002
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band2 = 1048580; // 0x100004
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band3 = 1048584; // 0x100008
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10 = 1; // 0x1
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10HDR10 = 4096; // 0x1000
+ field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10HDR10Plus = 8192; // 0x2000
field public static final int AV1Level2 = 1; // 0x1
field public static final int AV1Level21 = 2; // 0x2
field public static final int AV1Level22 = 4; // 0x4
@@ -23767,6 +23829,7 @@
field public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
field public static final String MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
field public static final String MIMETYPE_TEXT_VTT = "text/vtt";
+ field @FlaggedApi("android.media.codec.apv_support") public static final String MIMETYPE_VIDEO_APV = "video/apv";
field public static final String MIMETYPE_VIDEO_AV1 = "video/av01";
field public static final String MIMETYPE_VIDEO_AVC = "video/avc";
field public static final String MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision";
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9a294fd..0b891f6 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -102,6 +102,7 @@
method @NonNull public android.os.UserHandle getUser();
field public static final String PAC_PROXY_SERVICE = "pac_proxy";
field public static final String TEST_NETWORK_SERVICE = "test_network";
+ field @FlaggedApi("android.os.mainline_vcn_platform_api") public static final String VCN_MANAGEMENT_SERVICE = "vcn_management";
field @FlaggedApi("android.webkit.update_service_ipc_wrapper") public static final String WEBVIEW_UPDATE_SERVICE = "webviewupdate";
}
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
old mode 100644
new mode 100755
index 69c3bd3..0264f73
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -61,6 +61,8 @@
oneway void shutdown();
void executeShellCommandWithStderr(String command, in ParcelFileDescriptor sink,
in ParcelFileDescriptor source, in ParcelFileDescriptor stderrSink);
+ void executeShellCommandArrayWithStderr(in String[] command, in ParcelFileDescriptor sink,
+ in ParcelFileDescriptor source, in ParcelFileDescriptor stderrSink);
List<String> getAdoptedShellPermissions();
void addOverridePermissionState(int uid, String permission, int result);
void removeOverridePermissionState(int uid, String permission);
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 27f9f54..cb31bc7 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -250,6 +250,7 @@
* @hide
*/
@TestApi
[email protected]
public class PropertyInvalidatedCache<Query, Result> {
/**
* This is a configuration class that customizes a cache instance.
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
old mode 100644
new mode 100755
index 3c4bd9e..72db465
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -553,7 +553,12 @@
} catch (IOException exc) {
throw new RuntimeException("Error running shell command '" + command + "'", exc);
}
+ handleExecuteShellCommandProcess(process, sink, source, stderrSink);
+ }
+ private void handleExecuteShellCommandProcess(final java.lang.Process process,
+ final ParcelFileDescriptor sink, final ParcelFileDescriptor source,
+ final ParcelFileDescriptor stderrSink) {
// Read from process and write to pipe
final Thread readFromProcess;
if (sink != null) {
@@ -616,6 +621,26 @@
}
@Override
+ public void executeShellCommandArrayWithStderr(final String[] command,
+ final ParcelFileDescriptor sink, final ParcelFileDescriptor source,
+ final ParcelFileDescriptor stderrSink) throws RemoteException {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
+ final java.lang.Process process;
+
+ try {
+ process = Runtime.getRuntime().exec(command);
+ } catch (IOException exc) {
+ throw new RuntimeException(
+ "Error running shell command '" + String.join(" ", command) + "'", exc);
+ }
+ handleExecuteShellCommandProcess(process, sink, source, stderrSink);
+ }
+
+ @Override
public void shutdown() {
synchronized (mLock) {
if (isConnectedLocked()) {
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 1f19f81..5f61a91 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -713,8 +713,8 @@
mCurrentSize);
} else {
applyContent(null, false, e);
+ mLastExecutionSignal = null;
}
- mLastExecutionSignal = null;
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 36bdf73..744f019 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4855,6 +4855,8 @@
* @see android.net.vcn.VcnManager
* @hide
*/
+ @FlaggedApi(android.os.Flags.FLAG_MAINLINE_VCN_PLATFORM_API)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final String VCN_MANAGEMENT_SERVICE = "vcn_management";
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bca30b4..8de86d5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -146,6 +146,7 @@
* This exception is thrown when a given package, application, or component
* name cannot be found.
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static class NameNotFoundException extends AndroidException {
public NameNotFoundException() {
}
diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java
index 8ea450c..41585b3 100644
--- a/core/java/android/database/BulkCursorNative.java
+++ b/core/java/android/database/BulkCursorNative.java
@@ -53,7 +53,7 @@
return new BulkCursorProxy(obj);
}
-
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -79,7 +79,7 @@
reply.writeNoException();
return true;
}
-
+
case CLOSE_TRANSACTION: {
data.enforceInterface(IBulkCursor.descriptor);
close();
@@ -212,15 +212,22 @@
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IBulkCursor.descriptor);
-
- mRemote.transact(CLOSE_TRANSACTION, data, reply, 0);
- DatabaseUtils.readExceptionFromParcel(reply);
+ // If close() is being called from the finalizer thread, do not wait for a reply from
+ // the remote side.
+ final boolean fromFinalizer =
+ android.database.sqlite.Flags.onewayFinalizerClose()
+ && "FinalizerDaemon".equals(Thread.currentThread().getName());
+ mRemote.transact(CLOSE_TRANSACTION, data, reply,
+ fromFinalizer ? IBinder.FLAG_ONEWAY: 0);
+ if (!fromFinalizer) {
+ DatabaseUtils.readExceptionFromParcel(reply);
+ }
} finally {
data.recycle();
reply.recycle();
}
}
-
+
public int requery(IContentObserver observer) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -282,4 +289,3 @@
}
}
}
-
diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig
index 285f984..c597895 100644
--- a/core/java/android/database/sqlite/flags.aconfig
+++ b/core/java/android/database/sqlite/flags.aconfig
@@ -2,6 +2,13 @@
container: "system"
flag {
+ name: "oneway_finalizer_close"
+ namespace: "system_performance"
+ description: "Make BuildCursorNative.close oneway if in the the finalizer"
+ bug: "368221351"
+}
+
+flag {
name: "sqlite_apis_35"
is_exported: true
namespace: "system_performance"
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index ce0f9f59..0e739781 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -66,6 +66,7 @@
DS_FP32UI8,
S_UI8,
YCBCR_P010,
+ YCBCR_P210,
R_8,
R_16,
RG_1616,
@@ -111,6 +112,16 @@
* little-endian value, with the lower 6 bits set to zero.
*/
public static final int YCBCR_P010 = 0x36;
+ /**
+ * <p>Android YUV P210 format.</p>
+ *
+ * P210 is a 4:2:2 YCbCr semiplanar format comprised of a WxH Y plane
+ * followed by a WxH CbCr plane. Each sample is represented by a 16-bit
+ * little-endian value, with the lower 6 bits set to zero.
+ */
+ @FlaggedApi(android.media.codec.Flags.FLAG_P210_FORMAT_SUPPORT)
+ public static final int YCBCR_P210 = 0x3c;
+
/** Format: 8 bits red */
@FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V)
public static final int R_8 = 0x38;
diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig
index 48eb968..f7dc790 100644
--- a/core/java/android/net/flags.aconfig
+++ b/core/java/android/net/flags.aconfig
@@ -5,13 +5,6 @@
# Flags used for module APIs must be in aconfig files under each modules
flag {
- name: "ipsec_transform_state"
- namespace: "core_networking_ipsec"
- description: "The flag controls the access for getIpSecTransformState and IpSecTransformState"
- bug: "308011229"
-}
-
-flag {
name: "powered_off_finding_platform"
namespace: "nearby"
description: "Controls whether the Powered Off Finding feature is enabled"
diff --git a/core/java/android/net/vcn/OWNERS b/core/java/android/net/vcn/OWNERS
index 2441e77..937699a 100644
--- a/core/java/android/net/vcn/OWNERS
+++ b/core/java/android/net/vcn/OWNERS
@@ -1,7 +1,6 @@
set noparent
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected] #{LAST_RESORT_SUGGESTION}
[email protected] #{LAST_RESORT_SUGGESTION}
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
index 5b30624..1adefe5 100644
--- a/core/java/android/net/vcn/flags.aconfig
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -10,8 +10,26 @@
}
flag {
+ name: "mainline_vcn_module_api"
+ namespace: "vcn"
+ description: "Expose APIs from VCN for mainline migration"
+ is_exported: true
+ bug: "376339506"
+}
+
+flag {
name: "safe_mode_timeout_config"
namespace: "vcn"
description: "Feature flag for adjustable safe mode timeout"
bug: "317406085"
+}
+
+flag {
+ name: "fix_config_garbage_collection"
+ namespace: "vcn"
+ description: "Handle race condition in subscription change"
+ bug: "370862489"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index bf44d65..d7a308d 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -242,6 +242,7 @@
*/
@TestApi
@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
[email protected]
public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, Result> {
/**
* {@inheritDoc}
diff --git a/core/java/android/service/contextualsearch/OWNERS b/core/java/android/service/contextualsearch/OWNERS
index b723872..c435bd8 100644
--- a/core/java/android/service/contextualsearch/OWNERS
+++ b/core/java/android/service/contextualsearch/OWNERS
@@ -1,2 +1,3 @@
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/core/java/android/service/settings/OWNERS b/core/java/android/service/settings/OWNERS
new file mode 100644
index 0000000..c70c738
--- /dev/null
+++ b/core/java/android/service/settings/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 27091
+
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 2acda8a..e402ddf 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -249,6 +249,10 @@
return isExperimentEnabled("profilesystemserver");
}
+ private static boolean shouldProfileBootClasspath() {
+ return isExperimentEnabled("profilebootclasspath");
+ }
+
/**
* Performs Zygote process initialization. Loads and initializes commonly used classes.
*
@@ -352,7 +356,7 @@
// If we are profiling the boot image, reset the Jit counters after preloading the
// classes. We want to preload for performance, and we can use method counters to
// infer what clases are used after calling resetJitCounters, for profile purposes.
- if (isExperimentEnabled("profilebootclasspath")) {
+ if (shouldProfileBootClasspath()) {
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ResetJitCounters");
VMRuntime.resetJitCounters();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
@@ -460,12 +464,28 @@
? String.join(":", systemServerClasspath, standaloneSystemServerJars)
: systemServerClasspath;
prepareSystemServerProfile(systemServerPaths);
+ try {
+ SystemProperties.set("debug.tracing.profile_system_server", "1");
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Failed to set debug.tracing.profile_system_server", e);
+ }
} catch (Exception e) {
Log.wtf(TAG, "Failed to set up system server profile", e);
}
}
}
+ // Zygote can't set system properties due to permission denied. We need to be in System
+ // Server to set system properties, so we do it here instead of the more natural place in
+ // preloadClasses.
+ if (shouldProfileBootClasspath()) {
+ try {
+ SystemProperties.set("debug.tracing.profile_boot_classpath", "1");
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Failed to set debug.tracing.profile_boot_classpath", e);
+ }
+ }
+
if (parsedArgs.mInvokeWith != null) {
String[] args = parsedArgs.mRemainingArgs;
// If we have a non-null system server class path, we'll have to duplicate the
diff --git a/core/java/com/android/internal/os/flags.aconfig b/core/java/com/android/internal/os/flags.aconfig
index c8d6810..809c1a1 100644
--- a/core/java/com/android/internal/os/flags.aconfig
+++ b/core/java/com/android/internal/os/flags.aconfig
@@ -2,6 +2,48 @@
container: "system"
flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_rw_1"
+ description: "Ravenwood test RW flag 1"
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_rw_2"
+ description: "Ravenwood test RW flag 2"
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_ro_1"
+ description: "Ravenwood test RO flag 1"
+ is_fixed_read_only: true
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_ro_2"
+ description: "Ravenwood test RO flag 2"
+ is_fixed_read_only: true
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_apache_http_legacy_preload"
namespace: "system_performance"
description: "Enables zygote preload of non-BCP org.apache.http.legacy.jar library."
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index 89fdeeb0..933781c 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -14,17 +14,19 @@
* limitations under the License.
*/
+#include <core_jni_helpers.h>
+#include <cutils/trace.h>
#include <fcntl.h>
+#include <minikin/Hyphenator.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <tracing_perfetto.h>
+#include <unicode/uloc.h>
#include <unistd.h>
#include <algorithm>
-#include <core_jni_helpers.h>
-#include <minikin/Hyphenator.h>
-
namespace android {
static std::string buildFileName(const std::string& locale) {
@@ -79,6 +81,23 @@
minikin::addHyphenatorAlias(from, to);
}
+/*
+ * Cache the subtag key map by calling uloc_forLanguageTag with a subtag.
+ * minikin calls uloc_forLanguageTag with an Unicode extension specifying
+ * the line breaking strictness. Parsing the extension requires loading the key map
+ * from keyTypeData.res in the ICU.
+ * "lb" is the key commonly used by minikin. "ca" is a common legacy key mapping to
+ * the "calendar" key. It ensures that the key map is loaded and cached in icu4c.
+ * "en-Latn-US" is a common locale used in the Android system regardless what default locale
+ * is selected in the Settings app.
+ */
+inline static void cacheUnicodeExtensionSubtagsKeyMap() {
+ UErrorCode status = U_ZERO_ERROR;
+ char localeID[ULOC_FULLNAME_CAPACITY] = {};
+ uloc_forLanguageTag("en-Latn-US-u-lb-loose-ca-gregory", localeID, ULOC_FULLNAME_CAPACITY,
+ nullptr, &status);
+}
+
static void init() {
// TODO: Confirm that these are the best values. Various sources suggest (1, 1), but that
// appears too small.
@@ -190,6 +209,10 @@
addHyphenatorAlias("und-Orya", "or"); // Oriya
addHyphenatorAlias("und-Taml", "ta"); // Tamil
addHyphenatorAlias("und-Telu", "te"); // Telugu
+
+ tracing_perfetto::traceBegin(ATRACE_TAG_VIEW, "CacheUnicodeExtensionSubtagsKeyMap");
+ cacheUnicodeExtensionSubtagsKeyMap();
+ tracing_perfetto::traceEnd(ATRACE_TAG_VIEW); // CacheUnicodeExtensionSubtagsKeyMap
}
static const JNINativeMethod gMethods[] = {
diff --git a/core/tests/FileSystemUtilsTest/Android.bp b/core/tests/FileSystemUtilsTest/Android.bp
index 53c22df..ae04aa4 100644
--- a/core/tests/FileSystemUtilsTest/Android.bp
+++ b/core/tests/FileSystemUtilsTest/Android.bp
@@ -69,7 +69,7 @@
"compatibility-host-util",
"compatibility-tradefed",
],
- data: [
+ device_common_data: [
":embedded_native_libs_test_app",
":extract_native_libs_test_app",
],
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 7af3073..b1c48ab 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -256,6 +256,7 @@
"src/android/content/ContextTest.java",
"src/android/content/pm/PackageManagerTest.java",
"src/android/content/pm/UserInfoTest.java",
+ "src/android/app/PropertyInvalidatedCacheTests.java",
"src/android/database/CursorWindowTest.java",
"src/android/os/**/*.java",
"src/android/telephony/PinResultTest.java",
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index cd6abdd..95a4fe4 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -39,11 +39,7 @@
* atest FrameworksCoreTests:PropertyInvalidatedCacheTests
*/
@SmallTest
-@IgnoreUnderRavenwood(blockedBy = PropertyInvalidatedCache.class)
public class PropertyInvalidatedCacheTests {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
// Configuration for creating caches
private static final String MODULE = PropertyInvalidatedCache.MODULE_TEST;
private static final String API = "testApi";
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index b03fd64..5edf0ca 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -37,10 +37,7 @@
* atest FrameworksCoreTests:IpcDataCacheTest
*/
@SmallTest
-@IgnoreUnderRavenwood(blockedBy = IpcDataCache.class)
public class IpcDataCacheTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
// Configuration for creating caches
private static final String MODULE = IpcDataCache.MODULE_TEST;
diff --git a/core/tests/coretests/src/android/service/settings/OWNERS b/core/tests/coretests/src/android/service/settings/OWNERS
new file mode 100644
index 0000000..abd8ab0
--- /dev/null
+++ b/core/tests/coretests/src/android/service/settings/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/service/settings/OWNERS
diff --git a/core/tests/overlaytests/remount/Android.bp b/core/tests/overlaytests/remount/Android.bp
index 0a6b88b..31c1514 100644
--- a/core/tests/overlaytests/remount/Android.bp
+++ b/core/tests/overlaytests/remount/Android.bp
@@ -32,7 +32,7 @@
"frameworks-base-hostutils",
],
test_suites: ["general-tests"],
- java_resources: [
+ device_common_java_resources: [
":com.android.overlaytest.overlaid",
":com.android.overlaytest.overlay",
":OverlayRemountedTest_SharedLibrary",
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index cb3b64c..93d94c9 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import java.lang.annotation.Retention;
@@ -41,6 +42,7 @@
Y8,
Y16,
YCBCR_P010,
+ YCBCR_P210,
NV16,
NV21,
YUY2,
@@ -206,6 +208,26 @@
public static final int YCBCR_P010 = 0x36;
/**
+ * <p>Android YUV P210 format.</p>
+ *
+ * P210 is a 4:2:2 YCbCr semiplanar format comprised of a WxH Y plane
+ * followed by a WxH CbCr plane. Each sample is represented by a 16-bit
+ * little-endian value, with the lower 6 bits set to zero.
+ *
+ * <p>For example, the {@link android.media.Image} object can provide data
+ * in this format from a {@link android.hardware.camera2.CameraDevice}
+ * through a {@link android.media.ImageReader} object if this format is
+ * supported by {@link android.hardware.camera2.CameraDevice}.</p>
+ *
+ * @see android.media.Image
+ * @see android.media.ImageReader
+ * @see android.hardware.camera2.CameraDevice
+ *
+ */
+ @FlaggedApi(android.media.codec.Flags.FLAG_P210_FORMAT_SUPPORT)
+ public static final int YCBCR_P210 = 0x3c;
+
+ /**
* YCbCr format, used for video.
*
* <p>For the {@link android.hardware.camera2} API, the {@link #YUV_420_888} format is
@@ -849,6 +871,8 @@
return 16;
case YCBCR_P010:
return 24;
+ case YCBCR_P210:
+ return 32;
case RAW_DEPTH10:
case RAW10:
return 10;
@@ -899,7 +923,9 @@
case JPEG_R:
return true;
}
-
+ if (android.media.codec.Flags.p210FormatSupport() && format == YCBCR_P210) {
+ return true;
+ }
return false;
}
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 7f936f2..344d19a 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -23,9 +23,6 @@
import android.annotation.SystemApi;
import android.os.Process;
import android.security.keymaster.KeymasterDefs;
-
-import libcore.util.EmptyArray;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.spec.AlgorithmParameterSpec;
@@ -33,6 +30,7 @@
import java.security.spec.MGF1ParameterSpec;
import java.util.Collection;
import java.util.Locale;
+import libcore.util.EmptyArray;
/**
* Properties of <a href="{@docRoot}training/articles/keystore.html">Android Keystore</a> keys.
@@ -116,7 +114,7 @@
public static final int PURPOSE_AGREE_KEY = 1 << 6;
/**
- * Purpose of key: Signing attestaions. This purpose is incompatible with all others, meaning
+ * Purpose of key: Signing attestations. This purpose is incompatible with all others, meaning
* that when generating a key with PURPOSE_ATTEST_KEY, no other purposes may be specified. In
* addition, PURPOSE_ATTEST_KEY may not be specified for imported keys.
*/
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 25d3067..4b0c700 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -79,7 +79,7 @@
path: "src",
}
-genrule {
+java_genrule {
name: "wm_shell_protolog_src",
srcs: [
":protolog-impl",
@@ -99,7 +99,7 @@
out: ["wm_shell_protolog.srcjar"],
}
-genrule {
+java_genrule {
name: "generate-wm_shell_protolog.json",
srcs: [
":wm_shell_protolog-groups",
@@ -116,7 +116,7 @@
out: ["wm_shell_protolog.json"],
}
-genrule {
+java_genrule {
name: "gen-wmshell.protolog.pb",
srcs: [
":wm_shell_protolog-groups",
@@ -133,7 +133,7 @@
out: ["wmshell.protolog.pb"],
}
-genrule {
+java_genrule {
name: "protolog.json.gz",
srcs: [":generate-wm_shell_protolog.json"],
out: ["wmshell.protolog.json.gz"],
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
index 4988a94..851472f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
@@ -25,6 +25,7 @@
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
import android.window.IBackAnimationRunner;
import android.window.IOnBackInvokedCallback;
@@ -32,6 +33,8 @@
import com.android.internal.jank.Cuj.CujType;
import com.android.wm.shell.common.InteractionJankMonitorUtils;
+import java.lang.ref.WeakReference;
+
/**
* Used to register the animation callback and runner, it will trigger result if gesture was finish
* before it received IBackAnimationRunner#onAnimationStart, so the controller could continue
@@ -80,22 +83,49 @@
return mCallback;
}
+ private Runnable mFinishedCallback;
+ private RemoteAnimationTarget[] mApps;
+ private IRemoteAnimationFinishedCallback mRemoteCallback;
+
+ private static class RemoteAnimationFinishedStub extends IRemoteAnimationFinishedCallback.Stub {
+ //the binder callback should not hold strong reference to it to avoid memory leak.
+ private WeakReference<BackAnimationRunner> mRunnerRef;
+
+ private RemoteAnimationFinishedStub(BackAnimationRunner runner) {
+ mRunnerRef = new WeakReference<>(runner);
+ }
+
+ @Override
+ public void onAnimationFinished() {
+ BackAnimationRunner runner = mRunnerRef.get();
+ if (runner == null) {
+ return;
+ }
+ if (runner.shouldMonitorCUJ(runner.mApps)) {
+ InteractionJankMonitorUtils.endTracing(runner.mCujType);
+ }
+
+ runner.mFinishedCallback.run();
+ for (int i = runner.mApps.length - 1; i >= 0; --i) {
+ SurfaceControl sc = runner.mApps[i].leash;
+ if (sc != null && sc.isValid()) {
+ sc.release();
+ }
+ }
+ runner.mApps = null;
+ runner.mFinishedCallback = null;
+ }
+ }
+
/**
* Called from {@link IBackAnimationRunner}, it will deliver these
* {@link RemoteAnimationTarget}s to the corresponding runner.
*/
void startAnimation(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps, Runnable finishedCallback) {
- final IRemoteAnimationFinishedCallback callback =
- new IRemoteAnimationFinishedCallback.Stub() {
- @Override
- public void onAnimationFinished() {
- if (shouldMonitorCUJ(apps)) {
- InteractionJankMonitorUtils.endTracing(mCujType);
- }
- finishedCallback.run();
- }
- };
+ mFinishedCallback = finishedCallback;
+ mApps = apps;
+ if (mRemoteCallback == null) mRemoteCallback = new RemoteAnimationFinishedStub(this);
mWaitingAnimation = false;
if (shouldMonitorCUJ(apps)) {
InteractionJankMonitorUtils.beginTracing(
@@ -103,7 +133,7 @@
}
try {
getRunner().onAnimationStart(TRANSIT_OLD_UNSET, apps, wallpapers,
- nonApps, callback);
+ nonApps, mRemoteCallback);
} catch (RemoteException e) {
Log.w(TAG, "Failed call onAnimationStart", e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS
new file mode 100644
index 0000000..752d2fd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS
@@ -0,0 +1,2 @@
+# WM Shell sub-module dagger owners
[email protected]
\ No newline at end of file
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 77800a3..15ef58e 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -258,6 +258,8 @@
"tests/data/**/*.apk",
"tests/data/**/*.arsc",
"tests/data/**/*.idmap",
+ ],
+ device_common_data: [
":FrameworkResourcesSparseTestApp",
":FrameworkResourcesNotSparseTestApp",
],
diff --git a/libs/hwui/platform/host/android/api-level.h b/libs/hwui/platform/host/android/api-level.h
deleted file mode 120000
index 4fb4784..0000000
--- a/libs/hwui/platform/host/android/api-level.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../bionic/libc/include/android/api-level.h
\ No newline at end of file
diff --git a/media/java/android/media/AudioHalVersionInfo.java b/media/java/android/media/AudioHalVersionInfo.java
index 2b6f72e..3e456a6 100644
--- a/media/java/android/media/AudioHalVersionInfo.java
+++ b/media/java/android/media/AudioHalVersionInfo.java
@@ -84,7 +84,7 @@
* there is a change to supported versions.
*/
public static final @NonNull List<AudioHalVersionInfo> VERSIONS =
- List.of(AIDL_1_0, HIDL_7_1, HIDL_7_0, HIDL_6_0, HIDL_5_0);
+ List.of(AIDL_1_0, HIDL_7_1, HIDL_7_0, HIDL_6_0);
private static final String TAG = "AudioHalVersionInfo";
private AudioHalVersion mHalVersion = new AudioHalVersion();
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 8ff4305..96edd63 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -23,6 +23,7 @@
import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC;
import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE;
import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
+import static android.media.codec.Flags.FLAG_APV_SUPPORT;
import static android.media.MediaCodec.GetFlag;
import android.annotation.FlaggedApi;
@@ -462,6 +463,33 @@
@SuppressLint("AllUpper")
public static final int COLOR_FormatYUVP010 = 54;
+ /**
+ * P210 is 10-bit-per component 4:2:2 YCbCr semiplanar format.
+ * <p>
+ * This format uses 32 allocated bits per pixel with 20 bits of
+ * data per pixel. Chroma planes are subsampled by 2 both
+ * horizontally. Each chroma and luma component
+ * has 16 allocated bits in little-endian configuration with 10
+ * MSB of actual data.
+ *
+ * <pre>
+ * byte byte
+ * <--------- i --------> | <------ i + 1 ------>
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | UNUSED | Y/Cb/Cr |
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * 0 5 6 7 0 7
+ * bit
+ * </pre>
+ *
+ * Use this format with {@link Image}. This format corresponds
+ * to {@link android.graphics.ImageFormat#YCBCR_P210}.
+ * <p>
+ */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(android.media.codec.Flags.FLAG_P210_FORMAT_SUPPORT)
+ public static final int COLOR_FormatYUVP210 = 60;
+
/** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */
public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100;
// COLOR_FormatSurface indicates that the data will be a GraphicBuffer metadata reference.
@@ -4469,6 +4497,265 @@
@SuppressLint("AllUpper")
public static final int AC4Level4 = 0x10;
+ // Profiles and levels/bands for APV Codec, corresponding to the definitions in
+ // "Advanced Professional Video", 10.1.3 Profiles, 10.1.4 Levels and Bands
+ // found at https://www.ietf.org/archive/id/draft-lim-apv-02.html
+
+ /**
+ * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1
+ */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVProfile422_10 = 0x01;
+
+ /**
+ * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1
+ * with HDR10.
+ */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVProfile422_10HDR10 = 0x1000;
+
+ /**
+ * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1
+ * with HDR10Plus.
+ */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVProfile422_10HDR10Plus = 0x2000;
+
+ // For APV Levels, the numerical values are constructed as follows:
+ // ((0x100 << (level_num - 1)) | (1 << band))
+ // where:
+ // - "level_num" is the APV Level numbered consecutively
+ // (i.e., Level 1 == 1, Level 1.1 == 2, etc.)
+ // - "band" is the APV Band
+
+ /** APV Codec Level 1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel1Band0 = 0x101;
+ /** APV Codec Level 1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel1Band1 = 0x102;
+ /** APV Codec Level 1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel1Band2 = 0x104;
+ /** APV Codec Level 1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel1Band3 = 0x108;
+ /** APV Codec Level 1.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel11Band0 = 0x201;
+ /** APV Codec Level 1.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel11Band1 = 0x202;
+ /** APV Codec Level 1.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel11Band2 = 0x204;
+ /** APV Codec Level 1.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel11Band3 = 0x208;
+ /** APV Codec Level 2, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel2Band0 = 0x401;
+ /** APV Codec Level 2, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel2Band1 = 0x402;
+ /** APV Codec Level 2, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel2Band2 = 0x404;
+ /** APV Codec Level 2, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel2Band3 = 0x408;
+ /** APV Codec Level 2.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel21Band0 = 0x801;
+ /** APV Codec Level 2.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel21Band1 = 0x802;
+ /** APV Codec Level 2.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel21Band2 = 0x804;
+ /** APV Codec Level 2.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel21Band3 = 0x808;
+ /** APV Codec Level 3, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel3Band0 = 0x1001;
+ /** APV Codec Level 3, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel3Band1 = 0x1002;
+ /** APV Codec Level 3, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel3Band2 = 0x1004;
+ /** APV Codec Level 3, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel3Band3 = 0x1008;
+ /** APV Codec Level 3.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel31Band0 = 0x2001;
+ /** APV Codec Level 3.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel31Band1 = 0x2002;
+ /** APV Codec Level 3.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel31Band2 = 0x2004;
+ /** APV Codec Level 3.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel31Band3 = 0x2008;
+ /** APV Codec Level 4, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel4Band0 = 0x4001;
+ /** APV Codec Level 4, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel4Band1 = 0x4002;
+ /** APV Codec Level 4, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel4Band2 = 0x4004;
+ /** APV Codec Level 4, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel4Band3 = 0x4008;
+ /** APV Codec Level 4.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel41Band0 = 0x8001;
+ /** APV Codec Level 4.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel41Band1 = 0x8002;
+ /** APV Codec Level 4.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel41Band2 = 0x8004;
+ /** APV Codec Level 4.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel41Band3 = 0x8008;
+ /** APV Codec Level 5, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel5Band0 = 0x10001;
+ /** APV Codec Level 5, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel5Band1 = 0x10002;
+ /** APV Codec Level 5, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel5Band2 = 0x10004;
+ /** APV Codec Level 5, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel5Band3 = 0x10008;
+ /** APV Codec Level 5.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel51Band0 = 0x20001;
+ /** APV Codec Level 5.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel51Band1 = 0x20002;
+ /** APV Codec Level 5.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel51Band2 = 0x20004;
+ /** APV Codec Level 5.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel51Band3 = 0x20008;
+ /** APV Codec Level 6, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel6Band0 = 0x40001;
+ /** APV Codec Level 6, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel6Band1 = 0x40002;
+ /** APV Codec Level 6, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel6Band2 = 0x40004;
+ /** APV Codec Level 6, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel6Band3 = 0x40008;
+ /** APV Codec Level 6.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel61Band0 = 0x80001;
+ /** APV Codec Level 6.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel61Band1 = 0x80002;
+ /** APV Codec Level 6.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel61Band2 = 0x80004;
+ /** APV Codec Level 6.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel61Band3 = 0x80008;
+ /** APV Codec Level 7, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel7Band0 = 0x100001;
+ /** APV Codec Level 7, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel7Band1 = 0x100002;
+ /** APV Codec Level 7, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel7Band2 = 0x100004;
+ /** APV Codec Level 7, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel7Band3 = 0x100008;
+ /** APV Codec Level 7.1, Band 0 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel71Band0 = 0x200001;
+ /** APV Codec Level 7.1, Band 1 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel71Band1 = 0x200002;
+ /** APV Codec Level 7.1, Band 2 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel71Band2 = 0x200004;
+ /** APV Codec Level 7.1, Band 3 as per IETF lim-apv-02, 10.1.4 */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final int APVLevel71Band3 = 0x200008;
+
/**
* The profile of the media content. Depending on the type of media this can be
* one of the profile values defined in this class.
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index cd0654c..b08a86e 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -18,6 +18,7 @@
import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC;
import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
+import static android.media.codec.Flags.FLAG_APV_SUPPORT;
import static com.android.media.codec.flags.Flags.FLAG_CODEC_IMPORTANCE;
import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME;
@@ -157,6 +158,8 @@
public final class MediaFormat {
public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+ @FlaggedApi(FLAG_APV_SUPPORT)
+ public static final String MIMETYPE_VIDEO_APV = "video/apv";
public static final String MIMETYPE_VIDEO_AV1 = "video/av01";
public static final String MIMETYPE_VIDEO_AVC = "video/avc";
public static final String MIMETYPE_VIDEO_HEVC = "video/hevc";
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 5e55f64..678150b 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -584,45 +584,108 @@
* The following table summarizes codec support for containers across android releases:
*
* <table>
- * <thead>
- * <tr>
- * <th rowspan=2>OS Version(s)</th>
- * <td colspan=3>Codec support</th>
- * </tr><tr>
- * <th>{@linkplain OutputFormat#MUXER_OUTPUT_MPEG_4 MP4}</th>
- * <th>{@linkplain OutputFormat#MUXER_OUTPUT_WEBM WEBM}</th>
- * </tr>
- * </thead>
- * <tbody>
- * <tr>
- * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td>
- * <td rowspan=6>{@link MediaFormat#MIMETYPE_AUDIO_AAC AAC},<br>
- * {@link MediaFormat#MIMETYPE_AUDIO_AMR_NB NB-AMR},<br>
- * {@link MediaFormat#MIMETYPE_AUDIO_AMR_WB WB-AMR},<br>
- * {@link MediaFormat#MIMETYPE_VIDEO_H263 H.263},<br>
- * {@link MediaFormat#MIMETYPE_VIDEO_MPEG4 MPEG-4},<br>
- * {@link MediaFormat#MIMETYPE_VIDEO_AVC AVC} (H.264)</td>
- * <td rowspan=3>Not supported</td>
- * </tr><tr>
- * <td>{@link android.os.Build.VERSION_CODES#KITKAT}</td>
- * </tr><tr>
- * <td>{@link android.os.Build.VERSION_CODES#KITKAT_WATCH}</td>
- * </tr><tr>
- * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td>
- * <td rowspan=3>{@link MediaFormat#MIMETYPE_AUDIO_VORBIS Vorbis},<br>
- * {@link MediaFormat#MIMETYPE_VIDEO_VP8 VP8}</td>
- * </tr><tr>
- * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td>
- * </tr><tr>
- * <td>{@link android.os.Build.VERSION_CODES#M}</td>
- * </tr><tr>
- * <td>{@link android.os.Build.VERSION_CODES#N}</td>
- * <td>as above, plus<br>
- * {@link MediaFormat#MIMETYPE_VIDEO_HEVC HEVC} (H.265)</td>
- * <td>as above, plus<br>
- * {@link MediaFormat#MIMETYPE_VIDEO_VP9 VP9}</td>
- * </tr>
- * </tbody>
+ * <thead>
+ * <tr>
+ * <th>Codec</th>
+ * <th>{@linkplain OutputFormat#MUXER_OUTPUT_MPEG_4 MP4}</th>
+ * <th>{@linkplain OutputFormat#MUXER_OUTPUT_WEBM WEBM}</th>
+ * <th>{@linkplain OutputFormat#MUXER_OUTPUT_OGG OGG}</th>
+ * <th>Supported From SDK version</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_AUDIO_AAC AAC}</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>17</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_AUDIO_AMR_NB NB-AMR}</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>17</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_AUDIO_AMR_WB WB-AMR}</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>17</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_VIDEO_H263 H.263}</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>17</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_VIDEO_MPEG4 MPEG-4}</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>17</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_VIDEO_AVC AVC} (H.264)</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>17</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_AUDIO_VORBIS Vorbis}</td>
+ * <td></td>
+ * <td>✓</td>
+ * <td></td>
+ * <td>21</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_VIDEO_VP8 VP8}</td>
+ * <td></td>
+ * <td>✓</td>
+ * <td></td>
+ * <td>21</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_VIDEO_VP9 VP9}</td>
+ * <td></td>
+ * <td>✓</td>
+ * <td></td>
+ * <td>24</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_VIDEO_HEVC HEVC} (H.265)</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>24</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_AUDIO_OPUS OPUS}</td>
+ * <td></td>
+ * <td>✓</td>
+ * <td>✓</td>
+ * <td>26</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_VIDEO_AV1 AV1}</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>31</td>
+ * </tr>
+ * <tr>
+ * <td>{@link MediaFormat#MIMETYPE_VIDEO_DOLBY_VISION Dolby Vision}</td>
+ * <td>✓</td>
+ * <td></td>
+ * <td></td>
+ * <td>32</td>
+ * </tr>
+ * </tbody>
* </table>
*
* @param format The media format for the track. This must not be an empty
diff --git a/media/java/android/media/audiofx/Equalizer.java b/media/java/android/media/audiofx/Equalizer.java
index 7abada0..0986345 100644
--- a/media/java/android/media/audiofx/Equalizer.java
+++ b/media/java/android/media/audiofx/Equalizer.java
@@ -153,9 +153,8 @@
param[0] = PARAM_GET_PRESET_NAME;
for (int i = 0; i < mNumPresets; i++) {
param[1] = i;
- checkStatus(getParameter(param, value));
- int length = 0;
- while (value[length] != 0) length++;
+ final int length = getParameter(param, value);
+ checkStatus(length);
try {
mPresetNames[i] = new String(value, 0, length, "ISO-8859-1");
} catch (java.io.UnsupportedEncodingException e) {
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 371e3d2..b0c280b 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -935,6 +935,11 @@
return static_cast<jint>(PublicFormat::PRIVATE);
} else {
BufferItem* buffer = Image_getBufferItem(env, thiz);
+ if (buffer == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Image is not initialized");
+ return -1;
+ }
int readerHalFormat = mapPublicFormatToHalFormat(static_cast<PublicFormat>(readerFormat));
int32_t fmt = applyFormatOverrides(
buffer->mGraphicBuffer->getPixelFormat(), readerHalFormat);
@@ -953,6 +958,11 @@
static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
BufferItem* buffer = Image_getBufferItem(env, thiz);
+ if (buffer == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Image is not initialized");
+ return NULL;
+ }
AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer->mGraphicBuffer.get());
// don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us
// to link against libandroid.so
diff --git a/mime/Android.bp b/mime/Android.bp
index 757862b..20110f1 100644
--- a/mime/Android.bp
+++ b/mime/Android.bp
@@ -92,7 +92,7 @@
visibility: [
"//visibility:private",
],
- srcs: [
+ device_common_srcs: [
":debian.mime.types.minimized",
":android.mime.types.minimized",
":vendor.mime.types.minimized",
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 905d6f6..474ff8c 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -82,6 +82,13 @@
private boolean mRfDiscoveryStarted = false;
/**
+ * Broadcast Action: Sent on NFC stack initialization when NFC OEM extensions are enabled.
+ * <p> OEM extension modules should use this intent to start their extension service </p>
+ * @hide
+ */
+ public static final String ACTION_OEM_EXTENSION_INIT = "android.nfc.action.OEM_EXTENSION_INIT";
+
+ /**
* Mode Type for {@link #setControllerAlwaysOnMode(int)}.
* Enables the controller in default mode when NFC is disabled (existing API behavior).
* works same as {@link NfcAdapter#setControllerAlwaysOn(boolean)}.
@@ -923,12 +930,15 @@
}
private @CardEmulation.ProtocolAndTechnologyRoute int routeStringToInt(String route) {
- return switch (route) {
- case "DH" -> PROTOCOL_AND_TECHNOLOGY_ROUTE_DH;
- case "eSE" -> PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE;
- case "SIM" -> PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC;
- default -> throw new IllegalStateException("Unexpected value: " + route);
- };
+ if (route.equals("DH")) {
+ return PROTOCOL_AND_TECHNOLOGY_ROUTE_DH;
+ } else if (route.startsWith("eSE")) {
+ return PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE;
+ } else if (route.startsWith("SIM")) {
+ return PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC;
+ } else {
+ throw new IllegalStateException("Unexpected value: " + route);
+ }
}
private class ReceiverWrapper<T> implements Consumer<T> {
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 6a7e693..34f0200 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -165,3 +165,11 @@
description: "Enabling security log for nfc state change"
bug: "319934052"
}
+
+flag {
+ name: "nfc_associated_role_services"
+ is_exported: true
+ namespace: "nfc"
+ description: "Share wallet role routing priority with associated services"
+ bug: "366243361"
+}
diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp
index 5b3d47e..bd89263 100644
--- a/packages/CtsShim/build/Android.bp
+++ b/packages/CtsShim/build/Android.bp
@@ -55,7 +55,7 @@
],
}
-genrule {
+java_genrule {
name: "generate_priv_manifest",
srcs: [
"shim_priv/AndroidManifest.xml",
@@ -169,7 +169,7 @@
min_sdk_version: "24",
}
-genrule {
+java_genrule {
name: "generate_shim_manifest",
srcs: [
"shim/AndroidManifest.xml",
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
index 121f549..3696000 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -41,6 +41,7 @@
"/system_ext/etc/NOTICE.xml.gz",
"/vendor_dlkm/etc/NOTICE.xml.gz",
"/odm_dlkm/etc/NOTICE.xml.gz",
+ "/system_dlkm/etc/NOTICE.xml.gz",
};
static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
diff --git a/packages/SystemUI/animation/lib/OWNERS b/packages/SystemUI/animation/lib/OWNERS
new file mode 100644
index 0000000..7569419
--- /dev/null
+++ b/packages/SystemUI/animation/lib/OWNERS
@@ -0,0 +1,3 @@
+#inherits OWNERS from SystemUI in addition to WEAR framework owners below
+file:platform/frameworks/base:/WEAR_OWNERS
+
diff --git a/packages/SystemUI/checks/Android.bp b/packages/SystemUI/checks/Android.bp
index 04ac748..1ec22018 100644
--- a/packages/SystemUI/checks/Android.bp
+++ b/packages/SystemUI/checks/Android.bp
@@ -40,9 +40,11 @@
data: [
":androidx.annotation_annotation",
":dagger2",
- ":framework",
":kotlinx-coroutines-core",
],
+ device_common_data: [
+ ":framework",
+ ],
static_libs: [
"SystemUILintChecker",
],
diff --git a/packages/Vcn/TEST_MAPPING b/packages/Vcn/TEST_MAPPING
new file mode 100644
index 0000000..bde88fe
--- /dev/null
+++ b/packages/Vcn/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksVcnTests"
+ },
+ {
+ "name": "CtsVcnTestCases"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index ff2abd2..b3f78ab 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -170,7 +170,7 @@
"hoststubgen-helper-runtime.ravenwood",
"mockito-ravenwood-prebuilt",
],
- visibility: ["//frameworks/base"],
+ visibility: [":__subpackages__"],
jarjar_rules: ":ravenwood-services-jarjar-rules",
}
@@ -310,7 +310,7 @@
name: "ravenwood-stats-checker",
src: "scripts/ravenwood-stats-checker.sh",
test_suites: ["general-tests"],
- data: [
+ device_common_data: [
":framework-minus-apex.ravenwood-base_all{hoststubgen_framework-minus-apex_stats.csv}",
":framework-minus-apex.ravenwood-base_all{hoststubgen_framework-minus-apex_apis.csv}",
":framework-minus-apex.ravenwood-base_all{hoststubgen_framework-minus-apex_keep_all.txt}",
@@ -319,6 +319,16 @@
":services.core.ravenwood-base{hoststubgen_services.core_apis.csv}",
":services.core.ravenwood-base{hoststubgen_services.core_keep_all.txt}",
":services.core.ravenwood-base{hoststubgen_services.core_dump.txt}",
+
+ ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_stats.csv}",
+ ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_apis.csv}",
+ ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_keep_all.txt}",
+ ":framework-configinfrastructure.ravenwood-base{framework-configinfrastructure_dump.txt}",
+
+ ":framework-statsd.ravenwood-base{framework-statsd_stats.csv}",
+ ":framework-statsd.ravenwood-base{framework-statsd_apis.csv}",
+ ":framework-statsd.ravenwood-base{framework-statsd_keep_all.txt}",
+ ":framework-statsd.ravenwood-base{framework-statsd_dump.txt}",
],
}
@@ -350,6 +360,239 @@
],
}
+filegroup {
+ name: "ravenwood-data",
+ device_common_srcs: [
+ ":system-build.prop",
+ ":framework-res",
+ ":ravenwood-empty-res",
+ ":framework-platform-compat-config",
+ ":services-platform-compat-config",
+ ],
+ device_first_srcs: [
+ ":apex_icu.dat",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+// Keep in sync with build/make/target/product/generic/Android.bp
+filegroup {
+ name: "ravenwood-fonts",
+ device_common_srcs: [
+ ":AndroidClock.ttf",
+ ":CarroisGothicSC-Regular.ttf",
+ ":ComingSoon.ttf",
+ ":CutiveMono.ttf",
+ ":DancingScript-Regular.ttf",
+ ":DroidSansMono.ttf",
+ ":NotoColorEmoji.ttf",
+ ":NotoColorEmojiFlags.ttf",
+ ":NotoNaskhArabic-Bold.ttf",
+ ":NotoNaskhArabic-Regular.ttf",
+ ":NotoNaskhArabicUI-Bold.ttf",
+ ":NotoNaskhArabicUI-Regular.ttf",
+ ":NotoSansAdlam-VF.ttf",
+ ":NotoSansAhom-Regular.otf",
+ ":NotoSansAnatolianHieroglyphs-Regular.otf",
+ ":NotoSansArmenian-VF.ttf",
+ ":NotoSansAvestan-Regular.ttf",
+ ":NotoSansBalinese-Regular.ttf",
+ ":NotoSansBamum-Regular.ttf",
+ ":NotoSansBassaVah-Regular.otf",
+ ":NotoSansBatak-Regular.ttf",
+ ":NotoSansBengali-VF.ttf",
+ ":NotoSansBengaliUI-VF.ttf",
+ ":NotoSansBhaiksuki-Regular.otf",
+ ":NotoSansBrahmi-Regular.ttf",
+ ":NotoSansBuginese-Regular.ttf",
+ ":NotoSansBuhid-Regular.ttf",
+ ":NotoSansCJK-Regular.ttc",
+ ":NotoSansCanadianAboriginal-Regular.ttf",
+ ":NotoSansCarian-Regular.ttf",
+ ":NotoSansChakma-Regular.otf",
+ ":NotoSansCham-Bold.ttf",
+ ":NotoSansCham-Regular.ttf",
+ ":NotoSansCherokee-Regular.ttf",
+ ":NotoSansCoptic-Regular.ttf",
+ ":NotoSansCuneiform-Regular.ttf",
+ ":NotoSansCypriot-Regular.ttf",
+ ":NotoSansDeseret-Regular.ttf",
+ ":NotoSansDevanagari-VF.ttf",
+ ":NotoSansDevanagariUI-VF.ttf",
+ ":NotoSansEgyptianHieroglyphs-Regular.ttf",
+ ":NotoSansElbasan-Regular.otf",
+ ":NotoSansEthiopic-VF.ttf",
+ ":NotoSansGeorgian-VF.ttf",
+ ":NotoSansGlagolitic-Regular.ttf",
+ ":NotoSansGothic-Regular.ttf",
+ ":NotoSansGrantha-Regular.ttf",
+ ":NotoSansGujarati-Bold.ttf",
+ ":NotoSansGujarati-Regular.ttf",
+ ":NotoSansGujaratiUI-Bold.ttf",
+ ":NotoSansGujaratiUI-Regular.ttf",
+ ":NotoSansGunjalaGondi-Regular.otf",
+ ":NotoSansGurmukhi-VF.ttf",
+ ":NotoSansGurmukhiUI-VF.ttf",
+ ":NotoSansHanifiRohingya-Regular.otf",
+ ":NotoSansHanunoo-Regular.ttf",
+ ":NotoSansHatran-Regular.otf",
+ ":NotoSansHebrew-Bold.ttf",
+ ":NotoSansHebrew-Regular.ttf",
+ ":NotoSansImperialAramaic-Regular.ttf",
+ ":NotoSansInscriptionalPahlavi-Regular.ttf",
+ ":NotoSansInscriptionalParthian-Regular.ttf",
+ ":NotoSansJavanese-Regular.otf",
+ ":NotoSansKaithi-Regular.ttf",
+ ":NotoSansKannada-VF.ttf",
+ ":NotoSansKannadaUI-VF.ttf",
+ ":NotoSansKayahLi-Regular.ttf",
+ ":NotoSansKharoshthi-Regular.ttf",
+ ":NotoSansKhmer-VF.ttf",
+ ":NotoSansKhmerUI-Bold.ttf",
+ ":NotoSansKhmerUI-Regular.ttf",
+ ":NotoSansKhojki-Regular.otf",
+ ":NotoSansLao-Bold.ttf",
+ ":NotoSansLao-Regular.ttf",
+ ":NotoSansLaoUI-Bold.ttf",
+ ":NotoSansLaoUI-Regular.ttf",
+ ":NotoSansLepcha-Regular.ttf",
+ ":NotoSansLimbu-Regular.ttf",
+ ":NotoSansLinearA-Regular.otf",
+ ":NotoSansLinearB-Regular.ttf",
+ ":NotoSansLisu-Regular.ttf",
+ ":NotoSansLycian-Regular.ttf",
+ ":NotoSansLydian-Regular.ttf",
+ ":NotoSansMalayalam-VF.ttf",
+ ":NotoSansMalayalamUI-VF.ttf",
+ ":NotoSansMandaic-Regular.ttf",
+ ":NotoSansManichaean-Regular.otf",
+ ":NotoSansMarchen-Regular.otf",
+ ":NotoSansMasaramGondi-Regular.otf",
+ ":NotoSansMedefaidrin-VF.ttf",
+ ":NotoSansMeeteiMayek-Regular.ttf",
+ ":NotoSansMeroitic-Regular.otf",
+ ":NotoSansMiao-Regular.otf",
+ ":NotoSansModi-Regular.ttf",
+ ":NotoSansMongolian-Regular.ttf",
+ ":NotoSansMro-Regular.otf",
+ ":NotoSansMultani-Regular.otf",
+ ":NotoSansMyanmar-Bold.otf",
+ ":NotoSansMyanmar-Medium.otf",
+ ":NotoSansMyanmar-Regular.otf",
+ ":NotoSansMyanmarUI-Bold.otf",
+ ":NotoSansMyanmarUI-Medium.otf",
+ ":NotoSansMyanmarUI-Regular.otf",
+ ":NotoSansNKo-Regular.ttf",
+ ":NotoSansNabataean-Regular.otf",
+ ":NotoSansNewTaiLue-Regular.ttf",
+ ":NotoSansNewa-Regular.otf",
+ ":NotoSansOgham-Regular.ttf",
+ ":NotoSansOlChiki-Regular.ttf",
+ ":NotoSansOldItalic-Regular.ttf",
+ ":NotoSansOldNorthArabian-Regular.otf",
+ ":NotoSansOldPermic-Regular.otf",
+ ":NotoSansOldPersian-Regular.ttf",
+ ":NotoSansOldSouthArabian-Regular.ttf",
+ ":NotoSansOldTurkic-Regular.ttf",
+ ":NotoSansOriya-Bold.ttf",
+ ":NotoSansOriya-Regular.ttf",
+ ":NotoSansOriyaUI-Bold.ttf",
+ ":NotoSansOriyaUI-Regular.ttf",
+ ":NotoSansOsage-Regular.ttf",
+ ":NotoSansOsmanya-Regular.ttf",
+ ":NotoSansPahawhHmong-Regular.otf",
+ ":NotoSansPalmyrene-Regular.otf",
+ ":NotoSansPauCinHau-Regular.otf",
+ ":NotoSansPhagsPa-Regular.ttf",
+ ":NotoSansPhoenician-Regular.ttf",
+ ":NotoSansRejang-Regular.ttf",
+ ":NotoSansRunic-Regular.ttf",
+ ":NotoSansSamaritan-Regular.ttf",
+ ":NotoSansSaurashtra-Regular.ttf",
+ ":NotoSansSharada-Regular.otf",
+ ":NotoSansShavian-Regular.ttf",
+ ":NotoSansSinhala-VF.ttf",
+ ":NotoSansSinhalaUI-VF.ttf",
+ ":NotoSansSoraSompeng-Regular.otf",
+ ":NotoSansSoyombo-VF.ttf",
+ ":NotoSansSundanese-Regular.ttf",
+ ":NotoSansSylotiNagri-Regular.ttf",
+ ":NotoSansSymbols-Regular-Subsetted.ttf",
+ ":NotoSansSymbols-Regular-Subsetted2.ttf",
+ ":NotoSansSyriacEastern-Regular.ttf",
+ ":NotoSansSyriacEstrangela-Regular.ttf",
+ ":NotoSansSyriacWestern-Regular.ttf",
+ ":NotoSansTagalog-Regular.ttf",
+ ":NotoSansTagbanwa-Regular.ttf",
+ ":NotoSansTaiLe-Regular.ttf",
+ ":NotoSansTaiTham-Regular.ttf",
+ ":NotoSansTaiViet-Regular.ttf",
+ ":NotoSansTakri-VF.ttf",
+ ":NotoSansTamil-VF.ttf",
+ ":NotoSansTamilUI-VF.ttf",
+ ":NotoSansTelugu-VF.ttf",
+ ":NotoSansTeluguUI-VF.ttf",
+ ":NotoSansThaana-Bold.ttf",
+ ":NotoSansThaana-Regular.ttf",
+ ":NotoSansThai-Bold.ttf",
+ ":NotoSansThai-Regular.ttf",
+ ":NotoSansThaiUI-Bold.ttf",
+ ":NotoSansThaiUI-Regular.ttf",
+ ":NotoSansTifinagh-Regular.otf",
+ ":NotoSansUgaritic-Regular.ttf",
+ ":NotoSansVai-Regular.ttf",
+ ":NotoSansWancho-Regular.otf",
+ ":NotoSansWarangCiti-Regular.otf",
+ ":NotoSansYi-Regular.ttf",
+ ":NotoSerif-Bold.ttf",
+ ":NotoSerif-BoldItalic.ttf",
+ ":NotoSerif-Italic.ttf",
+ ":NotoSerif-Regular.ttf",
+ ":NotoSerifArmenian-VF.ttf",
+ ":NotoSerifBengali-VF.ttf",
+ ":NotoSerifCJK-Regular.ttc",
+ ":NotoSerifDevanagari-VF.ttf",
+ ":NotoSerifDogra-Regular.ttf",
+ ":NotoSerifEthiopic-VF.ttf",
+ ":NotoSerifGeorgian-VF.ttf",
+ ":NotoSerifGujarati-VF.ttf",
+ ":NotoSerifGurmukhi-VF.ttf",
+ ":NotoSerifHebrew-Bold.ttf",
+ ":NotoSerifHebrew-Regular.ttf",
+ ":NotoSerifHentaigana.ttf",
+ ":NotoSerifKannada-VF.ttf",
+ ":NotoSerifKhmer-Bold.otf",
+ ":NotoSerifKhmer-Regular.otf",
+ ":NotoSerifLao-Bold.ttf",
+ ":NotoSerifLao-Regular.ttf",
+ ":NotoSerifMalayalam-VF.ttf",
+ ":NotoSerifMyanmar-Bold.otf",
+ ":NotoSerifMyanmar-Regular.otf",
+ ":NotoSerifNyiakengPuachueHmong-VF.ttf",
+ ":NotoSerifSinhala-VF.ttf",
+ ":NotoSerifTamil-VF.ttf",
+ ":NotoSerifTelugu-VF.ttf",
+ ":NotoSerifThai-Bold.ttf",
+ ":NotoSerifThai-Regular.ttf",
+ ":NotoSerifTibetan-VF.ttf",
+ ":NotoSerifYezidi-VF.ttf",
+ ":Roboto-Regular.ttf",
+ ":RobotoFlex-Regular.ttf",
+ ":RobotoStatic-Regular.ttf",
+ ":SourceSansPro-Bold.ttf",
+ ":SourceSansPro-BoldItalic.ttf",
+ ":SourceSansPro-Italic.ttf",
+ ":SourceSansPro-Regular.ttf",
+ ":SourceSansPro-SemiBold.ttf",
+ ":SourceSansPro-SemiBoldItalic.ttf",
+ ],
+ device_first_srcs: [
+ ":font_fallback.xml",
+ ":fonts.xml",
+ ],
+ visibility: ["//visibility:private"],
+}
+
// JARs in "ravenwood-runtime" are set to the classpath, sorted alphabetically.
// Rename some of the dependencies to make sure they're included in the intended order.
@@ -376,13 +619,8 @@
android_ravenwood_libgroup {
name: "ravenwood-runtime",
- data: [
- ":system-build.prop",
- ":framework-res",
- ":ravenwood-empty-res",
- ":framework-platform-compat-config",
- ":services-platform-compat-config",
- ],
+ data: [":ravenwood-data"],
+ fonts: [":ravenwood-fonts"],
libs: [
"100-framework-minus-apex.ravenwood",
"200-kxml2-android",
@@ -403,6 +641,9 @@
// DeviceConfig
"framework-configinfrastructure.ravenwood",
+ // StatsD
+ "framework-statsd.ravenwood",
+
// Provide runtime versions of utils linked in below
"junit",
"truth",
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index 1bea434..d207738 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -344,3 +344,57 @@
"framework-configinfrastructure.ravenwood.jar",
],
}
+
+///////////////////////////////////
+// framework-statsd
+///////////////////////////////////
+
+java_genrule {
+ name: "framework-statsd.ravenwood-base",
+ tools: ["hoststubgen"],
+ cmd: "$(location hoststubgen) " +
+ "@$(location :ravenwood-standard-options) " +
+
+ "--debug-log $(location framework-statsd.log) " +
+ "--stats-file $(location framework-statsd_stats.csv) " +
+ "--supported-api-list-file $(location framework-statsd_apis.csv) " +
+ "--gen-keep-all-file $(location framework-statsd_keep_all.txt) " +
+ "--gen-input-dump-file $(location framework-statsd_dump.txt) " +
+
+ "--out-impl-jar $(location ravenwood.jar) " +
+ "--in-jar $(location :framework-statsd.impl{.jar}) " +
+
+ "--policy-override-file $(location :ravenwood-common-policies) " +
+ "--policy-override-file $(location :framework-statsd-ravenwood-policies) ",
+ srcs: [
+ ":framework-statsd.impl{.jar}",
+
+ ":ravenwood-common-policies",
+ ":framework-statsd-ravenwood-policies",
+ ":ravenwood-standard-options",
+ ],
+ out: [
+ "ravenwood.jar",
+
+ // Following files are created just as FYI.
+ "framework-statsd_keep_all.txt",
+ "framework-statsd_dump.txt",
+
+ "framework-statsd.log",
+ "framework-statsd_stats.csv",
+ "framework-statsd_apis.csv",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_genrule {
+ name: "framework-statsd.ravenwood",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":framework-statsd.ravenwood-base{ravenwood.jar}",
+ ],
+ out: [
+ "framework-statsd.ravenwood.jar",
+ ],
+}
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 72f62c5..a1243e3 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -5,8 +5,8 @@
{ "name": "hoststubgen-test-tiny-test" },
{ "name": "hoststubgen-invoke-test" },
{ "name": "RavenwoodMockitoTest_device" },
- // TODO(b/371215487): Re-enable when the test is fixed.
- // { "name": "RavenwoodBivalentTest_device" },
+ { "name": "RavenwoodBivalentTest_device" },
+ { "name": "RavenwoodBivalentTest_device_ravenizer" },
{ "name": "RavenwoodBivalentInstTest_nonself_inst" },
{ "name": "RavenwoodBivalentInstTest_self_inst_device" },
@@ -35,18 +35,18 @@
},
{
"name": "CarLibHostUnitTest",
- "host": true,
- "keywords": ["automotive_code_coverage"]
+ "keywords": ["automotive_code_coverage"],
+ "host": true
},
{
"name": "CarServiceHostUnitTest",
- "host": true,
- "keywords": ["automotive_code_coverage"]
+ "keywords": ["automotive_code_coverage"],
+ "host": true
},
{
"name": "CarSystemUIRavenTests",
- "host": true,
- "keywords": ["automotive_code_coverage"]
+ "keywords": ["automotive_code_coverage"],
+ "host": true
},
{
"name": "CtsAccountManagerTestCasesRavenwood",
@@ -65,6 +65,10 @@
"host": true
},
{
+ "name": "CtsDeviceConfigTestCasesRavenwood",
+ "host": true
+ },
+ {
"name": "CtsGraphicsTestCasesRavenwood",
"host": true
},
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
new file mode 100644
index 0000000..66a6890
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.ravenwood;
+
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicVoidMethod;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
+import android.platform.test.annotations.internal.InnerRunner;
+import android.platform.test.ravenwood.RavenwoodTestStats.Result;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.Filterable;
+import org.junit.runner.manipulation.NoTestsRemainException;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.Suite;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.util.function.BiConsumer;
+
+/**
+ * A test runner used for Ravenwood.
+ *
+ * It will delegate to another runner specified with {@link InnerRunner}
+ * (default = {@link BlockJUnit4ClassRunner}) with the following features.
+ * - Add a called before the inner runner gets a chance to run. This can be used to initialize
+ * stuff used by the inner runner.
+ * - Add hook points with help from the four test rules such as {@link #sImplicitClassOuterRule},
+ * which are also injected by the ravenizer tool.
+ *
+ * We use this runner to:
+ * - Initialize the Ravenwood environment.
+ * - Handle {@link android.platform.test.annotations.DisabledOnRavenwood}.
+ */
+public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase {
+ public static final String TAG = "Ravenwood";
+
+ /** Scope of a hook. */
+ public enum Scope {
+ Class,
+ Instance,
+ }
+
+ /** Order of a hook. */
+ public enum Order {
+ Outer,
+ Inner,
+ }
+
+ private record HookRule(Scope scope, Order order) implements TestRule {
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return getCurrentRunner().wrapWithHooks(base, description, scope, order);
+ }
+ }
+
+ // The following four rule instances will be injected to tests by the Ravenizer tool.
+ public static final TestRule sImplicitClassOuterRule = new HookRule(Scope.Class, Order.Outer);
+ public static final TestRule sImplicitClassInnerRule = new HookRule(Scope.Class, Order.Inner);
+ public static final TestRule sImplicitInstOuterRule = new HookRule(Scope.Instance, Order.Outer);
+ public static final TestRule sImplicitInstInnerRule = new HookRule(Scope.Instance, Order.Inner);
+
+ /** Keeps track of the runner on the current thread. */
+ private static final ThreadLocal<RavenwoodAwareTestRunner> sCurrentRunner = new ThreadLocal<>();
+
+ private static RavenwoodAwareTestRunner getCurrentRunner() {
+ var runner = sCurrentRunner.get();
+ if (runner == null) {
+ throw new RuntimeException("Current test runner not set!");
+ }
+ return runner;
+ }
+
+ final Class<?> mTestJavaClass;
+ private final Runner mRealRunner;
+ private TestClass mTestClass = null;
+
+ /**
+ * Stores internal states / methods associated with this runner that's only needed in
+ * junit-impl.
+ */
+ final RavenwoodRunnerState mState = new RavenwoodRunnerState(this);
+
+ /**
+ * Constructor.
+ */
+ public RavenwoodAwareTestRunner(Class<?> testClass) {
+ RavenwoodRuntimeEnvironmentController.globalInitOnce();
+ mTestJavaClass = testClass;
+
+ /*
+ * If the class has @DisabledOnRavenwood, then we'll delegate to
+ * ClassSkippingTestRunner, which simply skips it.
+ *
+ * We need to do it before instantiating TestClass for b/367694651.
+ */
+ if (!RavenwoodEnablementChecker.shouldRunClassOnRavenwood(testClass, true)) {
+ mRealRunner = new ClassSkippingTestRunner(testClass);
+ return;
+ }
+
+ mTestClass = new TestClass(testClass);
+
+ Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
+
+ // This is needed to make AndroidJUnit4ClassRunner happy.
+ InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
+
+ // Hook point to allow more customization.
+ runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
+
+ mRealRunner = instantiateRealRunner(mTestClass);
+
+ mState.enterTestRunner();
+ }
+
+ @Override
+ Runner getRealRunner() {
+ return mRealRunner;
+ }
+
+ private void runAnnotatedMethodsOnRavenwood(Class<? extends Annotation> annotationClass,
+ Object instance) {
+ Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
+
+ for (var method : mTestClass.getAnnotatedMethods(annotationClass)) {
+ ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null);
+
+ var methodDesc = method.getDeclaringClass().getName() + "."
+ + method.getMethod().toString();
+ try {
+ method.getMethod().invoke(instance);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw logAndFail("Caught exception while running method " + methodDesc, e);
+ }
+ }
+ }
+
+ @Override
+ public void run(RunNotifier realNotifier) {
+ final var notifier = new RavenwoodRunNotifier(realNotifier);
+ final var description = getDescription();
+
+ if (mRealRunner instanceof ClassSkippingTestRunner) {
+ mRealRunner.run(notifier);
+ Log.i(TAG, "onClassSkipped: description=" + description);
+ RavenwoodTestStats.getInstance().onClassSkipped(description);
+ return;
+ }
+
+ Log.v(TAG, "Starting " + mTestJavaClass.getCanonicalName());
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ dumpDescription(description);
+ }
+
+ // TODO(b/365976974): handle nested classes better
+ final boolean skipRunnerHook =
+ mRealRunnerTakesRunnerBuilder && mRealRunner instanceof Suite;
+
+ sCurrentRunner.set(this);
+ try {
+ if (!skipRunnerHook) {
+ try {
+ mState.enterTestClass();
+ } catch (Throwable th) {
+ notifier.reportBeforeTestFailure(description, th);
+ return;
+ }
+ }
+
+ // Delegate to the inner runner.
+ mRealRunner.run(notifier);
+ } finally {
+ sCurrentRunner.remove();
+
+ if (!skipRunnerHook) {
+ try {
+ RavenwoodTestStats.getInstance().onClassFinished(description);
+ mState.exitTestClass();
+ } catch (Throwable th) {
+ notifier.reportAfterTestFailure(th);
+ }
+ }
+ }
+ }
+
+ private Statement wrapWithHooks(Statement base, Description description, Scope scope,
+ Order order) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ runWithHooks(description, scope, order, base);
+ }
+ };
+ }
+
+ private void runWithHooks(Description description, Scope scope, Order order, Statement s)
+ throws Throwable {
+ assumeTrue(onBefore(description, scope, order));
+ try {
+ s.evaluate();
+ onAfter(description, scope, order, null);
+ } catch (Throwable t) {
+ if (onAfter(description, scope, order, t)) {
+ throw t;
+ }
+ }
+ }
+
+ /**
+ * A runner that simply skips a class. It still has to support {@link Filterable}
+ * because otherwise the result still says "SKIPPED" even when it's not included in the
+ * filter.
+ */
+ private static class ClassSkippingTestRunner extends Runner implements Filterable {
+ private final Description mDescription;
+ private boolean mFilteredOut;
+
+ ClassSkippingTestRunner(Class<?> testClass) {
+ mDescription = Description.createTestDescription(testClass, testClass.getSimpleName());
+ mFilteredOut = false;
+ }
+
+ @Override
+ public Description getDescription() {
+ return mDescription;
+ }
+
+ @Override
+ public void run(RunNotifier notifier) {
+ if (mFilteredOut) {
+ return;
+ }
+ notifier.fireTestSuiteStarted(mDescription);
+ notifier.fireTestIgnored(mDescription);
+ notifier.fireTestSuiteFinished(mDescription);
+ }
+
+ @Override
+ public void filter(Filter filter) throws NoTestsRemainException {
+ if (filter.shouldRun(mDescription)) {
+ mFilteredOut = false;
+ } else {
+ throw new NoTestsRemainException();
+ }
+ }
+ }
+
+ /**
+ * Called before a test / class.
+ *
+ * Return false if it should be skipped.
+ */
+ private boolean onBefore(Description description, Scope scope, Order order) {
+ Log.v(TAG, "onBefore: description=" + description + ", " + scope + ", " + order);
+
+ if (scope == Scope.Instance && order == Order.Outer) {
+ // Start of a test method.
+ mState.enterTestMethod(description);
+ }
+
+ final var classDescription = getDescription();
+
+ // Class-level annotations are checked by the runner already, so we only check
+ // method-level annotations here.
+ if (scope == Scope.Instance && order == Order.Outer) {
+ if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood(description, true)) {
+ RavenwoodTestStats.getInstance().onTestFinished(
+ classDescription, description, Result.Skipped);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called after a test / class.
+ *
+ * Return false if the exception should be ignored.
+ */
+ private boolean onAfter(Description description, Scope scope, Order order, Throwable th) {
+ Log.v(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th);
+
+ final var classDescription = getDescription();
+
+ if (scope == Scope.Instance && order == Order.Outer) {
+ // End of a test method.
+ mState.exitTestMethod();
+
+ final Result result;
+ if (th == null) {
+ result = Result.Passed;
+ } else if (th instanceof AssumptionViolatedException) {
+ result = Result.Skipped;
+ } else {
+ result = Result.Failed;
+ }
+
+ RavenwoodTestStats.getInstance().onTestFinished(classDescription, description, result);
+ }
+
+ // If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error.
+ if (RavenwoodRule.private$ravenwood().isRunningDisabledTests()
+ && scope == Scope.Instance && order == Order.Outer) {
+
+ boolean isTestEnabled = RavenwoodEnablementChecker.shouldEnableOnRavenwood(
+ description, false);
+ if (th == null) {
+ // Test passed. Is the test method supposed to be enabled?
+ if (isTestEnabled) {
+ // Enabled and didn't throw, okay.
+ return true;
+ } else {
+ // Disabled and didn't throw. We should report it.
+ fail("Test wasn't included under Ravenwood, but it actually "
+ + "passed under Ravenwood; consider updating annotations");
+ return true; // unreachable.
+ }
+ } else {
+ // Test failed.
+ if (isTestEnabled) {
+ // Enabled but failed. We should throw the exception.
+ return true;
+ } else {
+ // Disabled and failed. Expected. Don't throw.
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Called by RavenwoodRule.
+ */
+ static void onRavenwoodRuleEnter(Description description, RavenwoodRule rule) {
+ Log.v(TAG, "onRavenwoodRuleEnter: description=" + description);
+ getCurrentRunner().mState.enterRavenwoodRule(rule);
+ }
+
+ /**
+ * Called by RavenwoodRule.
+ */
+ static void onRavenwoodRuleExit(Description description, RavenwoodRule rule) {
+ Log.v(TAG, "onRavenwoodRuleExit: description=" + description);
+ getCurrentRunner().mState.exitRavenwoodRule(rule);
+ }
+
+ private void dumpDescription(Description desc) {
+ dumpDescription(desc, "[TestDescription]=", " ");
+ }
+
+ private void dumpDescription(Description desc, String header, String indent) {
+ Log.v(TAG, indent + header + desc);
+
+ var children = desc.getChildren();
+ var childrenIndent = " " + indent;
+ for (int i = 0; i < children.size(); i++) {
+ dumpDescription(children.get(i), "#" + i + ": ", childrenIndent);
+ }
+ }
+
+ static volatile BiConsumer<String, Throwable> sCriticalErrorHandler = null;
+
+ static void onCriticalError(@NonNull String message, @Nullable Throwable th) {
+ Log.e(TAG, "Critical error! " + message, th);
+ var handler = sCriticalErrorHandler;
+ if (handler == null) {
+ Log.e(TAG, "Ravenwood cannot continue. Killing self process.", th);
+ System.exit(1);
+ }
+ handler.accept(message, th);
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
deleted file mode 100644
index 478bead..0000000
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.platform.test.ravenwood;
-
-import static org.junit.Assert.fail;
-
-import android.os.Bundle;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Order;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Scope;
-import android.platform.test.ravenwood.RavenwoodTestStats.Result;
-import android.util.Log;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.runner.Description;
-import org.junit.runners.model.TestClass;
-
-/**
- * Provide hook points created by {@link RavenwoodAwareTestRunner}.
- *
- * States are associated with each {@link RavenwoodAwareTestRunner} are stored in
- * {@link RavenwoodRunnerState}, rather than as members of {@link RavenwoodAwareTestRunner}.
- * See its javadoc for the reasons.
- *
- * All methods in this class must be called from the test main thread.
- */
-public class RavenwoodAwareTestRunnerHook {
- private static final String TAG = RavenwoodAwareTestRunner.TAG;
-
- private RavenwoodAwareTestRunnerHook() {
- }
-
- /**
- * Called before any code starts. Internally it will only initialize the environment once.
- */
- public static void performGlobalInitialization() {
- RavenwoodRuntimeEnvironmentController.globalInitOnce();
- }
-
- /**
- * Called when a runner starts, before the inner runner gets a chance to run.
- */
- public static void onRunnerInitializing(RavenwoodAwareTestRunner runner, TestClass testClass) {
- Log.i(TAG, "onRunnerInitializing: testClass=" + testClass.getJavaClass()
- + " runner=" + runner);
-
- // This is needed to make AndroidJUnit4ClassRunner happy.
- InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
- }
-
- /**
- * Called when a whole test class is skipped.
- */
- public static void onClassSkipped(Description description) {
- Log.i(TAG, "onClassSkipped: description=" + description);
- RavenwoodTestStats.getInstance().onClassSkipped(description);
- }
-
- /**
- * Called before the inner runner starts.
- */
- public static void onBeforeInnerRunnerStart(
- RavenwoodAwareTestRunner runner, Description description) throws Throwable {
- Log.v(TAG, "onBeforeInnerRunnerStart: description=" + description);
-
- // Prepare the environment before the inner runner starts.
- runner.mState.enterTestClass(description);
- }
-
- /**
- * Called after the inner runner finished.
- */
- public static void onAfterInnerRunnerFinished(
- RavenwoodAwareTestRunner runner, Description description) throws Throwable {
- Log.v(TAG, "onAfterInnerRunnerFinished: description=" + description);
-
- RavenwoodTestStats.getInstance().onClassFinished(description);
- runner.mState.exitTestClass();
- }
-
- /**
- * Called before a test / class.
- *
- * Return false if it should be skipped.
- */
- public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description,
- Scope scope, Order order) throws Throwable {
- Log.v(TAG, "onBefore: description=" + description + ", " + scope + ", " + order);
-
- if (scope == Scope.Instance && order == Order.Outer) {
- // Start of a test method.
- runner.mState.enterTestMethod(description);
- }
-
- final var classDescription = runner.mState.getClassDescription();
-
- // Class-level annotations are checked by the runner already, so we only check
- // method-level annotations here.
- if (scope == Scope.Instance && order == Order.Outer) {
- if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood(
- description, true)) {
- RavenwoodTestStats.getInstance().onTestFinished(
- classDescription, description, Result.Skipped);
- return false;
- }
- }
- return true;
- }
-
- /**
- * Called after a test / class.
- *
- * Return false if the exception should be ignored.
- */
- public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description,
- Scope scope, Order order, Throwable th) {
- Log.v(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th);
-
- final var classDescription = runner.mState.getClassDescription();
-
- if (scope == Scope.Instance && order == Order.Outer) {
- // End of a test method.
- runner.mState.exitTestMethod();
- RavenwoodTestStats.getInstance().onTestFinished(classDescription, description,
- th == null ? Result.Passed : Result.Failed);
- }
-
- // If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error.
- if (RavenwoodRule.private$ravenwood().isRunningDisabledTests()
- && scope == Scope.Instance && order == Order.Outer) {
-
- boolean isTestEnabled = RavenwoodEnablementChecker.shouldEnableOnRavenwood(
- description, false);
- if (th == null) {
- // Test passed. Is the test method supposed to be enabled?
- if (isTestEnabled) {
- // Enabled and didn't throw, okay.
- return true;
- } else {
- // Disabled and didn't throw. We should report it.
- fail("Test wasn't included under Ravenwood, but it actually "
- + "passed under Ravenwood; consider updating annotations");
- return true; // unreachable.
- }
- } else {
- // Test failed.
- if (isTestEnabled) {
- // Enabled but failed. We should throw the exception.
- return true;
- } else {
- // Disabled and failed. Expected. Don't throw.
- return false;
- }
- }
- }
- return true;
- }
-
- /**
- * Called by {@link RavenwoodAwareTestRunner} to see if it should run a test class or not.
- */
- public static boolean shouldRunClassOnRavenwood(Class<?> clazz) {
- return RavenwoodEnablementChecker.shouldRunClassOnRavenwood(clazz, true);
- }
-
- /**
- * Called by RavenwoodRule.
- */
- public static void onRavenwoodRuleEnter(RavenwoodAwareTestRunner runner,
- Description description, RavenwoodRule rule) throws Throwable {
- Log.v(TAG, "onRavenwoodRuleEnter: description=" + description);
-
- runner.mState.enterRavenwoodRule(rule);
- }
-
-
- /**
- * Called by RavenwoodRule.
- */
- public static void onRavenwoodRuleExit(RavenwoodAwareTestRunner runner,
- Description description, RavenwoodRule rule) throws Throwable {
- Log.v(TAG, "onRavenwoodRuleExit: description=" + description);
-
- runner.mState.exitRavenwoodRule(rule);
- }
-}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigPrivate.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigPrivate.java
new file mode 100644
index 0000000..ffb642d
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigPrivate.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.ravenwood;
+
+import android.annotation.Nullable;
+
+import java.util.function.BiConsumer;
+
+/**
+ * Contains Ravenwood private APIs.
+ */
+public class RavenwoodConfigPrivate {
+ private RavenwoodConfigPrivate() {
+ }
+
+ /**
+ * Set a listener for onCriticalError(), for testing. If a listener is set, we won't call
+ * System.exit().
+ */
+ public static void setCriticalErrorHandler(@Nullable BiConsumer<String, Throwable> handler) {
+ RavenwoodAwareTestRunner.sCriticalErrorHandler = handler;
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunNotifier.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunNotifier.java
new file mode 100644
index 0000000..6903035
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunNotifier.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.ravenwood;
+
+import android.util.Log;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runner.notification.StoppedByUserException;
+import org.junit.runners.model.MultipleFailureException;
+
+import java.util.ArrayList;
+import java.util.Stack;
+
+/**
+ * A run notifier that wraps another notifier and provides the following features:
+ * - Handle a failure that happened before testStarted and testEnded (typically that means
+ * it's from @BeforeClass or @AfterClass, or a @ClassRule) and deliver it as if
+ * individual tests in the class reported it. This is for b/364395552.
+ *
+ * - Logging.
+ */
+class RavenwoodRunNotifier extends RunNotifier {
+ private final RunNotifier mRealNotifier;
+
+ private final Stack<Description> mSuiteStack = new Stack<>();
+ private Description mCurrentSuite = null;
+ private final ArrayList<Throwable> mOutOfTestFailures = new ArrayList<>();
+
+ private boolean mBeforeTest = true;
+ private boolean mAfterTest = false;
+
+ RavenwoodRunNotifier(RunNotifier realNotifier) {
+ mRealNotifier = realNotifier;
+ }
+
+ private boolean isInTest() {
+ return !mBeforeTest && !mAfterTest;
+ }
+
+ @Override
+ public void addListener(RunListener listener) {
+ mRealNotifier.addListener(listener);
+ }
+
+ @Override
+ public void removeListener(RunListener listener) {
+ mRealNotifier.removeListener(listener);
+ }
+
+ @Override
+ public void addFirstListener(RunListener listener) {
+ mRealNotifier.addFirstListener(listener);
+ }
+
+ @Override
+ public void fireTestRunStarted(Description description) {
+ Log.i(RavenwoodAwareTestRunner.TAG, "testRunStarted: " + description);
+ mRealNotifier.fireTestRunStarted(description);
+ }
+
+ @Override
+ public void fireTestRunFinished(Result result) {
+ Log.i(RavenwoodAwareTestRunner.TAG, "testRunFinished: "
+ + result.getRunCount() + ","
+ + result.getFailureCount() + ","
+ + result.getAssumptionFailureCount() + ","
+ + result.getIgnoreCount());
+ mRealNotifier.fireTestRunFinished(result);
+ }
+
+ @Override
+ public void fireTestSuiteStarted(Description description) {
+ Log.i(RavenwoodAwareTestRunner.TAG, "testSuiteStarted: " + description);
+ mRealNotifier.fireTestSuiteStarted(description);
+
+ mBeforeTest = true;
+ mAfterTest = false;
+
+ // Keep track of the current suite, needed if the outer test is a Suite,
+ // in which case its children are test classes. (not test methods)
+ mCurrentSuite = description;
+ mSuiteStack.push(description);
+
+ mOutOfTestFailures.clear();
+ }
+
+ @Override
+ public void fireTestSuiteFinished(Description description) {
+ Log.i(RavenwoodAwareTestRunner.TAG, "testSuiteFinished: " + description);
+ mRealNotifier.fireTestSuiteFinished(description);
+
+ maybeHandleOutOfTestFailures();
+
+ mBeforeTest = true;
+ mAfterTest = false;
+
+ // Restore the upper suite.
+ mSuiteStack.pop();
+ mCurrentSuite = mSuiteStack.size() == 0 ? null : mSuiteStack.peek();
+ }
+
+ @Override
+ public void fireTestStarted(Description description) throws StoppedByUserException {
+ Log.i(RavenwoodAwareTestRunner.TAG, "testStarted: " + description);
+ mRealNotifier.fireTestStarted(description);
+
+ mAfterTest = false;
+ mBeforeTest = false;
+ }
+
+ @Override
+ public void fireTestFailure(Failure failure) {
+ Log.i(RavenwoodAwareTestRunner.TAG, "testFailure: " + failure);
+
+ if (isInTest()) {
+ mRealNotifier.fireTestFailure(failure);
+ } else {
+ mOutOfTestFailures.add(failure.getException());
+ }
+ }
+
+ @Override
+ public void fireTestAssumptionFailed(Failure failure) {
+ Log.i(RavenwoodAwareTestRunner.TAG, "testAssumptionFailed: " + failure);
+
+ if (isInTest()) {
+ mRealNotifier.fireTestAssumptionFailed(failure);
+ } else {
+ mOutOfTestFailures.add(failure.getException());
+ }
+ }
+
+ @Override
+ public void fireTestIgnored(Description description) {
+ Log.i(RavenwoodAwareTestRunner.TAG, "testIgnored: " + description);
+ mRealNotifier.fireTestIgnored(description);
+ }
+
+ @Override
+ public void fireTestFinished(Description description) {
+ Log.i(RavenwoodAwareTestRunner.TAG, "testFinished: " + description);
+ mRealNotifier.fireTestFinished(description);
+
+ mAfterTest = true;
+ }
+
+ @Override
+ public void pleaseStop() {
+ Log.w(RavenwoodAwareTestRunner.TAG, "pleaseStop:");
+ mRealNotifier.pleaseStop();
+ }
+
+ /**
+ * At the end of each Suite, we handle failures happened out of test methods.
+ * (typically in @BeforeClass or @AfterClasses)
+ *
+ * This is to work around b/364395552.
+ */
+ private boolean maybeHandleOutOfTestFailures() {
+ if (mOutOfTestFailures.size() == 0) {
+ return false;
+ }
+ Throwable th;
+ if (mOutOfTestFailures.size() == 1) {
+ th = mOutOfTestFailures.get(0);
+ } else {
+ th = new MultipleFailureException(mOutOfTestFailures);
+ }
+ if (mBeforeTest) {
+ reportBeforeTestFailure(mCurrentSuite, th);
+ return true;
+ }
+ if (mAfterTest) {
+ reportAfterTestFailure(th);
+ return true;
+ }
+ return false;
+ }
+
+ public void reportBeforeTestFailure(Description suiteDesc, Throwable th) {
+ // If a failure happens befere running any tests, we'll need to pretend
+ // as if each test in the suite reported the failure, to work around b/364395552.
+ for (var child : suiteDesc.getChildren()) {
+ if (child.isSuite()) {
+ // If the chiil is still a "parent" -- a test class or a test suite
+ // -- propagate to its children.
+ mRealNotifier.fireTestSuiteStarted(child);
+ reportBeforeTestFailure(child, th);
+ mRealNotifier.fireTestSuiteFinished(child);
+ } else {
+ mRealNotifier.fireTestStarted(child);
+ Failure f = new Failure(child, th);
+ if (th instanceof AssumptionViolatedException) {
+ mRealNotifier.fireTestAssumptionFailed(f);
+ } else {
+ mRealNotifier.fireTestFailure(f);
+ }
+ mRealNotifier.fireTestFinished(child);
+ }
+ }
+ }
+
+ public void reportAfterTestFailure(Throwable th) {
+ // Unfortunately, there's no good way to report it, so kill the own process.
+ RavenwoodAwareTestRunner.onCriticalError(
+ "Failures detected in @AfterClass, which would be swallowed by tradefed",
+ th);
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
index 03513ab..ec00e8f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
@@ -20,8 +20,8 @@
import static org.junit.Assert.fail;
import android.annotation.Nullable;
+import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
import com.android.ravenwood.common.RavenwoodRuntimeException;
import org.junit.ClassRule;
@@ -29,9 +29,7 @@
import org.junit.rules.TestRule;
import org.junit.runner.Description;
-import java.io.IOException;
import java.lang.reflect.Field;
-import java.util.WeakHashMap;
/**
* Used to store various states associated with the current test runner that's inly needed
@@ -45,10 +43,6 @@
public final class RavenwoodRunnerState {
private static final String TAG = "RavenwoodRunnerState";
- @GuardedBy("sStates")
- private static final WeakHashMap<RavenwoodAwareTestRunner, RavenwoodRunnerState> sStates =
- new WeakHashMap<>();
-
private final RavenwoodAwareTestRunner mRunner;
/**
@@ -58,35 +52,65 @@
mRunner = runner;
}
- private Description mClassDescription;
+ /**
+ * The RavenwoodConfig used to configure the current Ravenwood environment.
+ * This can either come from mConfig or mRule.
+ */
+ private RavenwoodConfig mCurrentConfig;
+ /**
+ * The RavenwoodConfig declared in the test class
+ */
+ private RavenwoodConfig mConfig;
+ /**
+ * The RavenwoodRule currently in effect, declared in the test class
+ */
+ private RavenwoodRule mRule;
+ private boolean mHasRavenwoodRule;
private Description mMethodDescription;
- private RavenwoodConfig mCurrentConfig;
- private RavenwoodRule mCurrentRule;
- private boolean mHasRavenwoodRule;
-
- public Description getClassDescription() {
- return mClassDescription;
+ public RavenwoodConfig getConfig() {
+ return mCurrentConfig;
}
- public void enterTestClass(Description classDescription) throws IOException {
- mClassDescription = classDescription;
+ public void enterTestRunner() {
+ Log.i(TAG, "enterTestRunner: " + mRunner);
- mHasRavenwoodRule = hasRavenwoodRule(mRunner.getTestClass().getJavaClass());
- mCurrentConfig = extractConfiguration(mRunner.getTestClass().getJavaClass());
+ mHasRavenwoodRule = hasRavenwoodRule(mRunner.mTestJavaClass);
+ mConfig = extractConfiguration(mRunner.mTestJavaClass);
+
+ if (mConfig != null) {
+ if (mHasRavenwoodRule) {
+ fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class."
+ + " Suggest migrating to RavenwoodConfig.");
+ }
+ mCurrentConfig = mConfig;
+ } else if (!mHasRavenwoodRule) {
+ // If no RavenwoodConfig and no RavenwoodRule, use a default config
+ mCurrentConfig = new RavenwoodConfig.Builder().build();
+ }
if (mCurrentConfig != null) {
- RavenwoodRuntimeEnvironmentController.init(mCurrentConfig);
+ RavenwoodRuntimeEnvironmentController.init(mRunner);
+ }
+ }
+
+ public void enterTestClass() {
+ Log.i(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName());
+
+ if (mCurrentConfig != null) {
+ RavenwoodRuntimeEnvironmentController.init(mRunner);
}
}
public void exitTestClass() {
- if (mCurrentConfig != null) {
- try {
+ Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName());
+ try {
+ if (mCurrentConfig != null) {
RavenwoodRuntimeEnvironmentController.reset();
- } finally {
- mClassDescription = null;
}
+ } finally {
+ mConfig = null;
+ mRule = null;
}
}
@@ -96,55 +120,40 @@
public void exitTestMethod() {
mMethodDescription = null;
+ RavenwoodRuntimeEnvironmentController.reinit();
}
- public void enterRavenwoodRule(RavenwoodRule rule) throws IOException {
+ public void enterRavenwoodRule(RavenwoodRule rule) {
if (!mHasRavenwoodRule) {
fail("If you have a RavenwoodRule in your test, make sure the field type is"
+ " RavenwoodRule so Ravenwood can detect it.");
}
- if (mCurrentConfig != null) {
- fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class."
- + " Suggest migrating to RavenwoodConfig.");
- }
- if (mCurrentRule != null) {
+ if (mRule != null) {
fail("Multiple nesting RavenwoodRule's are detected in the same class,"
+ " which is not supported.");
}
- mCurrentRule = rule;
- RavenwoodRuntimeEnvironmentController.init(rule.getConfiguration());
+ mRule = rule;
+ if (mCurrentConfig == null) {
+ mCurrentConfig = rule.getConfiguration();
+ }
+ RavenwoodRuntimeEnvironmentController.init(mRunner);
}
public void exitRavenwoodRule(RavenwoodRule rule) {
- if (mCurrentRule != rule) {
- return; // This happens if the rule did _not_ take effect somehow.
+ if (mRule != rule) {
+ fail("RavenwoodRule did not take effect.");
}
-
- try {
- RavenwoodRuntimeEnvironmentController.reset();
- } finally {
- mCurrentRule = null;
- }
+ mRule = null;
}
/**
* @return a configuration from a test class, if any.
*/
@Nullable
- private RavenwoodConfig extractConfiguration(Class<?> testClass) {
+ private static RavenwoodConfig extractConfiguration(Class<?> testClass) {
var field = findConfigurationField(testClass);
if (field == null) {
- if (mHasRavenwoodRule) {
- // Should be handled by RavenwoodRule
- return null;
- }
-
- // If no RavenwoodConfig and no RavenwoodRule, return a default config
- return new RavenwoodConfig.Builder().build();
- }
- if (mHasRavenwoodRule) {
- fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class."
- + " Suggest migrating to RavenwoodConfig.");
+ return null;
}
try {
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index c2806da..0f16352 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -137,7 +137,7 @@
return res;
}
- private static RavenwoodConfig sConfig;
+ private static RavenwoodAwareTestRunner sRunner;
private static RavenwoodSystemProperties sProps;
private static boolean sInitialized = false;
@@ -165,20 +165,26 @@
RavenwoodSystemProperties.initialize(RAVENWOOD_BUILD_PROP);
setSystemProperties(null);
+ // Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
+ // before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS).
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging");
+ try {
+ Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
// Make sure libandroid_runtime is loaded.
RavenwoodNativeLoader.loadFrameworkNativeCode();
// Redirect stdout/stdin to liblog.
RuntimeInit.redirectLogStreams();
- if (RAVENWOOD_VERBOSE_LOGGING) {
- RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging");
- try {
- Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
- } catch (ErrnoException e) {
- // Shouldn't happen.
- }
- }
+ // Touch some references early to ensure they're <clinit>'ed
+ Objects.requireNonNull(Build.TYPE);
+ Objects.requireNonNull(Build.VERSION.SDK);
System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
// This will let AndroidJUnit4 use the original runner.
@@ -191,12 +197,19 @@
/**
* Initialize the environment.
*/
- public static void init(RavenwoodConfig config) throws IOException {
+ public static void init(RavenwoodAwareTestRunner runner) {
if (RAVENWOOD_VERBOSE_LOGGING) {
- Log.i(TAG, "init() called here", new RuntimeException("STACKTRACE"));
+ Log.i(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE"));
}
+ if (sRunner == runner) {
+ return;
+ }
+ if (sRunner != null) {
+ reset();
+ }
+ sRunner = runner;
try {
- initInner(config);
+ initInner(runner.mState.getConfig());
} catch (Exception th) {
Log.e(TAG, "init() failed", th);
reset();
@@ -205,10 +218,6 @@
}
private static void initInner(RavenwoodConfig config) throws IOException {
- if (sConfig != null) {
- throw new RavenwoodRuntimeException("Internal error: init() called without reset()");
- }
- sConfig = config;
if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
maybeThrowPendingUncaughtException(false);
Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
@@ -216,7 +225,7 @@
android.os.Process.init$ravenwood(config.mUid, config.mPid);
sOriginalIdentityToken = Binder.clearCallingIdentity();
- Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
+ reinit();
setSystemProperties(config.mSystemProperties);
ServiceManager.init$ravenwood();
@@ -269,9 +278,7 @@
config.mInstContext = instContext;
config.mTargetContext = targetContext;
- final Supplier<Resources> systemResourcesLoader = () -> {
- return config.mState.loadResources(null);
- };
+ final Supplier<Resources> systemResourcesLoader = () -> config.mState.loadResources(null);
config.mState.mSystemServerContext =
new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader);
@@ -288,10 +295,14 @@
RavenwoodRuntimeEnvironmentController::dumpStacks,
TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
}
+ }
- // Touch some references early to ensure they're <clinit>'ed
- Objects.requireNonNull(Build.TYPE);
- Objects.requireNonNull(Build.VERSION.SDK);
+ /**
+ * Partially re-initialize after each test method invocation
+ */
+ public static void reinit() {
+ var config = sRunner.mState.getConfig();
+ Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
}
/**
@@ -301,11 +312,11 @@
if (RAVENWOOD_VERBOSE_LOGGING) {
Log.i(TAG, "reset() called here", new RuntimeException("STACKTRACE"));
}
- if (sConfig == null) {
+ if (sRunner == null) {
throw new RavenwoodRuntimeException("Internal error: reset() already called");
}
- var config = sConfig;
- sConfig = null;
+ var config = sRunner.mState.getConfig();
+ sRunner = null;
if (ENABLE_TIMEOUT_STACKS) {
sPendingTimeout.cancel(false);
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
index b4b8715..016de8e 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
@@ -90,9 +90,7 @@
// Create the "latest" symlink.
Path symlink = Paths.get(tmpdir, basename + "latest.csv");
try {
- if (Files.exists(symlink)) {
- Files.delete(symlink);
- }
+ Files.deleteIfExists(symlink);
Files.createSymbolicLink(symlink, Paths.get(mOutputFile.getName()));
} catch (IOException e) {
diff --git a/ravenwood/junit-src/android/platform/test/annotations/RavenwoodTestRunnerInitializing.java b/ravenwood/junit-src/android/platform/test/annotations/RavenwoodTestRunnerInitializing.java
new file mode 100644
index 0000000..3bba27a
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/annotations/RavenwoodTestRunnerInitializing.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.annotations;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation similar to JUnit's BeforeClass, but this gets executed before
+ * the inner runner is instantiated, and only on Ravenwood.
+ * It can be used to initialize what's needed by the inner runner.
+ */
+@Target({METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RavenwoodTestRunnerInitializing {
+}
diff --git a/ravenwood/junit-src/android/platform/test/annotations/internal/InnerRunner.java b/ravenwood/junit-src/android/platform/test/annotations/internal/InnerRunner.java
new file mode 100644
index 0000000..dde53a5
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/annotations/internal/InnerRunner.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.annotations.internal;
+
+import static java.lang.annotation.ElementType.TYPE;
+
+import org.junit.runner.Runner;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Inherited
+@Target({TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface InnerRunner {
+ Class<? extends Runner> value();
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
deleted file mode 100644
index 5ba972d..0000000
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ /dev/null
@@ -1,733 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.platform.test.ravenwood;
-
-import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
-import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicVoidMethod;
-import static com.android.ravenwood.common.RavenwoodCommonUtils.isOnRavenwood;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.util.Log;
-
-import org.junit.Assume;
-import org.junit.AssumptionViolatedException;
-import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runner.Result;
-import org.junit.runner.Runner;
-import org.junit.runner.manipulation.Filter;
-import org.junit.runner.manipulation.Filterable;
-import org.junit.runner.manipulation.InvalidOrderingException;
-import org.junit.runner.manipulation.NoTestsRemainException;
-import org.junit.runner.manipulation.Orderable;
-import org.junit.runner.manipulation.Orderer;
-import org.junit.runner.manipulation.Sortable;
-import org.junit.runner.manipulation.Sorter;
-import org.junit.runner.notification.Failure;
-import org.junit.runner.notification.RunListener;
-import org.junit.runner.notification.RunNotifier;
-import org.junit.runner.notification.StoppedByUserException;
-import org.junit.runners.BlockJUnit4ClassRunner;
-import org.junit.runners.Suite;
-import org.junit.runners.model.MultipleFailureException;
-import org.junit.runners.model.RunnerBuilder;
-import org.junit.runners.model.Statement;
-import org.junit.runners.model.TestClass;
-
-import java.lang.annotation.Annotation;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Stack;
-import java.util.function.BiConsumer;
-
-/**
- * A test runner used for Ravenwood.
- *
- * It will delegate to another runner specified with {@link InnerRunner}
- * (default = {@link BlockJUnit4ClassRunner}) with the following features.
- * - Add a {@link RavenwoodAwareTestRunnerHook#onRunnerInitializing} hook, which is called before
- * the inner runner gets a chance to run. This can be used to initialize stuff used by the
- * inner runner.
- * - Add hook points, which are handed by RavenwoodAwareTestRunnerHook, with help from
- * the four test rules such as {@link #sImplicitClassOuterRule}, which are also injected by
- * the ravenizer tool.
- *
- * We use this runner to:
- * - Initialize the bare minimum environmnet just to be enough to make the actual test runners
- * happy.
- * - Handle {@link android.platform.test.annotations.DisabledOnRavenwood}.
- *
- * This class is built such that it can also be used on a real device, but in that case
- * it will basically just delegate to the inner wrapper, and won't do anything special.
- * (no hooks, etc.)
- */
-public final class RavenwoodAwareTestRunner extends Runner implements Filterable, Orderable {
- public static final String TAG = "Ravenwood";
-
- @Inherited
- @Target({TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface InnerRunner {
- Class<? extends Runner> value();
- }
-
- /**
- * An annotation similar to JUnit's BeforeClass, but this gets executed before
- * the inner runner is instantiated, and only on Ravenwood.
- * It can be used to initialize what's needed by the inner runner.
- */
- @Target({METHOD})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface RavenwoodTestRunnerInitializing {
- }
-
- /** Scope of a hook. */
- public enum Scope {
- Class,
- Instance,
- }
-
- /** Order of a hook. */
- public enum Order {
- Outer,
- Inner,
- }
-
- // The following four rule instances will be injected to tests by the Ravenizer tool.
- private static class RavenwoodClassOuterRule implements TestRule {
- @Override
- public Statement apply(Statement base, Description description) {
- return getCurrentRunner().wrapWithHooks(base, description, Scope.Class, Order.Outer);
- }
- }
-
- private static class RavenwoodClassInnerRule implements TestRule {
- @Override
- public Statement apply(Statement base, Description description) {
- return getCurrentRunner().wrapWithHooks(base, description, Scope.Class, Order.Inner);
- }
- }
-
- private static class RavenwoodInstanceOuterRule implements TestRule {
- @Override
- public Statement apply(Statement base, Description description) {
- return getCurrentRunner().wrapWithHooks(
- base, description, Scope.Instance, Order.Outer);
- }
- }
-
- private static class RavenwoodInstanceInnerRule implements TestRule {
- @Override
- public Statement apply(Statement base, Description description) {
- return getCurrentRunner().wrapWithHooks(
- base, description, Scope.Instance, Order.Inner);
- }
- }
-
- public static final TestRule sImplicitClassOuterRule = new RavenwoodClassOuterRule();
- public static final TestRule sImplicitClassInnerRule = new RavenwoodClassInnerRule();
- public static final TestRule sImplicitInstOuterRule = new RavenwoodInstanceOuterRule();
- public static final TestRule sImplicitInstInnerRule = new RavenwoodInstanceOuterRule();
-
- public static final String IMPLICIT_CLASS_OUTER_RULE_NAME = "sImplicitClassOuterRule";
- public static final String IMPLICIT_CLASS_INNER_RULE_NAME = "sImplicitClassInnerRule";
- public static final String IMPLICIT_INST_OUTER_RULE_NAME = "sImplicitInstOuterRule";
- public static final String IMPLICIT_INST_INNER_RULE_NAME = "sImplicitInstInnerRule";
-
- /** Keeps track of the runner on the current thread. */
- private static final ThreadLocal<RavenwoodAwareTestRunner> sCurrentRunner = new ThreadLocal<>();
-
- static RavenwoodAwareTestRunner getCurrentRunner() {
- var runner = sCurrentRunner.get();
- if (runner == null) {
- throw new RuntimeException("Current test runner not set!");
- }
- return runner;
- }
-
- private final Class<?> mTestJavaClass;
- private TestClass mTestClass = null;
- private Runner mRealRunner = null;
- private Description mDescription = null;
- private Throwable mExceptionInConstructor = null;
- private boolean mRealRunnerTakesRunnerBuilder = false;
-
- /**
- * Stores internal states / methods associated with this runner that's only needed in
- * junit-impl.
- */
- final RavenwoodRunnerState mState = new RavenwoodRunnerState(this);
-
- private Error logAndFail(String message, Throwable exception) {
- Log.e(TAG, message, exception);
- throw new AssertionError(message, exception);
- }
-
- public TestClass getTestClass() {
- return mTestClass;
- }
-
- /**
- * Constructor.
- */
- public RavenwoodAwareTestRunner(Class<?> testClass) {
- mTestJavaClass = testClass;
- try {
- performGlobalInitialization();
-
- /*
- * If the class has @DisabledOnRavenwood, then we'll delegate to
- * ClassSkippingTestRunner, which simply skips it.
- *
- * We need to do it before instantiating TestClass for b/367694651.
- */
- if (isOnRavenwood() && !RavenwoodAwareTestRunnerHook.shouldRunClassOnRavenwood(
- testClass)) {
- mRealRunner = new ClassSkippingTestRunner(testClass);
- mDescription = mRealRunner.getDescription();
- return;
- }
-
- mTestClass = new TestClass(testClass);
-
- Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
-
- onRunnerInitializing();
-
- // Find the real runner.
- final Class<? extends Runner> realRunnerClass;
- final InnerRunner innerRunnerAnnotation = mTestClass.getAnnotation(InnerRunner.class);
- if (innerRunnerAnnotation != null) {
- realRunnerClass = innerRunnerAnnotation.value();
- } else {
- // Default runner.
- realRunnerClass = BlockJUnit4ClassRunner.class;
- }
-
- try {
- Log.i(TAG, "Initializing the inner runner: " + realRunnerClass);
-
- mRealRunner = instantiateRealRunner(realRunnerClass, testClass);
- mDescription = mRealRunner.getDescription();
-
- } catch (InstantiationException | IllegalAccessException
- | InvocationTargetException | NoSuchMethodException e) {
- throw logAndFail("Failed to instantiate " + realRunnerClass, e);
- }
- } catch (Throwable th) {
- // If we throw in the constructor, Tradefed may not report it and just ignore the class,
- // so record it and throw it when the test actually started.
- Log.e(TAG, "Fatal: Exception detected in constructor", th);
- mExceptionInConstructor = new RuntimeException("Exception detected in constructor",
- th);
- mDescription = Description.createTestDescription(testClass, "Constructor");
-
- // This is for testing if tradefed is fixed.
- if ("1".equals(System.getenv("RAVENWOOD_THROW_EXCEPTION_IN_TEST_RUNNER"))) {
- throw th;
- }
- }
- }
-
- private Runner instantiateRealRunner(
- Class<? extends Runner> realRunnerClass,
- Class<?> testClass)
- throws NoSuchMethodException, InvocationTargetException, InstantiationException,
- IllegalAccessException {
- try {
- return realRunnerClass.getConstructor(Class.class).newInstance(testClass);
- } catch (NoSuchMethodException e) {
- var constructor = realRunnerClass.getConstructor(Class.class, RunnerBuilder.class);
- mRealRunnerTakesRunnerBuilder = true;
- return constructor.newInstance(testClass, new AllDefaultPossibilitiesBuilder());
- }
- }
-
- private void performGlobalInitialization() {
- if (!isOnRavenwood()) {
- return;
- }
- RavenwoodAwareTestRunnerHook.performGlobalInitialization();
- }
-
- /**
- * Run the bare minimum setup to initialize the wrapped runner.
- */
- // This method is called by the ctor, so never make it virtual.
- private void onRunnerInitializing() {
- if (!isOnRavenwood()) {
- return;
- }
-
- RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClass);
-
- // Hook point to allow more customization.
- runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
- }
-
- private void runAnnotatedMethodsOnRavenwood(Class<? extends Annotation> annotationClass,
- Object instance) {
- if (!isOnRavenwood()) {
- return;
- }
- Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
-
- for (var method : getTestClass().getAnnotatedMethods(annotationClass)) {
- ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null);
-
- var methodDesc = method.getDeclaringClass().getName() + "."
- + method.getMethod().toString();
- try {
- method.getMethod().invoke(instance);
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw logAndFail("Caught exception while running method " + methodDesc, e);
- }
- }
- }
-
- @Override
- public Description getDescription() {
- return mDescription;
- }
-
- @Override
- public void run(RunNotifier realNotifier) {
- final RavenwoodRunNotifier notifier = new RavenwoodRunNotifier(realNotifier);
-
- if (mRealRunner instanceof ClassSkippingTestRunner) {
- mRealRunner.run(notifier);
- RavenwoodAwareTestRunnerHook.onClassSkipped(getDescription());
- return;
- }
-
- Log.v(TAG, "Starting " + mTestJavaClass.getCanonicalName());
- if (RAVENWOOD_VERBOSE_LOGGING) {
- dumpDescription(getDescription());
- }
-
- if (maybeReportExceptionFromConstructor(notifier)) {
- return;
- }
-
- // TODO(b/365976974): handle nested classes better
- final boolean skipRunnerHook =
- mRealRunnerTakesRunnerBuilder && mRealRunner instanceof Suite;
-
- sCurrentRunner.set(this);
- try {
- if (!skipRunnerHook) {
- try {
- RavenwoodAwareTestRunnerHook.onBeforeInnerRunnerStart(
- this, getDescription());
- } catch (Throwable th) {
- notifier.reportBeforeTestFailure(getDescription(), th);
- return;
- }
- }
-
- // Delegate to the inner runner.
- mRealRunner.run(notifier);
- } finally {
- sCurrentRunner.remove();
-
- if (!skipRunnerHook) {
- try {
- RavenwoodAwareTestRunnerHook.onAfterInnerRunnerFinished(
- this, getDescription());
- } catch (Throwable th) {
- notifier.reportAfterTestFailure(th);
- }
- }
- }
- }
-
- /** Throw the exception detected in the constructor, if any. */
- private boolean maybeReportExceptionFromConstructor(RunNotifier notifier) {
- if (mExceptionInConstructor == null) {
- return false;
- }
- notifier.fireTestStarted(mDescription);
- notifier.fireTestFailure(new Failure(mDescription, mExceptionInConstructor));
- notifier.fireTestFinished(mDescription);
-
- return true;
- }
-
- @Override
- public void filter(Filter filter) throws NoTestsRemainException {
- if (mRealRunner instanceof Filterable r) {
- r.filter(filter);
- }
- }
-
- @Override
- public void order(Orderer orderer) throws InvalidOrderingException {
- if (mRealRunner instanceof Orderable r) {
- r.order(orderer);
- }
- }
-
- @Override
- public void sort(Sorter sorter) {
- if (mRealRunner instanceof Sortable r) {
- r.sort(sorter);
- }
- }
-
- private Statement wrapWithHooks(Statement base, Description description, Scope scope,
- Order order) {
- if (!isOnRavenwood()) {
- return base;
- }
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- runWithHooks(description, scope, order, base);
- }
- };
- }
-
- private void runWithHooks(Description description, Scope scope, Order order, Runnable r)
- throws Throwable {
- runWithHooks(description, scope, order, new Statement() {
- @Override
- public void evaluate() throws Throwable {
- r.run();
- }
- });
- }
-
- private void runWithHooks(Description description, Scope scope, Order order, Statement s)
- throws Throwable {
- if (isOnRavenwood()) {
- Assume.assumeTrue(
- RavenwoodAwareTestRunnerHook.onBefore(this, description, scope, order));
- }
- try {
- s.evaluate();
- if (isOnRavenwood()) {
- RavenwoodAwareTestRunnerHook.onAfter(this, description, scope, order, null);
- }
- } catch (Throwable t) {
- boolean shouldThrow = true;
- if (isOnRavenwood()) {
- shouldThrow = RavenwoodAwareTestRunnerHook.onAfter(
- this, description, scope, order, t);
- }
- if (shouldThrow) {
- throw t;
- }
- }
- }
-
- /**
- * A runner that simply skips a class. It still has to support {@link Filterable}
- * because otherwise the result still says "SKIPPED" even when it's not included in the
- * filter.
- */
- private static class ClassSkippingTestRunner extends Runner implements Filterable {
- private final Description mDescription;
- private boolean mFilteredOut;
-
- ClassSkippingTestRunner(Class<?> testClass) {
- mDescription = Description.createTestDescription(testClass, testClass.getSimpleName());
- mFilteredOut = false;
- }
-
- @Override
- public Description getDescription() {
- return mDescription;
- }
-
- @Override
- public void run(RunNotifier notifier) {
- if (mFilteredOut) {
- return;
- }
- notifier.fireTestSuiteStarted(mDescription);
- notifier.fireTestIgnored(mDescription);
- notifier.fireTestSuiteFinished(mDescription);
- }
-
- @Override
- public void filter(Filter filter) throws NoTestsRemainException {
- if (filter.shouldRun(mDescription)) {
- mFilteredOut = false;
- } else {
- throw new NoTestsRemainException();
- }
- }
- }
-
- private void dumpDescription(Description desc) {
- dumpDescription(desc, "[TestDescription]=", " ");
- }
-
- private void dumpDescription(Description desc, String header, String indent) {
- Log.v(TAG, indent + header + desc);
-
- var children = desc.getChildren();
- var childrenIndent = " " + indent;
- for (int i = 0; i < children.size(); i++) {
- dumpDescription(children.get(i), "#" + i + ": ", childrenIndent);
- }
- }
-
- /**
- * A run notifier that wraps another notifier and provides the following features:
- * - Handle a failure that happened before testStarted and testEnded (typically that means
- * it's from @BeforeClass or @AfterClass, or a @ClassRule) and deliver it as if
- * individual tests in the class reported it. This is for b/364395552.
- *
- * - Logging.
- */
- private class RavenwoodRunNotifier extends RunNotifier {
- private final RunNotifier mRealNotifier;
-
- private final Stack<Description> mSuiteStack = new Stack<>();
- private Description mCurrentSuite = null;
- private final ArrayList<Throwable> mOutOfTestFailures = new ArrayList<>();
-
- private boolean mBeforeTest = true;
- private boolean mAfterTest = false;
-
- private RavenwoodRunNotifier(RunNotifier realNotifier) {
- mRealNotifier = realNotifier;
- }
-
- private boolean isInTest() {
- return !mBeforeTest && !mAfterTest;
- }
-
- @Override
- public void addListener(RunListener listener) {
- mRealNotifier.addListener(listener);
- }
-
- @Override
- public void removeListener(RunListener listener) {
- mRealNotifier.removeListener(listener);
- }
-
- @Override
- public void addFirstListener(RunListener listener) {
- mRealNotifier.addFirstListener(listener);
- }
-
- @Override
- public void fireTestRunStarted(Description description) {
- Log.i(TAG, "testRunStarted: " + description);
- mRealNotifier.fireTestRunStarted(description);
- }
-
- @Override
- public void fireTestRunFinished(Result result) {
- Log.i(TAG, "testRunFinished: "
- + result.getRunCount() + ","
- + result.getFailureCount() + ","
- + result.getAssumptionFailureCount() + ","
- + result.getIgnoreCount());
- mRealNotifier.fireTestRunFinished(result);
- }
-
- @Override
- public void fireTestSuiteStarted(Description description) {
- Log.i(TAG, "testSuiteStarted: " + description);
- mRealNotifier.fireTestSuiteStarted(description);
-
- mBeforeTest = true;
- mAfterTest = false;
-
- // Keep track of the current suite, needed if the outer test is a Suite,
- // in which case its children are test classes. (not test methods)
- mCurrentSuite = description;
- mSuiteStack.push(description);
-
- mOutOfTestFailures.clear();
- }
-
- @Override
- public void fireTestSuiteFinished(Description description) {
- Log.i(TAG, "testSuiteFinished: " + description);
- mRealNotifier.fireTestSuiteFinished(description);
-
- maybeHandleOutOfTestFailures();
-
- mBeforeTest = true;
- mAfterTest = false;
-
- // Restore the upper suite.
- mSuiteStack.pop();
- mCurrentSuite = mSuiteStack.size() == 0 ? null : mSuiteStack.peek();
- }
-
- @Override
- public void fireTestStarted(Description description) throws StoppedByUserException {
- Log.i(TAG, "testStarted: " + description);
- mRealNotifier.fireTestStarted(description);
-
- mAfterTest = false;
- mBeforeTest = false;
- }
-
- @Override
- public void fireTestFailure(Failure failure) {
- Log.i(TAG, "testFailure: " + failure);
-
- if (isInTest()) {
- mRealNotifier.fireTestFailure(failure);
- } else {
- mOutOfTestFailures.add(failure.getException());
- }
- }
-
- @Override
- public void fireTestAssumptionFailed(Failure failure) {
- Log.i(TAG, "testAssumptionFailed: " + failure);
-
- if (isInTest()) {
- mRealNotifier.fireTestAssumptionFailed(failure);
- } else {
- mOutOfTestFailures.add(failure.getException());
- }
- }
-
- @Override
- public void fireTestIgnored(Description description) {
- Log.i(TAG, "testIgnored: " + description);
- mRealNotifier.fireTestIgnored(description);
- }
-
- @Override
- public void fireTestFinished(Description description) {
- Log.i(TAG, "testFinished: " + description);
- mRealNotifier.fireTestFinished(description);
-
- mAfterTest = true;
- }
-
- @Override
- public void pleaseStop() {
- Log.w(TAG, "pleaseStop:");
- mRealNotifier.pleaseStop();
- }
-
- /**
- * At the end of each Suite, we handle failures happened out of test methods.
- * (typically in @BeforeClass or @AfterClasses)
- *
- * This is to work around b/364395552.
- */
- private boolean maybeHandleOutOfTestFailures() {
- if (mOutOfTestFailures.size() == 0) {
- return false;
- }
- Throwable th;
- if (mOutOfTestFailures.size() == 1) {
- th = mOutOfTestFailures.get(0);
- } else {
- th = new MultipleFailureException(mOutOfTestFailures);
- }
- if (mBeforeTest) {
- reportBeforeTestFailure(mCurrentSuite, th);
- return true;
- }
- if (mAfterTest) {
- reportAfterTestFailure(th);
- return true;
- }
- return false;
- }
-
- public void reportBeforeTestFailure(Description suiteDesc, Throwable th) {
- // If a failure happens befere running any tests, we'll need to pretend
- // as if each test in the suite reported the failure, to work around b/364395552.
- for (var child : suiteDesc.getChildren()) {
- if (child.isSuite()) {
- // If the chiil is still a "parent" -- a test class or a test suite
- // -- propagate to its children.
- mRealNotifier.fireTestSuiteStarted(child);
- reportBeforeTestFailure(child, th);
- mRealNotifier.fireTestSuiteFinished(child);
- } else {
- mRealNotifier.fireTestStarted(child);
- Failure f = new Failure(child, th);
- if (th instanceof AssumptionViolatedException) {
- mRealNotifier.fireTestAssumptionFailed(f);
- } else {
- mRealNotifier.fireTestFailure(f);
- }
- mRealNotifier.fireTestFinished(child);
- }
- }
- }
-
- public void reportAfterTestFailure(Throwable th) {
- // Unfortunately, there's no good way to report it, so kill the own process.
- onCriticalError(
- "Failures detected in @AfterClass, which would be swallowed by tradefed",
- th);
- }
- }
-
- private static volatile BiConsumer<String, Throwable> sCriticalErrorHanler;
-
- private void onCriticalError(@NonNull String message, @Nullable Throwable th) {
- Log.e(TAG, "Critical error! " + message, th);
- var handler = sCriticalErrorHanler;
- if (handler == null) {
- handler = sDefaultCriticalErrorHandler;
- }
- handler.accept(message, th);
- }
-
- private static BiConsumer<String, Throwable> sDefaultCriticalErrorHandler = (message, th) -> {
- Log.e(TAG, "Ravenwood cannot continue. Killing self process.", th);
- System.exit(1);
- };
-
- /**
- * Contains Ravenwood private APIs.
- */
- public static class RavenwoodPrivate {
- private RavenwoodPrivate() {
- }
-
- /**
- * Set a listener for onCriticalError(), for testing. If a listener is set, we won't call
- * System.exit().
- */
- public void setCriticalErrorHandler(
- @Nullable BiConsumer<String, Throwable> handler) {
- sCriticalErrorHanler = handler;
- }
- }
-
- private static final RavenwoodPrivate sRavenwoodPrivate = new RavenwoodPrivate();
-
- public static RavenwoodPrivate private$ravenwood() {
- return sRavenwoodPrivate;
- }
-}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
new file mode 100644
index 0000000..31a1416
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.ravenwood;
+
+import android.platform.test.annotations.internal.InnerRunner;
+import android.util.Log;
+
+import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.Filterable;
+import org.junit.runner.manipulation.InvalidOrderingException;
+import org.junit.runner.manipulation.NoTestsRemainException;
+import org.junit.runner.manipulation.Orderable;
+import org.junit.runner.manipulation.Orderer;
+import org.junit.runner.manipulation.Sortable;
+import org.junit.runner.manipulation.Sorter;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.RunnerBuilder;
+import org.junit.runners.model.TestClass;
+
+abstract class RavenwoodAwareTestRunnerBase extends Runner implements Filterable, Orderable {
+ private static final String TAG = "Ravenwood";
+
+ boolean mRealRunnerTakesRunnerBuilder = false;
+
+ abstract Runner getRealRunner();
+
+ final Runner instantiateRealRunner(TestClass testClass) {
+ // Find the real runner.
+ final Class<? extends Runner> runnerClass;
+ final InnerRunner innerRunnerAnnotation = testClass.getAnnotation(InnerRunner.class);
+ if (innerRunnerAnnotation != null) {
+ runnerClass = innerRunnerAnnotation.value();
+ } else {
+ // Default runner.
+ runnerClass = BlockJUnit4ClassRunner.class;
+ }
+
+ try {
+ Log.i(TAG, "Initializing the inner runner: " + runnerClass);
+ try {
+ return runnerClass.getConstructor(Class.class)
+ .newInstance(testClass.getJavaClass());
+ } catch (NoSuchMethodException e) {
+ var constructor = runnerClass.getConstructor(Class.class, RunnerBuilder.class);
+ mRealRunnerTakesRunnerBuilder = true;
+ return constructor.newInstance(
+ testClass.getJavaClass(), new AllDefaultPossibilitiesBuilder());
+ }
+ } catch (ReflectiveOperationException e) {
+ throw logAndFail("Failed to instantiate " + runnerClass, e);
+ }
+ }
+
+ final Error logAndFail(String message, Throwable exception) {
+ Log.e(TAG, message, exception);
+ return new AssertionError(message, exception);
+ }
+
+ @Override
+ public final Description getDescription() {
+ return getRealRunner().getDescription();
+ }
+
+ @Override
+ public final void filter(Filter filter) throws NoTestsRemainException {
+ if (getRealRunner() instanceof Filterable r) {
+ r.filter(filter);
+ }
+ }
+
+ @Override
+ public final void order(Orderer orderer) throws InvalidOrderingException {
+ if (getRealRunner() instanceof Orderable r) {
+ r.order(orderer);
+ }
+ }
+
+ @Override
+ public final void sort(Sorter sorter) {
+ if (getRealRunner() instanceof Sortable r) {
+ r.sort(sorter);
+ }
+ }
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 93a6806..3d6ac0f 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -24,6 +24,8 @@
import android.content.Context;
import android.platform.test.annotations.DisabledOnRavenwood;
+import androidx.test.platform.app.InstrumentationRegistry;
+
import com.android.ravenwood.common.RavenwoodCommonUtils;
import org.junit.rules.TestRule;
@@ -219,8 +221,7 @@
*/
@Deprecated
public Context getContext() {
- return Objects.requireNonNull(mConfiguration.mInstContext,
- "Context is only available during @Test execution");
+ return InstrumentationRegistry.getInstrumentation().getContext();
}
/**
@@ -230,8 +231,7 @@
*/
@Deprecated
public Instrumentation getInstrumentation() {
- return Objects.requireNonNull(mConfiguration.mInstrumentation,
- "Instrumentation is only available during @Test execution");
+ return InstrumentationRegistry.getInstrumentation();
}
@Override
@@ -242,15 +242,11 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
- RavenwoodAwareTestRunnerHook.onRavenwoodRuleEnter(
- RavenwoodAwareTestRunner.getCurrentRunner(), description,
- RavenwoodRule.this);
+ RavenwoodAwareTestRunner.onRavenwoodRuleEnter(description, RavenwoodRule.this);
try {
base.evaluate();
} finally {
- RavenwoodAwareTestRunnerHook.onRavenwoodRuleExit(
- RavenwoodAwareTestRunner.getCurrentRunner(), description,
- RavenwoodRule.this);
+ RavenwoodAwareTestRunner.onRavenwoodRuleExit(description, RavenwoodRule.this);
}
}
};
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
new file mode 100644
index 0000000..b4b75178
--- /dev/null
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.ravenwood;
+
+import android.util.Log;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+/**
+ * A simple pass-through runner that just delegates to the inner runner without doing
+ * anything special (no hooks, etc.).
+ *
+ * This is only used when a real device-side test has Ravenizer enabled.
+ */
+public class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase {
+ private static final String TAG = "Ravenwood";
+
+ private static class NopRule implements TestRule {
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return base;
+ }
+ }
+
+ public static final TestRule sImplicitClassOuterRule = new NopRule();
+ public static final TestRule sImplicitClassInnerRule = sImplicitClassOuterRule;
+ public static final TestRule sImplicitInstOuterRule = sImplicitClassOuterRule;
+ public static final TestRule sImplicitInstInnerRule = sImplicitClassOuterRule;
+
+ private final Runner mRealRunner;
+
+ public RavenwoodAwareTestRunner(Class<?> clazz) {
+ Log.v(TAG, "RavenwoodAwareTestRunner starting for " + clazz.getCanonicalName());
+ mRealRunner = instantiateRealRunner(new TestClass(clazz));
+ }
+
+ @Override
+ Runner getRealRunner() {
+ return mRealRunner;
+ }
+
+ @Override
+ public void run(RunNotifier notifier) {
+ mRealRunner.run(notifier);
+ }
+
+ static void onRavenwoodRuleEnter(Description description, RavenwoodRule rule) {
+ }
+
+ static void onRavenwoodRuleExit(Description description, RavenwoodRule rule) {
+ }
+}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
deleted file mode 100644
index aa8c299..0000000
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.platform.test.ravenwood;
-
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Order;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Scope;
-
-import org.junit.runner.Description;
-import org.junit.runners.model.TestClass;
-
-/**
- * Provide hook points created by {@link RavenwoodAwareTestRunner}. This is a version
- * that's used on a device side test.
- *
- * All methods are no-op in real device tests.
- *
- * TODO: Use some kind of factory to provide different implementation for the device test
- * and the ravenwood test.
- */
-public class RavenwoodAwareTestRunnerHook {
- private RavenwoodAwareTestRunnerHook() {
- }
-
- /**
- * Called before any code starts. Internally it will only initialize the environment once.
- */
- public static void performGlobalInitialization() {
- }
-
- /**
- * Called when a runner starts, before the inner runner gets a chance to run.
- */
- public static void onRunnerInitializing(RavenwoodAwareTestRunner runner, TestClass testClass) {
- }
-
- /**
- * Called when a whole test class is skipped.
- */
- public static void onClassSkipped(Description description) {
- }
-
- /**
- * Called before the inner runner starts.
- */
- public static void onBeforeInnerRunnerStart(
- RavenwoodAwareTestRunner runner, Description description) throws Throwable {
- }
-
- /**
- * Called after the inner runner finished.
- */
- public static void onAfterInnerRunnerFinished(
- RavenwoodAwareTestRunner runner, Description description) throws Throwable {
- }
-
- /**
- * Called before a test / class.
- *
- * Return false if it should be skipped.
- */
- public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description,
- Scope scope, Order order) throws Throwable {
- return true;
- }
-
- public static void onRavenwoodRuleEnter(RavenwoodAwareTestRunner runner,
- Description description, RavenwoodRule rule) throws Throwable {
- }
-
- public static void onRavenwoodRuleExit(RavenwoodAwareTestRunner runner,
- Description description, RavenwoodRule rule) throws Throwable {
- }
-
-
- /**
- * Called after a test / class.
- *
- * Return false if the exception should be ignored.
- */
- public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description,
- Scope scope, Order order, Throwable th) {
- return true;
- }
-
- public static boolean shouldRunClassOnRavenwood(Class<?> clazz) {
- return true;
- }
-}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
index 43a28ba..7d3d8b9 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
@@ -15,7 +15,7 @@
*/
package android.platform.test.ravenwood;
-/** Stub class. The actual implementaetion is in junit-impl-src. */
+/** Stub class. The actual implementation is in junit-impl-src. */
public class RavenwoodConfigState {
public RavenwoodConfigState(RavenwoodConfig config) {
}
diff --git a/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java b/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java
deleted file mode 100644
index 1e3b3fc..0000000
--- a/ravenwood/runtime-helper-src/framework/android/util/StatsEvent.java
+++ /dev/null
@@ -1,1035 +0,0 @@
-/*
- * 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.util;
-
-// [ravenwood] This is an exact copy from StatsD, until we make StatsD available on Ravenwood.
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.os.Build;
-import android.os.SystemClock;
-
-import androidx.annotation.RequiresApi;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-/**
- * StatsEvent builds and stores the buffer sent over the statsd socket.
- * This class defines and encapsulates the socket protocol.
- *
- * <p>Usage:</p>
- * <pre>
- * // Pushed event
- * StatsEvent statsEvent = StatsEvent.newBuilder()
- * .setAtomId(atomId)
- * .writeBoolean(false)
- * .writeString("annotated String field")
- * .addBooleanAnnotation(annotationId, true)
- * .usePooledBuffer()
- * .build();
- * StatsLog.write(statsEvent);
- *
- * // Pulled event
- * StatsEvent statsEvent = StatsEvent.newBuilder()
- * .setAtomId(atomId)
- * .writeBoolean(false)
- * .writeString("annotated String field")
- * .addBooleanAnnotation(annotationId, true)
- * .build();
- * </pre>
- * @hide
- **/
-@SystemApi
-public final class StatsEvent {
- // Type Ids.
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_INT = 0x00;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_LONG = 0x01;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_STRING = 0x02;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_LIST = 0x03;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_FLOAT = 0x04;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_BOOLEAN = 0x05;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_BYTE_ARRAY = 0x06;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_OBJECT = 0x07;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_KEY_VALUE_PAIRS = 0x08;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_ATTRIBUTION_CHAIN = 0x09;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final byte TYPE_ERRORS = 0x0F;
-
- // Error flags.
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_NO_TIMESTAMP = 0x1;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_NO_ATOM_ID = 0x2;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_OVERFLOW = 0x4;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_ATTRIBUTION_CHAIN_TOO_LONG = 0x8;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_TOO_MANY_KEY_VALUE_PAIRS = 0x10;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD = 0x20;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_INVALID_ANNOTATION_ID = 0x40;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_ANNOTATION_ID_TOO_LARGE = 0x80;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_TOO_MANY_ANNOTATIONS = 0x100;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_TOO_MANY_FIELDS = 0x200;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x1000;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int ERROR_ATOM_ID_INVALID_POSITION = 0x2000;
-
- /**
- * @hide
- **/
- @VisibleForTesting public static final int ERROR_LIST_TOO_LONG = 0x4000;
-
- // Size limits.
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int MAX_ANNOTATION_COUNT = 15;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int MAX_ATTRIBUTION_NODES = 127;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int MAX_NUM_ELEMENTS = 127;
-
- /**
- * @hide
- **/
- @VisibleForTesting
- public static final int MAX_KEY_VALUE_PAIRS = 127;
-
- private static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;
-
- // Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag.
- // See android_util_StatsLog.cpp.
- private static final int MAX_PUSH_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
-
- private static final int MAX_PULL_PAYLOAD_SIZE = 50 * 1024; // 50 KB
-
- private final int mAtomId;
- private final byte[] mPayload;
- private Buffer mBuffer;
- private final int mNumBytes;
-
- private StatsEvent(final int atomId, @Nullable final Buffer buffer,
- @NonNull final byte[] payload, final int numBytes) {
- mAtomId = atomId;
- mBuffer = buffer;
- mPayload = payload;
- mNumBytes = numBytes;
- }
-
- /**
- * Returns a new StatsEvent.Builder for building StatsEvent object.
- **/
- @NonNull
- public static Builder newBuilder() {
- return new Builder(Buffer.obtain());
- }
-
- /**
- * Get the atom Id of the atom encoded in this StatsEvent object.
- *
- * @hide
- **/
- public int getAtomId() {
- return mAtomId;
- }
-
- /**
- * Get the byte array that contains the encoded payload that can be sent to statsd.
- *
- * @hide
- **/
- @NonNull
- public byte[] getBytes() {
- return mPayload;
- }
-
- /**
- * Get the number of bytes used to encode the StatsEvent payload.
- *
- * @hide
- **/
- public int getNumBytes() {
- return mNumBytes;
- }
-
- /**
- * Recycle resources used by this StatsEvent object.
- * No actions should be taken on this StatsEvent after release() is called.
- *
- * @hide
- **/
- public void release() {
- if (mBuffer != null) {
- mBuffer.release();
- mBuffer = null;
- }
- }
-
- /**
- * Builder for constructing a StatsEvent object.
- *
- * <p>This class defines and encapsulates the socket encoding for the
- *buffer. The write methods must be called in the same order as the order of
- *fields in the atom definition.</p>
- *
- * <p>setAtomId() must be called immediately after
- *StatsEvent.newBuilder().</p>
- *
- * <p>Example:</p>
- * <pre>
- * // Atom definition.
- * message MyAtom {
- * optional int32 field1 = 1;
- * optional int64 field2 = 2;
- * optional string field3 = 3 [(annotation1) = true];
- * optional repeated int32 field4 = 4;
- * }
- *
- * // StatsEvent construction for pushed event.
- * StatsEvent.newBuilder()
- * StatsEvent statsEvent = StatsEvent.newBuilder()
- * .setAtomId(atomId)
- * .writeInt(3) // field1
- * .writeLong(8L) // field2
- * .writeString("foo") // field 3
- * .addBooleanAnnotation(annotation1Id, true)
- * .writeIntArray({ 1, 2, 3 });
- * .usePooledBuffer()
- * .build();
- *
- * // StatsEvent construction for pulled event.
- * StatsEvent.newBuilder()
- * StatsEvent statsEvent = StatsEvent.newBuilder()
- * .setAtomId(atomId)
- * .writeInt(3) // field1
- * .writeLong(8L) // field2
- * .writeString("foo") // field 3
- * .addBooleanAnnotation(annotation1Id, true)
- * .writeIntArray({ 1, 2, 3 });
- * .build();
- * </pre>
- **/
- public static final class Builder {
- // Fixed positions.
- private static final int POS_NUM_ELEMENTS = 1;
- private static final int POS_TIMESTAMP_NS = POS_NUM_ELEMENTS + Byte.BYTES;
- private static final int POS_ATOM_ID = POS_TIMESTAMP_NS + Byte.BYTES + Long.BYTES;
-
- private final Buffer mBuffer;
- private long mTimestampNs;
- private int mAtomId;
- private byte mCurrentAnnotationCount;
- private int mPos;
- private int mPosLastField;
- private byte mLastType;
- private int mNumElements;
- private int mErrorMask;
- private boolean mUsePooledBuffer = false;
-
- private Builder(final Buffer buffer) {
- mBuffer = buffer;
- mCurrentAnnotationCount = 0;
- mAtomId = 0;
- mTimestampNs = SystemClock.elapsedRealtimeNanos();
- mNumElements = 0;
-
- // Set mPos to 0 for writing TYPE_OBJECT at 0th position.
- mPos = 0;
- writeTypeId(TYPE_OBJECT);
-
- // Write timestamp.
- mPos = POS_TIMESTAMP_NS;
- writeLong(mTimestampNs);
- }
-
- /**
- * Sets the atom id for this StatsEvent.
- *
- * This should be called immediately after StatsEvent.newBuilder()
- * and should only be called once.
- * Not calling setAtomId will result in ERROR_NO_ATOM_ID.
- * Calling setAtomId out of order will result in ERROR_ATOM_ID_INVALID_POSITION.
- **/
- @NonNull
- public Builder setAtomId(final int atomId) {
- if (0 == mAtomId) {
- mAtomId = atomId;
-
- if (1 == mNumElements) { // Only timestamp is written so far.
- writeInt(atomId);
- } else {
- // setAtomId called out of order.
- mErrorMask |= ERROR_ATOM_ID_INVALID_POSITION;
- }
- }
-
- return this;
- }
-
- /**
- * Write a boolean field to this StatsEvent.
- **/
- @NonNull
- public Builder writeBoolean(final boolean value) {
- // Write boolean typeId byte followed by boolean byte representation.
- writeTypeId(TYPE_BOOLEAN);
- mPos += mBuffer.putBoolean(mPos, value);
- mNumElements++;
- return this;
- }
-
- /**
- * Write an integer field to this StatsEvent.
- **/
- @NonNull
- public Builder writeInt(final int value) {
- // Write integer typeId byte followed by 4-byte representation of value.
- writeTypeId(TYPE_INT);
- mPos += mBuffer.putInt(mPos, value);
- mNumElements++;
- return this;
- }
-
- /**
- * Write a long field to this StatsEvent.
- **/
- @NonNull
- public Builder writeLong(final long value) {
- // Write long typeId byte followed by 8-byte representation of value.
- writeTypeId(TYPE_LONG);
- mPos += mBuffer.putLong(mPos, value);
- mNumElements++;
- return this;
- }
-
- /**
- * Write a float field to this StatsEvent.
- **/
- @NonNull
- public Builder writeFloat(final float value) {
- // Write float typeId byte followed by 4-byte representation of value.
- writeTypeId(TYPE_FLOAT);
- mPos += mBuffer.putFloat(mPos, value);
- mNumElements++;
- return this;
- }
-
- /**
- * Write a String field to this StatsEvent.
- **/
- @NonNull
- public Builder writeString(@NonNull final String value) {
- // Write String typeId byte, followed by 4-byte representation of number of bytes
- // in the UTF-8 encoding, followed by the actual UTF-8 byte encoding of value.
- final byte[] valueBytes = stringToBytes(value);
- writeByteArray(valueBytes, TYPE_STRING);
- return this;
- }
-
- /**
- * Write a byte array field to this StatsEvent.
- **/
- @NonNull
- public Builder writeByteArray(@NonNull final byte[] value) {
- // Write byte array typeId byte, followed by 4-byte representation of number of bytes
- // in value, followed by the actual byte array.
- writeByteArray(value, TYPE_BYTE_ARRAY);
- return this;
- }
-
- private void writeByteArray(@NonNull final byte[] value, final byte typeId) {
- writeTypeId(typeId);
- final int numBytes = value.length;
- mPos += mBuffer.putInt(mPos, numBytes);
- mPos += mBuffer.putByteArray(mPos, value);
- mNumElements++;
- }
-
- /**
- * Write an attribution chain field to this StatsEvent.
- *
- * The sizes of uids and tags must be equal. The AttributionNode at position i is
- * made up of uids[i] and tags[i].
- *
- * @param uids array of uids in the attribution nodes.
- * @param tags array of tags in the attribution nodes.
- **/
- @NonNull
- public Builder writeAttributionChain(
- @NonNull final int[] uids, @NonNull final String[] tags) {
- final byte numUids = (byte) uids.length;
- final byte numTags = (byte) tags.length;
-
- if (numUids != numTags) {
- mErrorMask |= ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL;
- } else if (numUids > MAX_ATTRIBUTION_NODES) {
- mErrorMask |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG;
- } else {
- // Write attribution chain typeId byte, followed by 1-byte representation of
- // number of attribution nodes, followed by encoding of each attribution node.
- writeTypeId(TYPE_ATTRIBUTION_CHAIN);
- mPos += mBuffer.putByte(mPos, numUids);
- for (int i = 0; i < numUids; i++) {
- // Each uid is encoded as 4-byte representation of its int value.
- mPos += mBuffer.putInt(mPos, uids[i]);
-
- // Each tag is encoded as 4-byte representation of number of bytes in its
- // UTF-8 encoding, followed by the actual UTF-8 bytes.
- final byte[] tagBytes = stringToBytes(tags[i]);
- mPos += mBuffer.putInt(mPos, tagBytes.length);
- mPos += mBuffer.putByteArray(mPos, tagBytes);
- }
- mNumElements++;
- }
- return this;
- }
-
- /**
- * Write KeyValuePairsAtom entries to this StatsEvent.
- *
- * @param intMap Integer key-value pairs.
- * @param longMap Long key-value pairs.
- * @param stringMap String key-value pairs.
- * @param floatMap Float key-value pairs.
- **/
- @NonNull
- public Builder writeKeyValuePairs(
- @Nullable final SparseIntArray intMap,
- @Nullable final SparseLongArray longMap,
- @Nullable final SparseArray<String> stringMap,
- @Nullable final SparseArray<Float> floatMap) {
- final int intMapSize = null == intMap ? 0 : intMap.size();
- final int longMapSize = null == longMap ? 0 : longMap.size();
- final int stringMapSize = null == stringMap ? 0 : stringMap.size();
- final int floatMapSize = null == floatMap ? 0 : floatMap.size();
- final int totalCount = intMapSize + longMapSize + stringMapSize + floatMapSize;
-
- if (totalCount > MAX_KEY_VALUE_PAIRS) {
- mErrorMask |= ERROR_TOO_MANY_KEY_VALUE_PAIRS;
- } else {
- writeTypeId(TYPE_KEY_VALUE_PAIRS);
- mPos += mBuffer.putByte(mPos, (byte) totalCount);
-
- for (int i = 0; i < intMapSize; i++) {
- final int key = intMap.keyAt(i);
- final int value = intMap.valueAt(i);
- mPos += mBuffer.putInt(mPos, key);
- writeTypeId(TYPE_INT);
- mPos += mBuffer.putInt(mPos, value);
- }
-
- for (int i = 0; i < longMapSize; i++) {
- final int key = longMap.keyAt(i);
- final long value = longMap.valueAt(i);
- mPos += mBuffer.putInt(mPos, key);
- writeTypeId(TYPE_LONG);
- mPos += mBuffer.putLong(mPos, value);
- }
-
- for (int i = 0; i < stringMapSize; i++) {
- final int key = stringMap.keyAt(i);
- final String value = stringMap.valueAt(i);
- mPos += mBuffer.putInt(mPos, key);
- writeTypeId(TYPE_STRING);
- final byte[] valueBytes = stringToBytes(value);
- mPos += mBuffer.putInt(mPos, valueBytes.length);
- mPos += mBuffer.putByteArray(mPos, valueBytes);
- }
-
- for (int i = 0; i < floatMapSize; i++) {
- final int key = floatMap.keyAt(i);
- final float value = floatMap.valueAt(i);
- mPos += mBuffer.putInt(mPos, key);
- writeTypeId(TYPE_FLOAT);
- mPos += mBuffer.putFloat(mPos, value);
- }
-
- mNumElements++;
- }
-
- return this;
- }
-
- /**
- * Write a repeated boolean field to this StatsEvent.
- *
- * The list size must not exceed 127. Otherwise, the array isn't written
- * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the
- * StatsEvent errors field.
- *
- * @param elements array of booleans.
- **/
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- @NonNull
- public Builder writeBooleanArray(@NonNull final boolean[] elements) {
- final byte numElements = (byte)elements.length;
-
- if (writeArrayInfo(numElements, TYPE_BOOLEAN)) {
- // Write encoding of each element.
- for (int i = 0; i < numElements; i++) {
- mPos += mBuffer.putBoolean(mPos, elements[i]);
- }
- mNumElements++;
- }
- return this;
- }
-
- /**
- * Write a repeated int field to this StatsEvent.
- *
- * The list size must not exceed 127. Otherwise, the array isn't written
- * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the
- * StatsEvent errors field.
- *
- * @param elements array of ints.
- **/
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- @NonNull
- public Builder writeIntArray(@NonNull final int[] elements) {
- final byte numElements = (byte)elements.length;
-
- if (writeArrayInfo(numElements, TYPE_INT)) {
- // Write encoding of each element.
- for (int i = 0; i < numElements; i++) {
- mPos += mBuffer.putInt(mPos, elements[i]);
- }
- mNumElements++;
- }
- return this;
- }
-
- /**
- * Write a repeated long field to this StatsEvent.
- *
- * The list size must not exceed 127. Otherwise, the array isn't written
- * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the
- * StatsEvent errors field.
- *
- * @param elements array of longs.
- **/
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- @NonNull
- public Builder writeLongArray(@NonNull final long[] elements) {
- final byte numElements = (byte)elements.length;
-
- if (writeArrayInfo(numElements, TYPE_LONG)) {
- // Write encoding of each element.
- for (int i = 0; i < numElements; i++) {
- mPos += mBuffer.putLong(mPos, elements[i]);
- }
- mNumElements++;
- }
- return this;
- }
-
- /**
- * Write a repeated float field to this StatsEvent.
- *
- * The list size must not exceed 127. Otherwise, the array isn't written
- * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the
- * StatsEvent errors field.
- *
- * @param elements array of floats.
- **/
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- @NonNull
- public Builder writeFloatArray(@NonNull final float[] elements) {
- final byte numElements = (byte)elements.length;
-
- if (writeArrayInfo(numElements, TYPE_FLOAT)) {
- // Write encoding of each element.
- for (int i = 0; i < numElements; i++) {
- mPos += mBuffer.putFloat(mPos, elements[i]);
- }
- mNumElements++;
- }
- return this;
- }
-
- /**
- * Write a repeated string field to this StatsEvent.
- *
- * The list size must not exceed 127. Otherwise, the array isn't written
- * to the StatsEvent and ERROR_LIST_TOO_LONG is appended to the
- * StatsEvent errors field.
- *
- * @param elements array of strings.
- **/
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- @NonNull
- public Builder writeStringArray(@NonNull final String[] elements) {
- final byte numElements = (byte)elements.length;
-
- if (writeArrayInfo(numElements, TYPE_STRING)) {
- // Write encoding of each element.
- for (int i = 0; i < numElements; i++) {
- final byte[] elementBytes = stringToBytes(elements[i]);
- mPos += mBuffer.putInt(mPos, elementBytes.length);
- mPos += mBuffer.putByteArray(mPos, elementBytes);
- }
- mNumElements++;
- }
- return this;
- }
-
- /**
- * Write a boolean annotation for the last field written.
- **/
- @NonNull
- public Builder addBooleanAnnotation(
- final byte annotationId, final boolean value) {
- // Ensure there's a field written to annotate.
- if (mNumElements < 2) {
- mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
- } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) {
- mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS;
- } else {
- mPos += mBuffer.putByte(mPos, annotationId);
- mPos += mBuffer.putByte(mPos, TYPE_BOOLEAN);
- mPos += mBuffer.putBoolean(mPos, value);
- mCurrentAnnotationCount++;
- writeAnnotationCount();
- }
-
- return this;
- }
-
- /**
- * Write an integer annotation for the last field written.
- **/
- @NonNull
- public Builder addIntAnnotation(final byte annotationId, final int value) {
- if (mNumElements < 2) {
- mErrorMask |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
- } else if (mCurrentAnnotationCount >= MAX_ANNOTATION_COUNT) {
- mErrorMask |= ERROR_TOO_MANY_ANNOTATIONS;
- } else {
- mPos += mBuffer.putByte(mPos, annotationId);
- mPos += mBuffer.putByte(mPos, TYPE_INT);
- mPos += mBuffer.putInt(mPos, value);
- mCurrentAnnotationCount++;
- writeAnnotationCount();
- }
-
- return this;
- }
-
- /**
- * Indicates to reuse Buffer's byte array as the underlying payload in StatsEvent.
- * This should be called for pushed events to reduce memory allocations and garbage
- * collections.
- **/
- @NonNull
- public Builder usePooledBuffer() {
- mUsePooledBuffer = true;
- mBuffer.setMaxSize(MAX_PUSH_PAYLOAD_SIZE, mPos);
- return this;
- }
-
- /**
- * Builds a StatsEvent object with values entered in this Builder.
- **/
- @NonNull
- public StatsEvent build() {
- if (0L == mTimestampNs) {
- mErrorMask |= ERROR_NO_TIMESTAMP;
- }
- if (0 == mAtomId) {
- mErrorMask |= ERROR_NO_ATOM_ID;
- }
- if (mBuffer.hasOverflowed()) {
- mErrorMask |= ERROR_OVERFLOW;
- }
- if (mNumElements > MAX_NUM_ELEMENTS) {
- mErrorMask |= ERROR_TOO_MANY_FIELDS;
- }
-
- if (0 == mErrorMask) {
- mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements);
- } else {
- // Write atom id and error mask. Overwrite any annotations for atom Id.
- mPos = POS_ATOM_ID;
- mPos += mBuffer.putByte(mPos, TYPE_INT);
- mPos += mBuffer.putInt(mPos, mAtomId);
- mPos += mBuffer.putByte(mPos, TYPE_ERRORS);
- mPos += mBuffer.putInt(mPos, mErrorMask);
- mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3);
- }
-
- final int size = mPos;
-
- if (mUsePooledBuffer) {
- return new StatsEvent(mAtomId, mBuffer, mBuffer.getBytes(), size);
- } else {
- // Create a copy of the buffer with the required number of bytes.
- final byte[] payload = new byte[size];
- System.arraycopy(mBuffer.getBytes(), 0, payload, 0, size);
-
- // Return Buffer instance to the pool.
- mBuffer.release();
-
- return new StatsEvent(mAtomId, null, payload, size);
- }
- }
-
- private void writeTypeId(final byte typeId) {
- mPosLastField = mPos;
- mLastType = typeId;
- mCurrentAnnotationCount = 0;
- final byte encodedId = (byte) (typeId & 0x0F);
- mPos += mBuffer.putByte(mPos, encodedId);
- }
-
- private void writeAnnotationCount() {
- // Use first 4 bits for annotation count and last 4 bits for typeId.
- final byte encodedId = (byte) ((mCurrentAnnotationCount << 4) | (mLastType & 0x0F));
- mBuffer.putByte(mPosLastField, encodedId);
- }
-
- @NonNull
- private static byte[] stringToBytes(@Nullable final String value) {
- return (null == value ? "" : value).getBytes(UTF_8);
- }
-
- private boolean writeArrayInfo(final byte numElements,
- final byte elementTypeId) {
- if (numElements > MAX_NUM_ELEMENTS) {
- mErrorMask |= ERROR_LIST_TOO_LONG;
- return false;
- }
- // Write list typeId byte, 1-byte representation of number of
- // elements, and element typeId byte.
- writeTypeId(TYPE_LIST);
- mPos += mBuffer.putByte(mPos, numElements);
- // Write element typeId byte without setting mPosLastField and mLastType (i.e. don't use
- // #writeTypeId)
- final byte encodedId = (byte) (elementTypeId & 0x0F);
- mPos += mBuffer.putByte(mPos, encodedId);
- return true;
- }
- }
-
- private static final class Buffer {
- private static Object sLock = new Object();
-
- @GuardedBy("sLock")
- private static Buffer sPool;
-
- private byte[] mBytes;
- private boolean mOverflow = false;
- private int mMaxSize = MAX_PULL_PAYLOAD_SIZE;
-
- @NonNull
- private static Buffer obtain() {
- final Buffer buffer;
- synchronized (sLock) {
- buffer = null == sPool ? new Buffer() : sPool;
- sPool = null;
- }
- buffer.reset();
- return buffer;
- }
-
- private Buffer() {
- final ByteBuffer tempBuffer = ByteBuffer.allocateDirect(MAX_PUSH_PAYLOAD_SIZE);
- mBytes = tempBuffer.hasArray() ? tempBuffer.array() : new byte [MAX_PUSH_PAYLOAD_SIZE];
- }
-
- @NonNull
- private byte[] getBytes() {
- return mBytes;
- }
-
- private void release() {
- // Recycle this Buffer if its size is MAX_PUSH_PAYLOAD_SIZE or under.
- if (mMaxSize <= MAX_PUSH_PAYLOAD_SIZE) {
- synchronized (sLock) {
- if (null == sPool) {
- sPool = this;
- }
- }
- }
- }
-
- private void reset() {
- mOverflow = false;
- mMaxSize = MAX_PULL_PAYLOAD_SIZE;
- }
-
- private void setMaxSize(final int maxSize, final int numBytesWritten) {
- mMaxSize = maxSize;
- if (numBytesWritten > maxSize) {
- mOverflow = true;
- }
- }
-
- private boolean hasOverflowed() {
- return mOverflow;
- }
-
- /**
- * Checks for available space in the byte array.
- *
- * @param index starting position in the buffer to start the check.
- * @param numBytes number of bytes to check from index.
- * @return true if space is available, false otherwise.
- **/
- private boolean hasEnoughSpace(final int index, final int numBytes) {
- final int totalBytesNeeded = index + numBytes;
-
- if (totalBytesNeeded > mMaxSize) {
- mOverflow = true;
- return false;
- }
-
- // Expand buffer if needed.
- if (mBytes.length < mMaxSize && totalBytesNeeded > mBytes.length) {
- int newSize = mBytes.length;
- do {
- newSize *= 2;
- } while (newSize <= totalBytesNeeded);
-
- if (newSize > mMaxSize) {
- newSize = mMaxSize;
- }
-
- mBytes = Arrays.copyOf(mBytes, newSize);
- }
-
- return true;
- }
-
- /**
- * Writes a byte into the buffer.
- *
- * @param index position in the buffer where the byte is written.
- * @param value the byte to write.
- * @return number of bytes written to buffer from this write operation.
- **/
- private int putByte(final int index, final byte value) {
- if (hasEnoughSpace(index, Byte.BYTES)) {
- mBytes[index] = (byte) (value);
- return Byte.BYTES;
- }
- return 0;
- }
-
- /**
- * Writes a boolean into the buffer.
- *
- * @param index position in the buffer where the boolean is written.
- * @param value the boolean to write.
- * @return number of bytes written to buffer from this write operation.
- **/
- private int putBoolean(final int index, final boolean value) {
- return putByte(index, (byte) (value ? 1 : 0));
- }
-
- /**
- * Writes an integer into the buffer.
- *
- * @param index position in the buffer where the integer is written.
- * @param value the integer to write.
- * @return number of bytes written to buffer from this write operation.
- **/
- private int putInt(final int index, final int value) {
- if (hasEnoughSpace(index, Integer.BYTES)) {
- // Use little endian byte order.
- mBytes[index] = (byte) (value);
- mBytes[index + 1] = (byte) (value >> 8);
- mBytes[index + 2] = (byte) (value >> 16);
- mBytes[index + 3] = (byte) (value >> 24);
- return Integer.BYTES;
- }
- return 0;
- }
-
- /**
- * Writes a long into the buffer.
- *
- * @param index position in the buffer where the long is written.
- * @param value the long to write.
- * @return number of bytes written to buffer from this write operation.
- **/
- private int putLong(final int index, final long value) {
- if (hasEnoughSpace(index, Long.BYTES)) {
- // Use little endian byte order.
- mBytes[index] = (byte) (value);
- mBytes[index + 1] = (byte) (value >> 8);
- mBytes[index + 2] = (byte) (value >> 16);
- mBytes[index + 3] = (byte) (value >> 24);
- mBytes[index + 4] = (byte) (value >> 32);
- mBytes[index + 5] = (byte) (value >> 40);
- mBytes[index + 6] = (byte) (value >> 48);
- mBytes[index + 7] = (byte) (value >> 56);
- return Long.BYTES;
- }
- return 0;
- }
-
- /**
- * Writes a float into the buffer.
- *
- * @param index position in the buffer where the float is written.
- * @param value the float to write.
- * @return number of bytes written to buffer from this write operation.
- **/
- private int putFloat(final int index, final float value) {
- return putInt(index, Float.floatToIntBits(value));
- }
-
- /**
- * Copies a byte array into the buffer.
- *
- * @param index position in the buffer where the byte array is copied.
- * @param value the byte array to copy.
- * @return number of bytes written to buffer from this write operation.
- **/
- private int putByteArray(final int index, @NonNull final byte[] value) {
- final int numBytes = value.length;
- if (hasEnoughSpace(index, numBytes)) {
- System.arraycopy(value, 0, mBytes, index, numBytes);
- return numBytes;
- }
- return 0;
- }
- }
-}
diff --git a/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java b/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java
deleted file mode 100644
index c1c20cf..0000000
--- a/ravenwood/runtime-helper-src/framework/android/util/StatsLog.java
+++ /dev/null
@@ -1,478 +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.util;
-
-/*
- * [Ravenwood] This is copied from StatsD, with the following changes:
- * - The static {} is commented out.
- * - All references to IStatsD and StatsdStatsLog are commented out.
- * - The native method is no-oped.
- */
-
-import static android.Manifest.permission.DUMP;
-import static android.Manifest.permission.PACKAGE_USAGE_STATS;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.os.Build;
-//import android.os.IStatsd;
-import android.os.Process;
-import android.util.proto.ProtoOutputStream;
-
-import androidx.annotation.RequiresApi;
-
-//import com.android.internal.statsd.StatsdStatsLog;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * StatsLog provides an API for developers to send events to statsd. The events can be used to
- * define custom metrics in side statsd.
- */
-public final class StatsLog {
-
-// // Load JNI library
-// static {
-// System.loadLibrary("stats_jni");
-// }
- private static final String TAG = "StatsLog";
- private static final boolean DEBUG = false;
- private static final int EXPERIMENT_IDS_FIELD_ID = 1;
-
- /**
- * Annotation ID constant for logging UID field.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- public static final byte ANNOTATION_ID_IS_UID = 1;
-
- /**
- * Annotation ID constant to indicate logged atom event's timestamp should be truncated.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- public static final byte ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2;
-
- /**
- * Annotation ID constant for a state atom's primary field.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- public static final byte ANNOTATION_ID_PRIMARY_FIELD = 3;
-
- /**
- * Annotation ID constant for state atom's state field.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- public static final byte ANNOTATION_ID_EXCLUSIVE_STATE = 4;
-
- /**
- * Annotation ID constant to indicate the first UID in the attribution chain
- * is a primary field.
- * Should only be used for attribution chain fields.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- public static final byte ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5;
-
- /**
- * Annotation ID constant to indicate which state is default for the state atom.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- public static final byte ANNOTATION_ID_DEFAULT_STATE = 6;
-
- /**
- * Annotation ID constant to signal all states should be reset to the default state.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- public static final byte ANNOTATION_ID_TRIGGER_STATE_RESET = 7;
-
- /**
- * Annotation ID constant to indicate state changes need to account for nesting.
- * This should only be used with binary state atoms.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- public static final byte ANNOTATION_ID_STATE_NESTED = 8;
-
- /**
- * Annotation ID constant to indicate the restriction category of an atom.
- * This annotation must only be attached to the atom id. This is an int annotation.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final byte ANNOTATION_ID_RESTRICTION_CATEGORY = 9;
-
- /**
- * Annotation ID to indicate that a field of an atom contains peripheral device info.
- * This is a bool annotation.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final byte ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO = 10;
-
- /**
- * Annotation ID to indicate that a field of an atom contains app usage information.
- * This is a bool annotation.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE = 11;
-
- /**
- * Annotation ID to indicate that a field of an atom contains app activity information.
- * This is a bool annotation.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY = 12;
-
- /**
- * Annotation ID to indicate that a field of an atom contains health connect information.
- * This is a bool annotation.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final byte ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT = 13;
-
- /**
- * Annotation ID to indicate that a field of an atom contains accessibility information.
- * This is a bool annotation.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final byte ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY = 14;
-
- /**
- * Annotation ID to indicate that a field of an atom contains system search information.
- * This is a bool annotation.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final byte ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH = 15;
-
- /**
- * Annotation ID to indicate that a field of an atom contains user engagement information.
- * This is a bool annotation.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final byte ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT = 16;
-
- /**
- * Annotation ID to indicate that a field of an atom contains ambient sensing information.
- * This is a bool annotation.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final byte ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING = 17;
-
- /**
- * Annotation ID to indicate that a field of an atom contains demographic classification
- * information. This is a bool annotation.
- *
- * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation()
- * accept byte as the type for annotation ids to save space.
- *
- * @hide
- */
- @SuppressLint("NoByteOrShort")
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final byte ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION = 18;
-
-
- /** @hide */
- @IntDef(prefix = { "RESTRICTION_CATEGORY_" }, value = {
- RESTRICTION_CATEGORY_DIAGNOSTIC,
- RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE,
- RESTRICTION_CATEGORY_AUTHENTICATION,
- RESTRICTION_CATEGORY_FRAUD_AND_ABUSE})
- @Retention(RetentionPolicy.SOURCE)
- public @interface RestrictionCategory {}
-
- /**
- * Restriction category for atoms about diagnostics.
- *
- * @hide
- */
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final int RESTRICTION_CATEGORY_DIAGNOSTIC = 1;
-
- /**
- * Restriction category for atoms about system intelligence.
- *
- * @hide
- */
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final int RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE = 2;
-
- /**
- * Restriction category for atoms about authentication.
- *
- * @hide
- */
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final int RESTRICTION_CATEGORY_AUTHENTICATION = 3;
-
- /**
- * Restriction category for atoms about fraud and abuse.
- *
- * @hide
- */
- @SystemApi
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public static final int RESTRICTION_CATEGORY_FRAUD_AND_ABUSE = 4;
-
- private StatsLog() {
- }
-
- /**
- * Logs a start event.
- *
- * @param label developer-chosen label.
- * @return True if the log request was sent to statsd.
- */
- public static boolean logStart(int label) {
- int callingUid = Process.myUid();
-// StatsdStatsLog.write(
-// StatsdStatsLog.APP_BREADCRUMB_REPORTED,
-// callingUid,
-// label,
-// StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__START);
- return true;
- }
-
- /**
- * Logs a stop event.
- *
- * @param label developer-chosen label.
- * @return True if the log request was sent to statsd.
- */
- public static boolean logStop(int label) {
- int callingUid = Process.myUid();
-// StatsdStatsLog.write(
-// StatsdStatsLog.APP_BREADCRUMB_REPORTED,
-// callingUid,
-// label,
-// StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
- return true;
- }
-
- /**
- * Logs an event that does not represent a start or stop boundary.
- *
- * @param label developer-chosen label.
- * @return True if the log request was sent to statsd.
- */
- public static boolean logEvent(int label) {
- int callingUid = Process.myUid();
-// StatsdStatsLog.write(
-// StatsdStatsLog.APP_BREADCRUMB_REPORTED,
-// callingUid,
-// label,
-// StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
- return true;
- }
-
- /**
- * Logs an event for binary push for module updates.
- *
- * @param trainName name of install train.
- * @param trainVersionCode version code of the train.
- * @param options optional flags about this install.
- * The last 3 bits indicate options:
- * 0x01: FLAG_REQUIRE_STAGING
- * 0x02: FLAG_ROLLBACK_ENABLED
- * 0x04: FLAG_REQUIRE_LOW_LATENCY_MONITOR
- * @param state current install state. Defined as State enums in
- * BinaryPushStateChanged atom in
- * frameworks/proto_logging/stats/atoms.proto
- * @param experimentIds experiment ids.
- * @return True if the log request was sent to statsd.
- */
- @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
- public static boolean logBinaryPushStateChanged(@NonNull String trainName,
- long trainVersionCode, int options, int state,
- @NonNull long[] experimentIds) {
- ProtoOutputStream proto = new ProtoOutputStream();
- for (long id : experimentIds) {
- proto.write(
- ProtoOutputStream.FIELD_TYPE_INT64
- | ProtoOutputStream.FIELD_COUNT_REPEATED
- | EXPERIMENT_IDS_FIELD_ID,
- id);
- }
-// StatsdStatsLog.write(StatsdStatsLog.BINARY_PUSH_STATE_CHANGED,
-// trainName,
-// trainVersionCode,
-// (options & IStatsd.FLAG_REQUIRE_STAGING) > 0,
-// (options & IStatsd.FLAG_ROLLBACK_ENABLED) > 0,
-// (options & IStatsd.FLAG_REQUIRE_LOW_LATENCY_MONITOR) > 0,
-// state,
-// proto.getBytes(),
-// 0,
-// 0,
-// false);
- return true;
- }
-
- /**
- * Write an event to stats log using the raw format.
- *
- * @param buffer The encoded buffer of data to write.
- * @param size The number of bytes from the buffer to write.
- * @hide
- * @deprecated Use {@link write(final StatsEvent statsEvent)} instead.
- *
- */
- @Deprecated
- @SystemApi
- public static void writeRaw(@NonNull byte[] buffer, int size) {
- writeImpl(buffer, size, 0);
- }
-
- /**
- * Write an event to stats log using the raw format.
- *
- * @param buffer The encoded buffer of data to write.
- * @param size The number of bytes from the buffer to write.
- * @param atomId The id of the atom to which the event belongs.
- */
-// private static native void writeImpl(@NonNull byte[] buffer, int size, int atomId);
- private static void writeImpl(@NonNull byte[] buffer, int size, int atomId) {
- // no-op for now
- }
-
- /**
- * Write an event to stats log using the raw format encapsulated in StatsEvent.
- * After writing to stats log, release() is called on the StatsEvent object.
- * No further action should be taken on the StatsEvent object following this call.
- *
- * @param statsEvent The StatsEvent object containing the encoded buffer of data to write.
- * @hide
- */
- @SystemApi
- public static void write(@NonNull final StatsEvent statsEvent) {
- writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId());
- statsEvent.release();
- }
-}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index 3ff0848..2a3c26e 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -173,6 +173,24 @@
throwIfMinusOne(env, "setenv", setenv(name.c_str(), value.c_str(), overwrite ? 1 : 0));
}
+static void maybeRedirectLog() {
+ auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
+ if (ravenwoodLogOut == NULL) {
+ return;
+ }
+ ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut);
+
+ // Redirect stdin / stdout to /dev/tty.
+ int ttyFd = open(ravenwoodLogOut, O_WRONLY);
+ if (ttyFd == -1) {
+ ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut,
+ strerror(errno));
+ return;
+ }
+ dup2(ttyFd, 1);
+ dup2(ttyFd, 2);
+}
+
// ---- Registration ----
extern void register_android_system_OsConstants(JNIEnv* env);
@@ -192,6 +210,8 @@
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
+ maybeRedirectLog();
+
ALOGI("%s: JNI_OnLoad", __FILE__);
JNIEnv* env = GetJNIEnvOrDie(vm);
diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh
index 36601bd..b83216a 100755
--- a/ravenwood/scripts/ravenwood-stats-collector.sh
+++ b/ravenwood/scripts/ravenwood-stats-collector.sh
@@ -62,6 +62,8 @@
dump "framework-minus-apex" hoststubgen_framework-minus-apex_stats.csv
dump "service.core" hoststubgen_services.core_stats.csv
+ dump "framework-configinfrastructure" framework-configinfrastructure_stats.csv
+ dump "framework-statsd" framework-statsd_stats.csv
} > "$out"
echo "Stats CVS created at $out"
@@ -76,6 +78,8 @@
dump "framework-minus-apex" hoststubgen_framework-minus-apex_apis.csv
dump "service.core" hoststubgen_services.core_apis.csv
+ dump "framework-configinfrastructure" framework-configinfrastructure_apis.csv
+ dump "framework-statsd" framework-statsd_apis.csv
} > "$out"
echo "API CVS created at $out"
diff --git a/ravenwood/scripts/ravenwood-test-summary b/ravenwood/scripts/ravenwood-test-summary
new file mode 100755
index 0000000..602fbff
--- /dev/null
+++ b/ravenwood/scripts/ravenwood-test-summary
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+'''
+Print the latest Ravenwood test execution summary
+
+Usage: /ravenwood-test-summary
+
+Example output:
+Module Passed Failed Skipped
+android.test.mock.ravenwood.tests 2 0 0
+CarLibHostUnitTest 565 0 7
+CarServiceHostUnitTest 364 0 0
+CtsAccountManagerTestCasesRavenwood 4 0 0
+CtsAppTestCasesRavenwood 21 0 0
+
+Description:
+This script finds all the test execution result from /tmp/Ravenwood-stats*,
+and shows per-module summary.
+'''
+
+import csv
+import glob
+import sys
+
+# Find the latest stats files.
+stats_files = glob.glob('/tmp/Ravenwood-stats_*_latest.csv')
+
+if len(stats_files) == 0:
+ print("No log files found.", file=sys.stderr)
+ exit(1)
+
+
+def parse_stats(file, result):
+ module = '(unknwon)'
+ passed = 0
+ failed = 0
+ skipped = 0
+ with open(file) as csvfile:
+ reader = csv.reader(csvfile, delimiter=',')
+
+
+ for i, row in enumerate(reader):
+ if i == 0: continue # Skip header line
+ module = row[0]
+ passed += int(row[3])
+ failed += int(row[4])
+ skipped += int(row[5])
+
+ result[module] = (passed, failed, skipped)
+
+
+result = {}
+
+for file in stats_files:
+ parse_stats(file, result)
+
+print('%-60s %8s %8s %8s' % ("Module", "Passed", "Failed", "Skipped"))
+
+for module in sorted(result.keys(), key=str.casefold):
+ r = result[module]
+ print('%-60s %8d %8d %8d' % (module, r[0], r[1], r[2]))
diff --git a/ravenwood/scripts/run-ravenwood-tests.sh b/ravenwood/scripts/run-ravenwood-tests.sh
index 926c08f..5d623e0 100755
--- a/ravenwood/scripts/run-ravenwood-tests.sh
+++ b/ravenwood/scripts/run-ravenwood-tests.sh
@@ -14,15 +14,42 @@
# limitations under the License.
# Run all the ravenwood tests + hoststubgen unit tests.
+#
+# Options:
+#
+# -s: "Smoke" test -- skip slow tests (SysUI, ICU)
-all_tests="hoststubgentest tiny-framework-dump-test hoststubgen-invoke-test ravenwood-stats-checker"
+smoke=0
+while getopts "s" opt; do
+case "$opt" in
+ s)
+ smoke=1
+ ;;
+ '?')
+ exit 1
+ ;;
+esac
+done
+shift $(($OPTIND - 1))
-# "echo" is to remove the newlines
-all_tests="$all_tests $(echo $(${0%/*}/list-ravenwood-tests.sh) )"
+all_tests=(hoststubgentest tiny-framework-dump-test hoststubgen-invoke-test ravenwood-stats-checker)
+all_tests+=( $(${0%/*}/list-ravenwood-tests.sh) )
+
+# Regex to identify slow tests, in PCRE
+slow_tests_re='^(SystemUiRavenTests|CtsIcuTestCasesRavenwood)$'
+
+if (( $smoke )) ; then
+ # Remove the slow tests.
+ all_tests=( $(
+ for t in "${all_tests[@]}"; do
+ echo $t | grep -vP "$slow_tests_re"
+ done
+ ) )
+fi
run() {
echo "Running: $*"
"${@}"
}
-run ${ATEST:-atest} $all_tests
+run ${ATEST:-atest} "${all_tests[@]}"
diff --git a/ravenwood/scripts/update-test-mapping.sh b/ravenwood/scripts/update-test-mapping.sh
index e478b50..ab37baf 100755
--- a/ravenwood/scripts/update-test-mapping.sh
+++ b/ravenwood/scripts/update-test-mapping.sh
@@ -23,6 +23,14 @@
# Tests that shouldn't be in presubmit.
EXEMPT='^(SystemUiRavenTests)$'
+is_car() {
+ local module="$1"
+
+ # If the module name starts with "Car", then it's a test for "Car".
+ [[ "$module" =~ ^Car ]]
+ return $?
+}
+
main() {
local script_name="${0##*/}"
local script_dir="${0%/*}"
@@ -62,6 +70,10 @@
fi
echo " {"
echo " \"name\": \"${tests[$i]}\","
+ if is_car "${tests[$i]}"; then
+ echo ' "keywords": ["automotive_code_coverage"],'
+ fi
+
echo " \"host\": true"
echo " }$comma"
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index d7f4b3e..4895a1a 100644
--- a/ravenwood/tests/bivalenttest/Android.bp
+++ b/ravenwood/tests/bivalenttest/Android.bp
@@ -31,9 +31,8 @@
],
}
-android_ravenwood_test {
- name: "RavenwoodBivalentTest",
-
+java_defaults {
+ name: "ravenwood-bivalent-defaults",
static_libs: [
"androidx.annotation_annotation",
"androidx.test.ext.junit",
@@ -44,46 +43,56 @@
// To make sure it won't cause VerifyError (b/324063814)
"platformprotosnano",
+
+ "com.android.internal.os.flags-aconfig-java",
],
srcs: [
"test/**/*.java",
+ "test/**/*.kt",
],
jni_libs: [
"libravenwoodbivalenttest_jni",
],
- auto_gen_config: true,
}
-android_test {
- name: "RavenwoodBivalentTest_device",
-
- srcs: [
- "test/**/*.java",
- ],
+java_defaults {
+ name: "ravenwood-bivalent-device-defaults",
+ defaults: ["ravenwood-bivalent-defaults"],
// TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture
exclude_srcs: [
"test/**/ravenizer/*.java",
+ "test/**/ravenizer/*.kt",
],
static_libs: [
"junit",
"truth",
-
- "androidx.annotation_annotation",
- "androidx.test.ext.junit",
- "androidx.test.rules",
-
- "junit-params",
- "platform-parametric-runner-lib",
-
+ "flag-junit",
"ravenwood-junit",
],
- jni_libs: [
- "libravenwoodbivalenttest_jni",
- ],
test_suites: [
"device-tests",
],
optimize: {
enabled: false,
},
+ test_config_template: "AndroidTestTemplate.xml",
+}
+
+android_ravenwood_test {
+ name: "RavenwoodBivalentTest",
+ defaults: ["ravenwood-bivalent-defaults"],
+ auto_gen_config: true,
+}
+
+android_test {
+ name: "RavenwoodBivalentTest_device",
+ defaults: ["ravenwood-bivalent-device-defaults"],
+}
+
+android_test {
+ name: "RavenwoodBivalentTest_device_ravenizer",
+ defaults: ["ravenwood-bivalent-device-defaults"],
+ ravenizer: {
+ enabled: true,
+ },
}
diff --git a/ravenwood/tests/bivalenttest/AndroidTest.xml b/ravenwood/tests/bivalenttest/AndroidTestTemplate.xml
similarity index 94%
rename from ravenwood/tests/bivalenttest/AndroidTest.xml
rename to ravenwood/tests/bivalenttest/AndroidTestTemplate.xml
index 9e5dd11..8f1a92c 100644
--- a/ravenwood/tests/bivalenttest/AndroidTest.xml
+++ b/ravenwood/tests/bivalenttest/AndroidTestTemplate.xml
@@ -19,7 +19,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="RavenwoodBivalentTest_device.apk" />
+ <option name="test-file-name" value="{MODULE}.apk"/>
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt
new file mode 100644
index 0000000..fd6d6fb
--- /dev/null
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.bivalenttest.aconfig
+
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.os.Flags
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+@RunWith(AndroidJUnit4::class)
+class RavenwoodAconfigSimpleReadTests {
+ @Test
+ fun testFalseFlags() {
+ assertFalse(Flags.ravenwoodFlagRo1())
+ assertFalse(Flags.ravenwoodFlagRw1())
+ }
+
+ @Test
+ @Ignore // TODO: Enable this test after rolling out the "2" flags.
+ fun testTrueFlags() {
+ assertTrue(Flags.ravenwoodFlagRo2())
+ assertTrue(Flags.ravenwoodFlagRw2())
+ }
+}
+
+@RunWith(AndroidJUnit4::class)
+class RavenwoodAconfigCheckFlagsRuleTests {
+ @Rule
+ @JvmField
+ val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RAVENWOOD_FLAG_RO_1)
+ fun testRequireFlagsEnabledRo() {
+ fail("This test shouldn't be executed")
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_RAVENWOOD_FLAG_RW_1)
+ fun testRequireFlagsEnabledRw() {
+ fail("This test shouldn't be executed")
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_RAVENWOOD_FLAG_RO_2)
+ @Ignore // TODO: Enable this test after rolling out the "2" flags.
+ fun testRequireFlagsDisabledRo() {
+ fail("This test shouldn't be executed")
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_RAVENWOOD_FLAG_RW_2)
+ @Ignore // TODO: Enable this test after rolling out the "2" flags.
+ fun testRequireFlagsDisabledRw() {
+ fail("This test shouldn't be executed")
+ }
+}
+
+@RunWith(AndroidJUnit4::class)
+class RavenwoodAconfigSetFlagsRuleWithDefaultTests {
+ @Rule
+ @JvmField
+ val setFlagsRule = SetFlagsRule()
+
+ @Test
+ @EnableFlags(Flags.FLAG_RAVENWOOD_FLAG_RO_1)
+ fun testSetRoFlag() {
+ assertTrue(Flags.ravenwoodFlagRo1())
+ assertFalse(Flags.ravenwoodFlagRw1())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RAVENWOOD_FLAG_RW_1)
+ fun testSetRwFlag() {
+ assertFalse(Flags.ravenwoodFlagRo1())
+ assertTrue(Flags.ravenwoodFlagRw1())
+ }
+}
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java
index d7c2c6c..4e21f86 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java
@@ -16,12 +16,15 @@
package com.android.ravenwoodtest.bivalenttest.ravenizer;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;
+import androidx.test.platform.app.InstrumentationRegistry;
+
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
@@ -30,6 +33,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
/**
* Make sure RavenwoodAwareTestRunnerTest properly delegates to the original runner,
* and also run the special annotated methods.
@@ -61,13 +66,22 @@
sCallTracker.incrementMethodCallCount();
}
+ public RavenwoodAwareTestRunnerTest() {
+ // Make sure the environment is already initialized when the constructor is called
+ assertNotNull(InstrumentationRegistry.getInstrumentation());
+ }
+
@Test
public void test1() {
sCallTracker.incrementMethodCallCount();
}
+ public static List<String> testParams() {
+ return List.of("foo", "bar");
+ }
+
@Test
- @Parameters({"foo", "bar"})
+ @Parameters(method = "testParams")
public void testWithParams(String arg) {
sCallTracker.incrementMethodCallCount();
}
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java
index 9d878f4..77a807d 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java
@@ -16,7 +16,7 @@
package com.android.ravenwoodtest.bivalenttest.ravenizer;
import android.platform.test.annotations.NoRavenizer;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
import org.junit.Test;
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java
index c77841b..e6e617b 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java
@@ -18,7 +18,7 @@
import static org.junit.Assert.fail;
import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java
index ea1a29d..ef18c82b 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java
@@ -18,7 +18,7 @@
import static org.junit.Assert.fail;
import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.annotations.RavenwoodTestRunnerInitializing;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.Log;
diff --git a/ravenwood/tests/coretest/Android.bp b/ravenwood/tests/coretest/Android.bp
index 85f1baf..9dd7cc6 100644
--- a/ravenwood/tests/coretest/Android.bp
+++ b/ravenwood/tests/coretest/Android.bp
@@ -16,13 +16,17 @@
"androidx.test.rules",
"junit-params",
"platform-parametric-runner-lib",
- "truth",
// This library should be removed by Ravenizer
"mockito-target-minus-junit4",
],
+ libs: [
+ // We access internal private classes
+ "ravenwood-junit-impl",
+ ],
srcs: [
"test/**/*.java",
+ "test/**/*.kt",
],
ravenizer: {
strip_mockito: true,
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodStatsDTest.kt
similarity index 68%
rename from ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
rename to ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodStatsDTest.kt
index 83cbc52..d5f5e29 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodStatsDTest.kt
@@ -13,10 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.platform.test.ravenwood;
+package com.android.ravenwoodtest.coretest
-/** Stub class. The actual implementaetion is in junit-impl-src. */
-public class RavenwoodRunnerState {
- public RavenwoodRunnerState(RavenwoodAwareTestRunner runner) {
+import com.android.internal.util.FrameworkStatsLog
+import org.junit.Test
+
+class RavenwoodStatsDTest {
+ @Test
+ fun testFrameworkStatsLog() {
+ FrameworkStatsLog.write(FrameworkStatsLog.PHONE_SIGNAL_STRENGTH_CHANGED, 123)
}
-}
+}
\ No newline at end of file
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
index bd01313..6720e45 100644
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java
@@ -337,9 +337,9 @@
@Expected("""
testRunStarted: classes
testSuiteStarted: classes
- testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest)
- testFailure: Exception detected in constructor
- testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest)
+ testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest)
+ testFailure: Failed to instantiate class androidx.test.ext.junit.runners.AndroidJUnit4
+ testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest)
testSuiteFinished: classes
testRunFinished: 1,1,0,0
""")
@@ -439,9 +439,9 @@
@Expected("""
testRunStarted: classes
testSuiteStarted: classes
- testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest)
- testFailure: Exception detected in constructor
- testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest)
+ testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest)
+ testFailure: Failed to instantiate class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenTestRunner
+ testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest)
testSuiteFinished: classes
testRunFinished: 1,1,0,0
""")
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
index 73ea64f..02d1073 100644
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
@@ -106,17 +106,11 @@
@Expected("""
testRunStarted: classes
testSuiteStarted: classes
- testStarted: testMethod1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
+ testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
- testFinished: testMethod1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
- testStarted: testMethod2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
- testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
- testFinished: testMethod2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
- testStarted: testMethod3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
- testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
- testFinished: testMethod3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
+ testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
testSuiteFinished: classes
- testRunFinished: 3,3,0,0
+ testRunFinished: 1,1,0,0
""")
// CHECKSTYLE:ON
public static class ErrorMustBeReportedFromEachTest {
@@ -145,9 +139,9 @@
@Expected("""
testRunStarted: classes
testSuiteStarted: classes
- testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
+ testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
testFailure: Class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.DuplicateConfigTest has multiple fields with @RavenwoodConfig.Config
- testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
+ testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
testSuiteFinished: classes
testRunFinished: 1,1,0,0
""")
@@ -175,9 +169,9 @@
@Expected("""
testRunStarted: classes
testSuiteStarted: classes
- testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
+ testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest.sConfig expected to be public static
- testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
+ testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
testSuiteFinished: classes
testRunFinished: 1,1,0,0
""")
@@ -201,9 +195,9 @@
@Expected("""
testRunStarted: classes
testSuiteStarted: classes
- testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
+ testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest.sConfig expected to be public static
- testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
+ testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
testSuiteFinished: classes
testRunFinished: 1,1,0,0
""")
@@ -227,9 +221,9 @@
@Expected("""
testRunStarted: classes
testSuiteStarted: classes
- testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
+ testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
testFailure: Field com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.WrongTypeConfigTest.sConfig has @RavenwoodConfig.Config but type is not RavenwoodConfig
- testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
+ testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
testSuiteFinished: classes
testRunFinished: 1,1,0,0
""")
@@ -282,9 +276,9 @@
@Expected("""
testRunStarted: classes
testSuiteStarted: classes
- testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
+ testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig.
- testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
+ testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
testSuiteFinished: classes
testRunFinished: 1,1,0,0
""")
@@ -311,9 +305,9 @@
@Expected("""
testRunStarted: classes
testSuiteStarted: classes
- testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
- testFailure: Exception detected in constructor
- testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
+ testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
+ testFailure: Failed to instantiate class androidx.test.ext.junit.runners.AndroidJUnit4
+ testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
testSuiteFinished: classes
testRunFinished: 1,1,0,0
""")
@@ -400,9 +394,9 @@
@Expected("""
testRunStarted: classes
testSuiteStarted: classes
- testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
+ testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig.
- testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
+ testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
testSuiteFinished: classes
testRunFinished: 1,1,0,0
""")
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
index 9a6934bf..f7a2198 100644
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java
@@ -20,6 +20,7 @@
import android.platform.test.annotations.NoRavenizer;
import android.platform.test.ravenwood.RavenwoodAwareTestRunner;
+import android.platform.test.ravenwood.RavenwoodConfigPrivate;
import android.util.Log;
import junitparams.JUnitParamsRunner;
@@ -137,15 +138,14 @@
// Set a listener to critical errors. This will also prevent
// {@link RavenwoodAwareTestRunner} from calling System.exit() when there's
// a critical error.
- RavenwoodAwareTestRunner.private$ravenwood().setCriticalErrorHandler(
- listener.sCriticalErrorListener);
+ RavenwoodConfigPrivate.setCriticalErrorHandler(listener.sCriticalErrorListener);
try {
// Run the test class.
junitCore.run(testClazz);
} finally {
// Clear the critical error listener.
- RavenwoodAwareTestRunner.private$ravenwood().setCriticalErrorHandler(null);
+ RavenwoodConfigPrivate.setCriticalErrorHandler(null);
}
// Check the result.
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index 9c86389..a26fe66 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -359,3 +359,6 @@
com.android.server.SystemServiceManager
com.android.server.utils.TimingsTraceAndSlog
+
+android.os.IpcDataCache
+android.app.PropertyInvalidatedCache
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index b64944e..80126df 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -5,8 +5,7 @@
rename com/.*/nano/ devicenano/
rename android/.*/nano/ devicenano/
-
-# StatsD autogenerated classes. Maybe add a heuristic?
+# StatsD auto-generated
class com.android.internal.util.FrameworkStatsLog keepclass
# Exported to Mainline modules; cannot use annotations
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index a02082d..f47aaba 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -327,6 +327,10 @@
return (this.access and Opcodes.ACC_SYNTHETIC) != 0
}
+fun ClassNode.isAbstract(): Boolean {
+ return (this.access and Opcodes.ACC_ABSTRACT) != 0
+}
+
fun MethodNode.isSynthetic(): Boolean {
return (this.access and Opcodes.ACC_SYNTHETIC) != 0
}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
index 32dcbe5..a0e5599 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
@@ -46,7 +46,7 @@
var enableValidation: SetOnce<Boolean> = SetOnce(true),
/** Whether the validation failure is fatal or not. */
- var fatalValidation: SetOnce<Boolean> = SetOnce(false),
+ var fatalValidation: SetOnce<Boolean> = SetOnce(true),
/** Whether to remove mockito and dexmaker classes. */
var stripMockito: SetOnce<Boolean> = SetOnce(false),
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
index 37a7975..6092fcc 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
@@ -15,6 +15,7 @@
*/
package com.android.platform.test.ravenwood.ravenizer
+import android.platform.test.annotations.internal.InnerRunner
import android.platform.test.annotations.NoRavenizer
import android.platform.test.ravenwood.RavenwoodAwareTestRunner
import com.android.hoststubgen.asm.ClassNodes
@@ -39,7 +40,7 @@
val ruleAnotType = TypeHolder(org.junit.Rule::class.java)
val classRuleAnotType = TypeHolder(org.junit.ClassRule::class.java)
val runWithAnotType = TypeHolder(RunWith::class.java)
-val innerRunnerAnotType = TypeHolder(RavenwoodAwareTestRunner.InnerRunner::class.java)
+val innerRunnerAnotType = TypeHolder(InnerRunner::class.java)
val noRavenizerAnotType = TypeHolder(NoRavenizer::class.java)
val testRuleType = TypeHolder(TestRule::class.java)
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
index 27092d2..8ec0932 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
@@ -16,10 +16,12 @@
package com.android.platform.test.ravenwood.ravenizer
import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.isAbstract
import com.android.hoststubgen.asm.startsWithAny
import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.log
import org.objectweb.asm.tree.ClassNode
+import java.util.regex.Pattern
fun validateClasses(classes: ClassNodes): Boolean {
var allOk = true
@@ -41,25 +43,35 @@
}
var allOk = true
+ log.i("Checking ${cn.name.toHumanReadableClassName()}")
+
// See if there's any class that extends a legacy base class.
// But ignore the base classes in android.test.
- if (!cn.name.startsWithAny("android/test/")) {
- allOk = checkSuperClass(cn, cn, classes) && allOk
+ if (!cn.isAbstract() && !cn.name.startsWith("android/test/")
+ && !isAllowListedLegacyTest(cn)
+ ) {
+ allOk = checkSuperClassForJunit3(cn, cn, classes) && allOk
}
return allOk
}
-fun checkSuperClass(targetClass: ClassNode, currentClass: ClassNode, classes: ClassNodes): Boolean {
+fun checkSuperClassForJunit3(
+ targetClass: ClassNode,
+ currentClass: ClassNode,
+ classes: ClassNodes,
+): Boolean {
if (currentClass.superName == null || currentClass.superName == "java/lang/Object") {
return true // No parent class
}
+ // Make sure the class doesn't extend a junit3 TestCase class.
if (currentClass.superName.isLegacyTestBaseClass()) {
log.e("Error: Class ${targetClass.name.toHumanReadableClassName()} extends"
- + " a legacy test class ${currentClass.superName.toHumanReadableClassName()}.")
+ + " a legacy test class ${currentClass.superName.toHumanReadableClassName()}"
+ + ", which is not supported on Ravenwood. Please migrate to Junit4 syntax.")
return false
}
classes.findClass(currentClass.superName)?.let {
- return checkSuperClass(targetClass, it, classes)
+ return checkSuperClassForJunit3(targetClass, it, classes)
}
// Super class not found.
// log.w("Class ${currentClass.superName} not found.")
@@ -73,9 +85,64 @@
return this.startsWithAny(
"junit/framework/TestCase",
- // In case the test doesn't statically include JUnit, we need
+ // In case the test doesn't statically include JUnit, we need the following.
"android/test/AndroidTestCase",
"android/test/InstrumentationTestCase",
"android/test/InstrumentationTestSuite",
)
}
+
+private val allowListedLegacyTests = setOf(
+// List of existing test classes that use the JUnit3 syntax. We exempt them for now, but
+// will reject any more of them.
+//
+// Note, we want internal class names, but for convenience, we use '.'s and '%'s here
+// and replace them later. (a '$' would be parsed as a string template.)
+ *"""
+android.util.proto.cts.DebuggingTest
+android.util.proto.cts.EncodedBufferTest
+android.util.proto.cts.ProtoOutputStreamBoolTest
+android.util.proto.cts.ProtoOutputStreamBytesTest
+android.util.proto.cts.ProtoOutputStreamDoubleTest
+android.util.proto.cts.ProtoOutputStreamEnumTest
+android.util.proto.cts.ProtoOutputStreamFixed32Test
+android.util.proto.cts.ProtoOutputStreamFixed64Test
+android.util.proto.cts.ProtoOutputStreamFloatTest
+android.util.proto.cts.ProtoOutputStreamInt32Test
+android.util.proto.cts.ProtoOutputStreamInt64Test
+android.util.proto.cts.ProtoOutputStreamObjectTest
+android.util.proto.cts.ProtoOutputStreamSFixed32Test
+android.util.proto.cts.ProtoOutputStreamSFixed64Test
+android.util.proto.cts.ProtoOutputStreamSInt32Test
+android.util.proto.cts.ProtoOutputStreamSInt64Test
+android.util.proto.cts.ProtoOutputStreamStringTest
+android.util.proto.cts.ProtoOutputStreamSwitchedWriteTest
+android.util.proto.cts.ProtoOutputStreamTagTest
+android.util.proto.cts.ProtoOutputStreamUInt32Test
+android.util.proto.cts.ProtoOutputStreamUInt64Test
+
+android.os.cts.BadParcelableExceptionTest
+android.os.cts.DeadObjectExceptionTest
+android.os.cts.ParcelFormatExceptionTest
+android.os.cts.PatternMatcherTest
+android.os.cts.RemoteExceptionTest
+
+android.os.storage.StorageManagerBaseTest
+android.os.storage.StorageManagerIntegrationTest
+android.util.LogTest%PerformanceTest
+
+com.android.server.power.stats.BatteryStatsCounterTest
+com.android.server.power.stats.BatteryStatsDualTimerTest
+com.android.server.power.stats.BatteryStatsDurationTimerTest
+com.android.server.power.stats.BatteryStatsSamplingTimerTest
+com.android.server.power.stats.BatteryStatsStopwatchTimerTest
+com.android.server.power.stats.BatteryStatsTimeBaseTest
+com.android.server.power.stats.BatteryStatsTimerTest
+
+ """.trim().replace('%', '$').replace('.', '/')
+ .split(Pattern.compile("""\s+""")).toTypedArray()
+)
+
+private fun isAllowListedLegacyTest(targetClass: ClassNode): Boolean {
+ return allowListedLegacyTests.contains(targetClass.name)
+}
\ No newline at end of file
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
index cf6d6f6..81fe3da 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
@@ -15,7 +15,6 @@
*/
package com.android.platform.test.ravenwood.ravenizer.adapter
-import android.platform.test.ravenwood.RavenwoodAwareTestRunner
import com.android.hoststubgen.ClassParseException
import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
@@ -28,8 +27,8 @@
import com.android.hoststubgen.visitors.OPCODE_VERSION
import com.android.platform.test.ravenwood.ravenizer.RavenizerInternalException
import com.android.platform.test.ravenwood.ravenizer.classRuleAnotType
-import com.android.platform.test.ravenwood.ravenizer.isTestLookingClass
import com.android.platform.test.ravenwood.ravenizer.innerRunnerAnotType
+import com.android.platform.test.ravenwood.ravenizer.isTestLookingClass
import com.android.platform.test.ravenwood.ravenizer.noRavenizerAnotType
import com.android.platform.test.ravenwood.ravenizer.ravenwoodTestRunnerType
import com.android.platform.test.ravenwood.ravenizer.ruleAnotType
@@ -50,7 +49,7 @@
* Class visitor to update the RunWith and inject some necessary rules.
*
* - Change the @RunWith(RavenwoodAwareTestRunner.class).
- * - If the original class has a @RunWith(...), then change it to an @OrigRunWith(...).
+ * - If the original class has a @RunWith(...), then change it to an @InnerRunner(...).
* - Add RavenwoodAwareTestRunner's member rules as junit rules.
* - Update the order of the existing JUnit rules to make sure they don't use the MIN or MAX.
*/
@@ -146,7 +145,7 @@
/**
* Inject `@RunWith(RavenwoodAwareTestRunner.class)`. If the class already has
- * a `@RunWith`, then change it to add a `@OrigRunWith`.
+ * a `@RunWith`, then change it to add a `@InnerRunner`.
*/
private fun injectRunWithAnnotation() {
// Extract the original RunWith annotation and its value.
@@ -172,7 +171,7 @@
+ " in class ${classInternalName.toHumanReadableClassName()}")
}
- // Inject an @OrigRunWith.
+ // Inject an @InnerRunner.
visitAnnotation(innerRunnerAnotType.desc, true)!!.let { av ->
av.visit("value", runWithClass)
av.visitEnd()
@@ -302,7 +301,7 @@
override fun visitCode() {
visitFieldInsn(Opcodes.GETSTATIC,
ravenwoodTestRunnerType.internlName,
- RavenwoodAwareTestRunner.IMPLICIT_CLASS_OUTER_RULE_NAME,
+ IMPLICIT_CLASS_OUTER_RULE_NAME,
testRuleType.desc
)
visitFieldInsn(Opcodes.PUTSTATIC,
@@ -313,7 +312,7 @@
visitFieldInsn(Opcodes.GETSTATIC,
ravenwoodTestRunnerType.internlName,
- RavenwoodAwareTestRunner.IMPLICIT_CLASS_INNER_RULE_NAME,
+ IMPLICIT_CLASS_INNER_RULE_NAME,
testRuleType.desc
)
visitFieldInsn(Opcodes.PUTSTATIC,
@@ -361,7 +360,7 @@
visitVarInsn(ALOAD, 0)
visitFieldInsn(Opcodes.GETSTATIC,
ravenwoodTestRunnerType.internlName,
- RavenwoodAwareTestRunner.IMPLICIT_INST_OUTER_RULE_NAME,
+ IMPLICIT_INST_OUTER_RULE_NAME,
testRuleType.desc
)
visitFieldInsn(Opcodes.PUTFIELD,
@@ -373,7 +372,7 @@
visitVarInsn(ALOAD, 0)
visitFieldInsn(Opcodes.GETSTATIC,
ravenwoodTestRunnerType.internlName,
- RavenwoodAwareTestRunner.IMPLICIT_INST_INNER_RULE_NAME,
+ IMPLICIT_INST_INNER_RULE_NAME,
testRuleType.desc
)
visitFieldInsn(Opcodes.PUTFIELD,
@@ -435,6 +434,11 @@
}
companion object {
+ const val IMPLICIT_CLASS_OUTER_RULE_NAME = "sImplicitClassOuterRule"
+ const val IMPLICIT_CLASS_INNER_RULE_NAME = "sImplicitClassInnerRule"
+ const val IMPLICIT_INST_OUTER_RULE_NAME = "sImplicitInstOuterRule"
+ const val IMPLICIT_INST_INNER_RULE_NAME = "sImplicitInstInnerRule"
+
fun shouldProcess(classes: ClassNodes, className: String): Boolean {
if (!isTestLookingClass(classes, className)) {
return false
@@ -463,4 +467,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 5567707..0c99fcf 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -209,6 +209,10 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
if (DEBUG) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 3532b0a..349f3ee 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -50,7 +50,7 @@
],
}
-genrule {
+java_genrule {
name: "services.core.protologsrc",
srcs: [
":protolog-impl",
@@ -70,7 +70,7 @@
out: ["services.core.protolog.srcjar"],
}
-genrule {
+java_genrule {
name: "generate-protolog.json",
srcs: [
":protolog-groups",
@@ -87,7 +87,7 @@
out: ["services.core.protolog.json"],
}
-genrule {
+java_genrule {
name: "gen-core.protolog.pb",
srcs: [
":protolog-groups",
@@ -281,7 +281,7 @@
src: "java/com/android/server/location/gnss/gps_debug.conf",
}
-genrule {
+java_genrule {
name: "services.core.json.gz",
srcs: [":generate-protolog.json"],
out: ["services.core.protolog.json.gz"],
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 37dddc6..c15cf34 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -48,3 +48,6 @@
# CertBlocklister
per-file Cert*.java = [email protected], [email protected], [email protected], [email protected]
+
+# TradeInMode
+per-file TradeInModeService.java = [email protected], [email protected]
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 8b619a4..9b987e9 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -670,6 +670,17 @@
}
private void readAllPermissions() {
+ readAllPermissionsFromXml();
+ readAllPermissionsFromEnvironment();
+
+ // Apply global feature removal last, after all features have been read.
+ // This only needs to happen once.
+ for (String featureName : mUnavailableFeatures) {
+ removeFeature(featureName);
+ }
+ }
+
+ private void readAllPermissionsFromXml() {
final XmlPullParser parser = Xml.newPullParser();
// Read configuration from system
@@ -1732,7 +1743,13 @@
} finally {
IoUtils.closeQuietly(permReader);
}
+ }
+ // Add features or permission dependent on global system properties (as
+ // opposed to XML permission files).
+ // This only needs to be called once after all features have been parsed
+ // from various partition/apex sources.
+ private void readAllPermissionsFromEnvironment() {
// Some devices can be field-converted to FBE, so offer to splice in
// those features if not already defined by the static config
if (StorageManager.isFileEncrypted()) {
@@ -1773,10 +1790,6 @@
addFeature(PackageManager.FEATURE_EROFS_LEGACY, 0);
}
}
-
- for (String featureName : mUnavailableFeatures) {
- removeFeature(featureName);
- }
}
private @Nullable SignedPackage parseEnhancedConfirmationTrustedPackage(XmlPullParser parser,
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 51c768b..06e6c8b 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -48,6 +48,7 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.vcn.Flags;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
@@ -524,6 +525,9 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
switch (action) {
case Intent.ACTION_PACKAGE_ADDED: // Fallthrough
@@ -878,6 +882,7 @@
private void garbageCollectAndWriteVcnConfigsLocked() {
final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class);
+ final Set<ParcelUuid> subGroups = mLastSnapshot.getAllSubscriptionGroups();
boolean shouldWrite = false;
@@ -885,11 +890,20 @@
while (configsIterator.hasNext()) {
final ParcelUuid subGrp = configsIterator.next();
- final List<SubscriptionInfo> subscriptions = subMgr.getSubscriptionsInGroup(subGrp);
- if (subscriptions == null || subscriptions.isEmpty()) {
- // Trim subGrps with no more subscriptions; must have moved to another subGrp
- configsIterator.remove();
- shouldWrite = true;
+ if (Flags.fixConfigGarbageCollection()) {
+ if (!subGroups.contains(subGrp)) {
+ // Trim subGrps with no more subscriptions; must have moved to another subGrp
+ logDbg("Garbage collect VcnConfig for group=" + subGrp);
+ configsIterator.remove();
+ shouldWrite = true;
+ }
+ } else {
+ final List<SubscriptionInfo> subscriptions = subMgr.getSubscriptionsInGroup(subGrp);
+ if (subscriptions == null || subscriptions.isEmpty()) {
+ // Trim subGrps with no more subscriptions; must have moved to another subGrp
+ configsIterator.remove();
+ shouldWrite = true;
+ }
}
}
@@ -1094,13 +1108,7 @@
synchronized (mLock) {
final Vcn vcn = mVcns.get(subGrp);
final VcnConfig vcnConfig = mConfigs.get(subGrp);
- if (vcn != null) {
- if (vcnConfig == null) {
- // TODO: b/284381334 Investigate for the root cause of this issue
- // and handle it properly
- logWtf("Vcn instance exists but VcnConfig does not for " + subGrp);
- }
-
+ if (vcn != null && vcnConfig != null) {
if (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE) {
isVcnManagedNetwork = true;
}
@@ -1120,6 +1128,8 @@
}
}
}
+ } else if (vcn != null && vcnConfig == null) {
+ logWtf("Vcn instance exists but VcnConfig does not for " + subGrp);
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 918f130..0dedb73 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -289,7 +289,7 @@
public void scheduleCheckLocked(long handlerCheckerTimeoutMillis) {
mWaitMaxMillis = handlerCheckerTimeoutMillis;
- if (mCompleted) {
+ if (mCompleted && !mMonitorQueue.isEmpty()) {
// Safe to update monitors in queue, Handler is not in the middle of work
mMonitors.addAll(mMonitorQueue);
mMonitorQueue.clear();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4efe62c..d11abf8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2874,8 +2874,12 @@
// Add common services.
// IMPORTANT: Before adding services here, make sure ephemeral apps can access them too.
// Enable the check in ApplicationThread.bindApplication() to make sure.
- if (!android.server.Flags.removeJavaServiceManagerCache()) {
- addServiceToMap(mAppBindArgs, "permissionmgr");
+
+ // Removing User Service and App Ops Service from cache breaks boot for auto.
+ // Removing permissionmgr breaks tests for Android Auto due to SELinux restrictions.
+ // TODO: fix SELinux restrictions and remove caching for Android Auto.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ || !android.server.Flags.removeJavaServiceManagerCache()) {
addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
@@ -2884,14 +2888,12 @@
addServiceToMap(mAppBindArgs, Context.INPUT_METHOD_SERVICE);
addServiceToMap(mAppBindArgs, Context.INPUT_SERVICE);
addServiceToMap(mAppBindArgs, "graphicsstats");
- addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
addServiceToMap(mAppBindArgs, "content");
addServiceToMap(mAppBindArgs, Context.JOB_SCHEDULER_SERVICE);
addServiceToMap(mAppBindArgs, Context.NOTIFICATION_SERVICE);
addServiceToMap(mAppBindArgs, Context.VIBRATOR_SERVICE);
addServiceToMap(mAppBindArgs, Context.ACCOUNT_SERVICE);
addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
- addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
addServiceToMap(mAppBindArgs, "mount");
addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE);
}
@@ -2901,6 +2903,9 @@
// TODO: remove exception
addServiceToMap(mAppBindArgs, "package");
addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
+ addServiceToMap(mAppBindArgs, "permissionmgr");
+ addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
}
return mAppBindArgs;
}
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 4c87e1c..c036605 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -373,7 +373,10 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- switch (intent.getAction()) {
+ if (action == null) {
+ return;
+ }
+ switch (action) {
case Intent.ACTION_PACKAGE_ADDED: {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index d67d3ca..7600855 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4973,17 +4973,7 @@
}
success = true;
- } catch (IllegalStateException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } catch (NullPointerException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } catch (XmlPullParserException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } catch (IOException e) {
- Slog.w(TAG, "Failed parsing " + e);
- } catch (IndexOutOfBoundsException e) {
+ } catch (Exception e) {
Slog.w(TAG, "Failed parsing " + e);
} finally {
if (!success) {
diff --git a/services/core/java/com/android/server/appop/LegacyAppOpStateParser.java b/services/core/java/com/android/server/appop/LegacyAppOpStateParser.java
index 9ed3a99..b677a1d 100644
--- a/services/core/java/com/android/server/appop/LegacyAppOpStateParser.java
+++ b/services/core/java/com/android/server/appop/LegacyAppOpStateParser.java
@@ -50,6 +50,10 @@
public int readState(AtomicFile file, SparseArray<SparseIntArray> uidModes,
SparseArray<ArrayMap<String, SparseIntArray>> userPackageModes) {
try (FileInputStream stream = file.openRead()) {
+ SparseArray<SparseIntArray> parsedUidModes = new SparseArray<>();
+ SparseArray<ArrayMap<String, SparseIntArray>> parsedUserPackageModes =
+ new SparseArray<>();
+
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
@@ -75,26 +79,37 @@
// version 2 has the structure pkg -> uid -> op ->
// in version 3, since pkg and uid states are kept completely
// independent we switch to user -> pkg -> op
- readPackage(parser, userPackageModes);
+ readPackage(parser, parsedUserPackageModes);
} else if (tagName.equals("uid")) {
- readUidOps(parser, uidModes);
+ readUidOps(parser, parsedUidModes);
} else if (tagName.equals("user")) {
- readUser(parser, userPackageModes);
+ readUser(parser, parsedUserPackageModes);
} else {
Slog.w(TAG, "Unknown element under <app-ops>: "
+ parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
+
+ // Parsing is complete, copy all parsed values to output
+ final int parsedUidModesSize = parsedUidModes.size();
+ for (int i = 0; i < parsedUidModesSize; i++) {
+ uidModes.put(parsedUidModes.keyAt(i), parsedUidModes.valueAt(i));
+ }
+ final int parsedUserPackageModesSize = parsedUserPackageModes.size();
+ for (int i = 0; i < parsedUserPackageModesSize; i++) {
+ userPackageModes.put(parsedUserPackageModes.keyAt(i),
+ parsedUserPackageModes.valueAt(i));
+ }
+
return versionAtBoot;
} catch (FileNotFoundException e) {
Slog.i(TAG, "No existing app ops " + file.getBaseFile() + "; starting empty");
- return NO_FILE_VERSION;
- } catch (XmlPullParserException e) {
- throw new RuntimeException(e);
- } catch (IOException e) {
- throw new RuntimeException(e);
+ } catch (Exception e) {
+ // All exceptions must be caught, otherwise device will not be able to boot
+ Slog.wtf(TAG, "Failed parsing " + e);
}
+ return NO_FILE_VERSION;
}
private void readPackage(TypedXmlPullParser parser,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b0590fe..2d3b7f3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -787,9 +787,11 @@
AudioSystem.DEVICE_OUT_HDMI_EARC
));
+ private final Object mAbsoluteVolumeDeviceInfoMapLock = new Object();
// Devices where the framework sends a full scale audio signal, and controls the volume of
// the external audio system separately.
// For possible volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
+ @GuardedBy("mAbsoluteVolumeDeviceInfoMapLock")
Map<Integer, AbsoluteVolumeDeviceInfo> mAbsoluteVolumeDeviceInfoMap = new ArrayMap<>();
/**
@@ -3729,9 +3731,8 @@
int oldIndex = mStreamStates[streamType].getIndex(device);
// Check if the volume adjustment should be handled by an absolute volume controller instead
- if (isAbsoluteVolumeDevice(device)
- && (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
- AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
+ if (isAbsoluteVolumeDevice(device) && (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
+ final AbsoluteVolumeDeviceInfo info = getAbsoluteVolumeDeviceInfo(device);
if (info.mHandlesVolumeAdjustment) {
dispatchAbsoluteVolumeAdjusted(streamType, info, oldIndex, direction,
keyEventMode);
@@ -3798,7 +3799,7 @@
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);
} else if (isAbsoluteVolumeDevice(device)
&& (flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0) {
- AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
+ final AbsoluteVolumeDeviceInfo info = getAbsoluteVolumeDeviceInfo(device);
dispatchAbsoluteVolumeChanged(streamType, info, newIndex);
}
@@ -4801,7 +4802,7 @@
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
} else if (isAbsoluteVolumeDevice(device)
&& ((flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0)) {
- AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
+ final AbsoluteVolumeDeviceInfo info = getAbsoluteVolumeDeviceInfo(device);
dispatchAbsoluteVolumeChanged(streamType, info, index);
}
@@ -7603,7 +7604,8 @@
if (register) {
AbsoluteVolumeDeviceInfo info = new AbsoluteVolumeDeviceInfo(
device, volumes, cb, handlesVolumeAdjustment, deviceVolumeBehavior);
- AbsoluteVolumeDeviceInfo oldInfo = mAbsoluteVolumeDeviceInfoMap.get(deviceOut);
+ final AbsoluteVolumeDeviceInfo oldInfo = getAbsoluteVolumeDeviceInfo(deviceOut);
+
boolean volumeBehaviorChanged = (oldInfo == null)
|| (oldInfo.mDeviceVolumeBehavior != deviceVolumeBehavior);
if (volumeBehaviorChanged) {
@@ -7763,8 +7765,10 @@
if (mAbsVolumeMultiModeCaseDevices.contains(audioSystemDeviceOut)) {
return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE;
}
- if (mAbsoluteVolumeDeviceInfoMap.containsKey(audioSystemDeviceOut)) {
- return mAbsoluteVolumeDeviceInfoMap.get(audioSystemDeviceOut).mDeviceVolumeBehavior;
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ if (mAbsoluteVolumeDeviceInfoMap.containsKey(audioSystemDeviceOut)) {
+ return mAbsoluteVolumeDeviceInfoMap.get(audioSystemDeviceOut).mDeviceVolumeBehavior;
+ }
}
if (isA2dpAbsoluteVolumeDevice(audioSystemDeviceOut)
@@ -11806,10 +11810,12 @@
}
private Set<Integer> getAbsoluteVolumeDevicesWithBehavior(int behavior) {
- return mAbsoluteVolumeDeviceInfoMap.entrySet().stream()
- .filter(entry -> entry.getValue().mDeviceVolumeBehavior == behavior)
- .map(Map.Entry::getKey)
- .collect(Collectors.toSet());
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ return mAbsoluteVolumeDeviceInfoMap.entrySet().stream()
+ .filter(entry -> entry.getValue().mDeviceVolumeBehavior == behavior)
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toSet());
+ }
}
private String dumpDeviceTypes(@NonNull Set<Integer> deviceTypes) {
@@ -14302,14 +14308,26 @@
}
/**
+ * Returns the input device which uses absolute volume behavior, including its variants,
+ * or {@code null} if there is no mapping for the device type
+ */
+ @Nullable
+ private AbsoluteVolumeDeviceInfo getAbsoluteVolumeDeviceInfo(int deviceType) {
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ return mAbsoluteVolumeDeviceInfoMap.get(deviceType);
+ }
+ }
+
+ /**
* Returns whether the input device uses absolute volume behavior, including its variants.
* For included volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
- *
- * This is distinct from Bluetooth A2DP absolute volume behavior
+ * <p>This is distinct from Bluetooth A2DP absolute volume behavior
* ({@link #isA2dpAbsoluteVolumeDevice}).
*/
private boolean isAbsoluteVolumeDevice(int deviceType) {
- return mAbsoluteVolumeDeviceInfoMap.containsKey(deviceType);
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ return mAbsoluteVolumeDeviceInfoMap.containsKey(deviceType);
+ }
}
/**
@@ -14421,7 +14439,9 @@
+ AudioDeviceVolumeManager.volumeBehaviorName(info.mDeviceVolumeBehavior)
);
}
- mAbsoluteVolumeDeviceInfoMap.put(audioSystemDeviceOut, info);
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ mAbsoluteVolumeDeviceInfoMap.put(audioSystemDeviceOut, info);
+ }
}
private AbsoluteVolumeDeviceInfo removeAudioSystemDeviceOutFromAbsVolumeDevices(
@@ -14430,7 +14450,10 @@
Log.d(TAG, "Removing DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
+ " from mAbsoluteVolumeDeviceInfoMap");
}
- return mAbsoluteVolumeDeviceInfoMap.remove(audioSystemDeviceOut);
+
+ synchronized (mAbsoluteVolumeDeviceInfoMapLock) {
+ return mAbsoluteVolumeDeviceInfoMap.remove(audioSystemDeviceOut);
+ }
}
//====================
diff --git a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
index 01f770b..9fa5da4 100644
--- a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
+++ b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java
@@ -133,6 +133,9 @@
private static final EventLogger sLogger = new EventLogger(
AudioService.LOG_NB_EVENTS_LOUDNESS_CODEC, "Loudness updates");
+ private final Object mDispatcherLock = new Object();
+
+ @GuardedBy("mDispatcherLock")
private final LoudnessRemoteCallbackList mLoudnessUpdateDispatchers =
new LoudnessRemoteCallbackList(this);
@@ -339,12 +342,16 @@
}
void registerLoudnessCodecUpdatesDispatcher(ILoudnessCodecUpdatesDispatcher dispatcher) {
- mLoudnessUpdateDispatchers.register(dispatcher, Binder.getCallingPid());
+ synchronized (mDispatcherLock) {
+ mLoudnessUpdateDispatchers.register(dispatcher, Binder.getCallingPid());
+ }
}
void unregisterLoudnessCodecUpdatesDispatcher(
ILoudnessCodecUpdatesDispatcher dispatcher) {
- mLoudnessUpdateDispatchers.unregister(dispatcher);
+ synchronized (mDispatcherLock) {
+ mLoudnessUpdateDispatchers.unregister(dispatcher);
+ }
}
void startLoudnessCodecUpdates(int sessionId) {
@@ -640,17 +647,20 @@
Log.d(TAG,
"dispatchNewLoudnessParameters: sessionId " + sessionId + " bundle: " + bundle);
}
- final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast();
- for (int i = 0; i < nbDispatchers; ++i) {
- try {
- mLoudnessUpdateDispatchers.getBroadcastItem(i)
- .dispatchLoudnessCodecParameterChange(sessionId, bundle);
- } catch (RemoteException e) {
- Log.e(TAG, "Error dispatching for sessionId " + sessionId + " bundle: " + bundle,
- e);
+ synchronized (mDispatcherLock) {
+ final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast();
+ for (int i = 0; i < nbDispatchers; ++i) {
+ try {
+ mLoudnessUpdateDispatchers.getBroadcastItem(i)
+ .dispatchLoudnessCodecParameterChange(sessionId, bundle);
+ } catch (RemoteException e) {
+ Log.e(TAG,
+ "Error dispatching for sessionId " + sessionId + " bundle: " + bundle,
+ e);
+ }
}
+ mLoudnessUpdateDispatchers.finishBroadcast();
}
- mLoudnessUpdateDispatchers.finishBroadcast();
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index bbe7b2b..8436c80 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1975,7 +1975,6 @@
void setAudioStatus(boolean mute, int volume) {
if (!isTvDeviceEnabled()
|| !tv().isSystemAudioActivated()
- || !tv().isArcEstablished() // Don't update TV volume when SAM is on and ARC is off
|| getHdmiCecVolumeControl()
== HdmiControlManager.VOLUME_CONTROL_DISABLED) {
return;
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
deleted file mode 100644
index 7f0231e..0000000
--- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * 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 com.android.server.integrity;
-
-import android.annotation.Nullable;
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.Rule;
-import android.os.Environment;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.integrity.model.RuleMetadata;
-import com.android.server.integrity.parser.RandomAccessObject;
-import com.android.server.integrity.parser.RuleBinaryParser;
-import com.android.server.integrity.parser.RuleIndexRange;
-import com.android.server.integrity.parser.RuleIndexingController;
-import com.android.server.integrity.parser.RuleMetadataParser;
-import com.android.server.integrity.parser.RuleParseException;
-import com.android.server.integrity.parser.RuleParser;
-import com.android.server.integrity.serializer.RuleBinarySerializer;
-import com.android.server.integrity.serializer.RuleMetadataSerializer;
-import com.android.server.integrity.serializer.RuleSerializeException;
-import com.android.server.integrity.serializer.RuleSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-
-/** Abstraction over the underlying storage of rules and other metadata. */
-public class IntegrityFileManager {
- private static final String TAG = "IntegrityFileManager";
-
- private static final String METADATA_FILE = "metadata";
- private static final String RULES_FILE = "rules";
- private static final String INDEXING_FILE = "indexing";
- private static final Object RULES_LOCK = new Object();
-
- private static IntegrityFileManager sInstance = null;
-
- private final RuleParser mRuleParser;
- private final RuleSerializer mRuleSerializer;
-
- private final File mDataDir;
- // mRulesDir contains data of the actual rules currently stored.
- private final File mRulesDir;
- // mStagingDir is used to store the temporary rules / metadata during updating, since we want to
- // update rules atomically.
- private final File mStagingDir;
-
- @Nullable private RuleMetadata mRuleMetadataCache;
- @Nullable private RuleIndexingController mRuleIndexingController;
-
- /** Get the singleton instance of this class. */
- public static synchronized IntegrityFileManager getInstance() {
- if (sInstance == null) {
- sInstance = new IntegrityFileManager();
- }
- return sInstance;
- }
-
- private IntegrityFileManager() {
- this(
- new RuleBinaryParser(),
- new RuleBinarySerializer(),
- Environment.getDataSystemDirectory());
- }
-
- @VisibleForTesting
- IntegrityFileManager(RuleParser ruleParser, RuleSerializer ruleSerializer, File dataDir) {
- mRuleParser = ruleParser;
- mRuleSerializer = ruleSerializer;
- mDataDir = dataDir;
-
- mRulesDir = new File(dataDir, "integrity_rules");
- mStagingDir = new File(dataDir, "integrity_staging");
-
- if (!mStagingDir.mkdirs() || !mRulesDir.mkdirs()) {
- Slog.e(TAG, "Error creating staging and rules directory");
- // TODO: maybe throw an exception?
- }
-
- File metadataFile = new File(mRulesDir, METADATA_FILE);
- if (metadataFile.exists()) {
- try (FileInputStream inputStream = new FileInputStream(metadataFile)) {
- mRuleMetadataCache = RuleMetadataParser.parse(inputStream);
- } catch (Exception e) {
- Slog.e(TAG, "Error reading metadata file.", e);
- }
- }
-
- updateRuleIndexingController();
- }
-
- /**
- * Returns if the rules have been initialized.
- *
- * <p>Used to fail early if there are no rules (so we don't need to parse the apk at all).
- */
- public boolean initialized() {
- return new File(mRulesDir, RULES_FILE).exists()
- && new File(mRulesDir, METADATA_FILE).exists()
- && new File(mRulesDir, INDEXING_FILE).exists();
- }
-
- /** Write rules to persistent storage. */
- public void writeRules(String version, String ruleProvider, List<Rule> rules)
- throws IOException, RuleSerializeException {
- try {
- writeMetadata(mStagingDir, ruleProvider, version);
- } catch (IOException e) {
- Slog.e(TAG, "Error writing metadata.", e);
- // We don't consider this fatal so we continue execution.
- }
-
- try (FileOutputStream ruleFileOutputStream =
- new FileOutputStream(new File(mStagingDir, RULES_FILE));
- FileOutputStream indexingFileOutputStream =
- new FileOutputStream(new File(mStagingDir, INDEXING_FILE))) {
- mRuleSerializer.serialize(
- rules, Optional.empty(), ruleFileOutputStream, indexingFileOutputStream);
- }
-
- switchStagingRulesDir();
-
- // Update object holding the indexing information.
- updateRuleIndexingController();
- }
-
- /**
- * Read rules from persistent storage.
- *
- * @param appInstallMetadata information about the install used to select rules to read. If
- * null, all rules will be read.
- */
- public List<Rule> readRules(@Nullable AppInstallMetadata appInstallMetadata)
- throws IOException, RuleParseException {
- synchronized (RULES_LOCK) {
- // Try to identify indexes from the index file.
- List<RuleIndexRange> ruleReadingIndexes = Collections.emptyList();
- if (appInstallMetadata != null) {
- try {
- ruleReadingIndexes =
- mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);
- } catch (Exception e) {
- Slog.w(TAG, "Error identifying the rule indexes. Trying unindexed.", e);
- }
- }
-
- // Read the rules based on the index information when available.
- File ruleFile = new File(mRulesDir, RULES_FILE);
- List<Rule> rules =
- mRuleParser.parse(RandomAccessObject.ofFile(ruleFile), ruleReadingIndexes);
- return rules;
- }
- }
-
- /** Read the metadata of the current rules in storage. */
- @Nullable
- public RuleMetadata readMetadata() {
- return mRuleMetadataCache;
- }
-
- private void switchStagingRulesDir() throws IOException {
- synchronized (RULES_LOCK) {
- File tmpDir = new File(mDataDir, "temp");
-
- if (!(mRulesDir.renameTo(tmpDir)
- && mStagingDir.renameTo(mRulesDir)
- && tmpDir.renameTo(mStagingDir))) {
- throw new IOException("Error switching staging/rules directory");
- }
-
- for (File file : mStagingDir.listFiles()) {
- file.delete();
- }
- }
- }
-
- private void updateRuleIndexingController() {
- File ruleIndexingFile = new File(mRulesDir, INDEXING_FILE);
- if (ruleIndexingFile.exists()) {
- try (FileInputStream inputStream = new FileInputStream(ruleIndexingFile)) {
- mRuleIndexingController = new RuleIndexingController(inputStream);
- } catch (Exception e) {
- Slog.e(TAG, "Error parsing the rule indexing file.", e);
- }
- }
- }
-
- private void writeMetadata(File directory, String ruleProvider, String version)
- throws IOException {
- mRuleMetadataCache = new RuleMetadata(ruleProvider, version);
-
- File metadataFile = new File(directory, METADATA_FILE);
-
- try (FileOutputStream outputStream = new FileOutputStream(metadataFile)) {
- RuleMetadataSerializer.serialize(mRuleMetadataCache, outputStream);
- }
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java b/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
deleted file mode 100644
index f09e035e..0000000
--- a/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-
-import android.content.integrity.IntegrityUtils;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Helper methods for reading standard data structures from {@link BitInputStream}.
- */
-public class BinaryFileOperations {
-
- /**
- * Read an string value with the given size and hash status from a {@code BitInputStream}.
- *
- * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
- * All hashed values are hex-encoded.
- */
- public static String getStringValue(BitInputStream bitInputStream) throws IOException {
- boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
- int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
- return getStringValue(bitInputStream, valueSize, isHashedValue);
- }
-
- /**
- * Read an string value with the given size and hash status from a {@code BitInputStream}.
- *
- * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
- * All hashed values are hex-encoded.
- */
- public static String getStringValue(
- BitInputStream bitInputStream, int valueSize, boolean isHashedValue)
- throws IOException {
- if (!isHashedValue) {
- StringBuilder value = new StringBuilder();
- while (valueSize-- > 0) {
- value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
- }
- return value.toString();
- }
- ByteBuffer byteBuffer = ByteBuffer.allocate(valueSize);
- while (valueSize-- > 0) {
- byteBuffer.put((byte) (bitInputStream.getNext(/* numOfBits= */ 8) & 0xFF));
- }
- return IntegrityUtils.getHexDigest(byteBuffer.array());
- }
-
- /** Read an integer value from a {@code BitInputStream}. */
- public static int getIntValue(BitInputStream bitInputStream) throws IOException {
- return bitInputStream.getNext(/* numOfBits= */ 32);
- }
-
- /** Read an boolean value from a {@code BitInputStream}. */
- public static boolean getBooleanValue(BitInputStream bitInputStream) throws IOException {
- return bitInputStream.getNext(/* numOfBits= */ 1) == 1;
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/LimitInputStream.java b/services/core/java/com/android/server/integrity/parser/LimitInputStream.java
deleted file mode 100644
index a91bbb7..0000000
--- a/services/core/java/com/android/server/integrity/parser/LimitInputStream.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-/** An {@link InputStream} that basically truncates another {@link InputStream} */
-public class LimitInputStream extends FilterInputStream {
- private int mReadBytes;
- private final int mLimit;
-
- public LimitInputStream(InputStream in, int limit) {
- super(in);
- if (limit < 0) {
- throw new IllegalArgumentException("limit " + limit + " cannot be negative");
- }
- mReadBytes = 0;
- mLimit = limit;
- }
-
- @Override
- public int available() throws IOException {
- return Math.min(super.available(), mLimit - mReadBytes);
- }
-
- @Override
- public int read() throws IOException {
- if (mReadBytes == mLimit) {
- return -1;
- }
- mReadBytes++;
- return super.read();
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- return read(b, 0, b.length);
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- if (len <= 0) {
- return 0;
- }
- int available = available();
- if (available <= 0) {
- return -1;
- }
- int result = super.read(b, off, Math.min(len, available));
- mReadBytes += result;
- return result;
- }
-
- @Override
- public long skip(long n) throws IOException {
- if (n <= 0) {
- return 0;
- }
- int available = available();
- if (available <= 0) {
- return 0;
- }
- int bytesToSkip = (int) Math.min(available, n);
- long bytesSkipped = super.skip(bytesToSkip);
- mReadBytes += (int) bytesSkipped;
- return bytesSkipped;
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RandomAccessInputStream.java b/services/core/java/com/android/server/integrity/parser/RandomAccessInputStream.java
deleted file mode 100644
index 206e6a1..0000000
--- a/services/core/java/com/android/server/integrity/parser/RandomAccessInputStream.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/** A wrapper around {@link RandomAccessObject} to turn it into a {@link InputStream}. */
-public class RandomAccessInputStream extends InputStream {
-
- private final RandomAccessObject mRandomAccessObject;
-
- private int mPosition;
-
- public RandomAccessInputStream(RandomAccessObject object) throws IOException {
- mRandomAccessObject = object;
- mPosition = 0;
- }
-
- /** Returns the position of the file pointer. */
- public int getPosition() {
- return mPosition;
- }
-
- /** See {@link RandomAccessObject#seek(int)} */
- public void seek(int position) throws IOException {
- mRandomAccessObject.seek(position);
- mPosition = position;
- }
-
- @Override
- public int available() throws IOException {
- return mRandomAccessObject.length() - mPosition;
- }
-
- @Override
- public void close() throws IOException {
- mRandomAccessObject.close();
- }
-
- @Override
- public int read() throws IOException {
- if (available() <= 0) {
- return -1;
- }
- mPosition++;
- return mRandomAccessObject.read();
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- return read(b, 0, b.length);
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- if (len <= 0) {
- return 0;
- }
- int available = available();
- if (available <= 0) {
- return -1;
- }
- int result = mRandomAccessObject.read(b, off, Math.min(len, available));
- mPosition += result;
- return result;
- }
-
- @Override
- public long skip(long n) throws IOException {
- if (n <= 0) {
- return 0;
- }
- int available = available();
- if (available <= 0) {
- return 0;
- }
- int skipAmount = (int) Math.min(available, n);
- mPosition += skipAmount;
- mRandomAccessObject.seek(mPosition);
- return skipAmount;
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RandomAccessObject.java b/services/core/java/com/android/server/integrity/parser/RandomAccessObject.java
deleted file mode 100644
index d9b2e38..0000000
--- a/services/core/java/com/android/server/integrity/parser/RandomAccessObject.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-
-/** An interface for random access objects like RandomAccessFile or byte arrays. */
-public abstract class RandomAccessObject {
-
- /** See {@link RandomAccessFile#seek(long)}. */
- public abstract void seek(int position) throws IOException;
-
- /** See {@link RandomAccessFile#read()}. */
- public abstract int read() throws IOException;
-
- /** See {@link RandomAccessFile#read(byte[], int, int)}. */
- public abstract int read(byte[] bytes, int off, int len) throws IOException;
-
- /** See {@link RandomAccessFile#close()}. */
- public abstract void close() throws IOException;
-
- /** See {@link java.io.RandomAccessFile#length()}. */
- public abstract int length();
-
- /** Static constructor from a file. */
- public static RandomAccessObject ofFile(File file) throws IOException {
- return new RandomAccessFileObject(file);
- }
-
- /** Static constructor from a byte array. */
- public static RandomAccessObject ofBytes(byte[] bytes) {
- return new RandomAccessByteArrayObject(bytes);
- }
-
- private static class RandomAccessFileObject extends RandomAccessObject {
- private final RandomAccessFile mRandomAccessFile;
- // We cache the length since File.length() invokes file IO.
- private final int mLength;
-
- RandomAccessFileObject(File file) throws IOException {
- long length = file.length();
- if (length > Integer.MAX_VALUE) {
- throw new IOException("Unsupported file size (too big) " + length);
- }
-
- mRandomAccessFile = new RandomAccessFile(file, /* mode= */ "r");
- mLength = (int) length;
- }
-
- @Override
- public void seek(int position) throws IOException {
- mRandomAccessFile.seek(position);
- }
-
- @Override
- public int read() throws IOException {
- return mRandomAccessFile.read();
- }
-
- @Override
- public int read(byte[] bytes, int off, int len) throws IOException {
- return mRandomAccessFile.read(bytes, off, len);
- }
-
- @Override
- public void close() throws IOException {
- mRandomAccessFile.close();
- }
-
- @Override
- public int length() {
- return mLength;
- }
- }
-
- private static class RandomAccessByteArrayObject extends RandomAccessObject {
-
- private final ByteBuffer mBytes;
-
- RandomAccessByteArrayObject(byte[] bytes) {
- mBytes = ByteBuffer.wrap(bytes);
- }
-
- @Override
- public void seek(int position) throws IOException {
- mBytes.position(position);
- }
-
- @Override
- public int read() throws IOException {
- if (!mBytes.hasRemaining()) {
- return -1;
- }
-
- return mBytes.get() & 0xFF;
- }
-
- @Override
- public int read(byte[] bytes, int off, int len) throws IOException {
- int bytesToCopy = Math.min(len, mBytes.remaining());
- if (bytesToCopy <= 0) {
- return 0;
- }
- mBytes.get(bytes, off, len);
- return bytesToCopy;
- }
-
- @Override
- public void close() throws IOException {}
-
- @Override
- public int length() {
- return mBytes.capacity();
- }
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
deleted file mode 100644
index ea3a3d5..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * 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 com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.BYTE_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START;
-import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SIGNAL_BIT;
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
-
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.InstallerAllowedByManifestFormula;
-import android.content.integrity.IntegrityFormula;
-import android.content.integrity.Rule;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** A helper class to parse rules into the {@link Rule} model from Binary representation. */
-public class RuleBinaryParser implements RuleParser {
-
- @Override
- public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
- return parse(RandomAccessObject.ofBytes(ruleBytes), Collections.emptyList());
- }
-
- @Override
- public List<Rule> parse(RandomAccessObject randomAccessObject, List<RuleIndexRange> indexRanges)
- throws RuleParseException {
- try (RandomAccessInputStream randomAccessInputStream =
- new RandomAccessInputStream(randomAccessObject)) {
- return parseRules(randomAccessInputStream, indexRanges);
- } catch (Exception e) {
- throw new RuleParseException(e.getMessage(), e);
- }
- }
-
- private List<Rule> parseRules(
- RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges)
- throws IOException {
-
- // Read the rule binary file format version.
- randomAccessInputStream.skip(FORMAT_VERSION_BITS / BYTE_BITS);
-
- return indexRanges.isEmpty()
- ? parseAllRules(randomAccessInputStream)
- : parseIndexedRules(randomAccessInputStream, indexRanges);
- }
-
- private List<Rule> parseAllRules(RandomAccessInputStream randomAccessInputStream)
- throws IOException {
- List<Rule> parsedRules = new ArrayList<>();
-
- BitInputStream inputStream =
- new BitInputStream(new BufferedInputStream(randomAccessInputStream));
- while (inputStream.hasNext()) {
- if (inputStream.getNext(SIGNAL_BIT) == 1) {
- parsedRules.add(parseRule(inputStream));
- }
- }
-
- return parsedRules;
- }
-
- private List<Rule> parseIndexedRules(
- RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges)
- throws IOException {
- List<Rule> parsedRules = new ArrayList<>();
-
- for (RuleIndexRange range : indexRanges) {
- randomAccessInputStream.seek(range.getStartIndex());
-
- BitInputStream inputStream =
- new BitInputStream(
- new BufferedInputStream(
- new LimitInputStream(
- randomAccessInputStream,
- range.getEndIndex() - range.getStartIndex())));
-
- // Read the rules until we reach the end index. available() here is not reliable.
- while (inputStream.hasNext()) {
- if (inputStream.getNext(SIGNAL_BIT) == 1) {
- parsedRules.add(parseRule(inputStream));
- }
- }
- }
-
- return parsedRules;
- }
-
- private Rule parseRule(BitInputStream bitInputStream) throws IOException {
- IntegrityFormula formula = parseFormula(bitInputStream);
- int effect = bitInputStream.getNext(EFFECT_BITS);
-
- if (bitInputStream.getNext(SIGNAL_BIT) != 1) {
- throw new IllegalArgumentException("A rule must end with a '1' bit.");
- }
-
- return new Rule(formula, effect);
- }
-
- private IntegrityFormula parseFormula(BitInputStream bitInputStream) throws IOException {
- int separator = bitInputStream.getNext(SEPARATOR_BITS);
- switch (separator) {
- case ATOMIC_FORMULA_START:
- return parseAtomicFormula(bitInputStream);
- case COMPOUND_FORMULA_START:
- return parseCompoundFormula(bitInputStream);
- case COMPOUND_FORMULA_END:
- return null;
- case INSTALLER_ALLOWED_BY_MANIFEST_START:
- return new InstallerAllowedByManifestFormula();
- default:
- throw new IllegalArgumentException(
- String.format("Unknown formula separator: %s", separator));
- }
- }
-
- private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) throws IOException {
- int connector = bitInputStream.getNext(CONNECTOR_BITS);
- List<IntegrityFormula> formulas = new ArrayList<>();
-
- IntegrityFormula parsedFormula = parseFormula(bitInputStream);
- while (parsedFormula != null) {
- formulas.add(parsedFormula);
- parsedFormula = parseFormula(bitInputStream);
- }
-
- return new CompoundFormula(connector, formulas);
- }
-
- private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) throws IOException {
- int key = bitInputStream.getNext(KEY_BITS);
- int operator = bitInputStream.getNext(OPERATOR_BITS);
-
- switch (key) {
- case AtomicFormula.PACKAGE_NAME:
- case AtomicFormula.APP_CERTIFICATE:
- case AtomicFormula.APP_CERTIFICATE_LINEAGE:
- case AtomicFormula.INSTALLER_NAME:
- case AtomicFormula.INSTALLER_CERTIFICATE:
- case AtomicFormula.STAMP_CERTIFICATE_HASH:
- boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
- int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
- String stringValue = getStringValue(bitInputStream, valueSize, isHashedValue);
- return new AtomicFormula.StringAtomicFormula(key, stringValue, isHashedValue);
- case AtomicFormula.VERSION_CODE:
- // TODO(b/147880712): temporary hack until our input handles long
- long upper = getIntValue(bitInputStream);
- long lower = getIntValue(bitInputStream);
- long longValue = (upper << 32) | lower;
- return new AtomicFormula.LongAtomicFormula(key, operator, longValue);
- case AtomicFormula.PRE_INSTALLED:
- case AtomicFormula.STAMP_TRUSTED:
- boolean booleanValue = getBooleanValue(bitInputStream);
- return new AtomicFormula.BooleanAtomicFormula(key, booleanValue);
- default:
- throw new IllegalArgumentException(String.format("Unknown key: %d", key));
- }
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
deleted file mode 100644
index 595a035..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import android.annotation.Nullable;
-
-/**
- * A wrapper class to represent an indexing range that is identified by the {@link
- * RuleIndexingController}.
- */
-public class RuleIndexRange {
- private int mStartIndex;
- private int mEndIndex;
-
- /** Constructor with start and end indexes. */
- public RuleIndexRange(int startIndex, int endIndex) {
- this.mStartIndex = startIndex;
- this.mEndIndex = endIndex;
- }
-
- /** Returns the startIndex. */
- public int getStartIndex() {
- return mStartIndex;
- }
-
- /** Returns the end index. */
- public int getEndIndex() {
- return mEndIndex;
- }
-
- @Override
- public boolean equals(@Nullable Object object) {
- return mStartIndex == ((RuleIndexRange) object).getStartIndex()
- && mEndIndex == ((RuleIndexRange) object).getEndIndex();
- }
-
- @Override
- public String toString() {
- return String.format("Range{%d, %d}", mStartIndex, mEndIndex);
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
deleted file mode 100644
index 348a03b..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
-import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
-import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
-
-import android.content.integrity.AppInstallMetadata;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/** Helper class to identify the necessary indexes that needs to be read. */
-public class RuleIndexingController {
-
- private static LinkedHashMap<String, Integer> sPackageNameBasedIndexes;
- private static LinkedHashMap<String, Integer> sAppCertificateBasedIndexes;
- private static LinkedHashMap<String, Integer> sUnindexedRuleIndexes;
-
- /**
- * Provide the indexing file to read and the object will be constructed by reading and
- * identifying the indexes.
- */
- public RuleIndexingController(InputStream inputStream) throws IOException {
- BitInputStream bitInputStream = new BitInputStream(inputStream);
- sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream);
- sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream);
- sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream);
- }
-
- /**
- * Returns a list of integers with the starting and ending bytes of the rules that needs to be
- * read and evaluated.
- */
- public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
- List<RuleIndexRange> indexRanges = new ArrayList<>();
-
- // Add the range for package name indexes rules.
- indexRanges.add(
- searchIndexingKeysRangeContainingKey(
- sPackageNameBasedIndexes, appInstallMetadata.getPackageName()));
-
- // Add the range for app certificate indexes rules of all certificates.
- for (String appCertificate : appInstallMetadata.getAppCertificates()) {
- indexRanges.add(
- searchIndexingKeysRangeContainingKey(
- sAppCertificateBasedIndexes, appCertificate));
- }
-
- // Add the range for unindexed rules.
- indexRanges.add(
- new RuleIndexRange(
- sUnindexedRuleIndexes.get(START_INDEXING_KEY),
- sUnindexedRuleIndexes.get(END_INDEXING_KEY)));
-
- return indexRanges;
- }
-
- private LinkedHashMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
- throws IOException {
- LinkedHashMap<String, Integer> keyToIndexMap = new LinkedHashMap<>();
- while (bitInputStream.hasNext()) {
- String key = getStringValue(bitInputStream);
- int value = getIntValue(bitInputStream);
-
- keyToIndexMap.put(key, value);
-
- if (key.matches(END_INDEXING_KEY)) {
- break;
- }
- }
- if (keyToIndexMap.size() < 2) {
- throw new IllegalStateException("Indexing file is corrupt.");
- }
- return keyToIndexMap;
- }
-
- private static RuleIndexRange searchIndexingKeysRangeContainingKey(
- LinkedHashMap<String, Integer> indexMap, String searchedKey) {
- List<String> keys = indexMap.keySet().stream().collect(Collectors.toList());
- List<String> identifiedKeyRange =
- searchKeysRangeContainingKey(keys, searchedKey, 0, keys.size() - 1);
- return new RuleIndexRange(
- indexMap.get(identifiedKeyRange.get(0)), indexMap.get(identifiedKeyRange.get(1)));
- }
-
- private static List<String> searchKeysRangeContainingKey(
- List<String> sortedKeyList, String key, int startIndex, int endIndex) {
- if (endIndex <= startIndex) {
- throw new IllegalStateException("Indexing file is corrupt.");
- }
- if (endIndex - startIndex == 1) {
- return Arrays.asList(sortedKeyList.get(startIndex), sortedKeyList.get(endIndex));
- }
-
- int midKeyIndex = startIndex + ((endIndex - startIndex) / 2);
- String midKey = sortedKeyList.get(midKeyIndex);
-
- if (key.compareTo(midKey) >= 0) {
- return searchKeysRangeContainingKey(sortedKeyList, key, midKeyIndex, endIndex);
- } else {
- return searchKeysRangeContainingKey(sortedKeyList, key, startIndex, midKeyIndex);
- }
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleParseException.java b/services/core/java/com/android/server/integrity/parser/RuleParseException.java
deleted file mode 100644
index c0f36a6..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleParseException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 com.android.server.integrity.parser;
-
-import android.annotation.NonNull;
-
-/**
- * Thrown when rule parsing fails.
- */
-public class RuleParseException extends Exception {
- public RuleParseException(@NonNull String message) {
- super(message);
- }
-
- public RuleParseException(@NonNull String message, @NonNull Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleParser.java b/services/core/java/com/android/server/integrity/parser/RuleParser.java
deleted file mode 100644
index 126dacc..0000000
--- a/services/core/java/com/android/server/integrity/parser/RuleParser.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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 com.android.server.integrity.parser;
-
-import android.content.integrity.Rule;
-
-import java.util.List;
-
-/** A helper class to parse rules into the {@link Rule} model. */
-public interface RuleParser {
-
- /** Parse rules from bytes. */
- List<Rule> parse(byte[] ruleBytes) throws RuleParseException;
-
- /** Parse rules from an input stream. */
- List<Rule> parse(RandomAccessObject randomAccessObject, List<RuleIndexRange> ruleIndexRanges)
- throws RuleParseException;
-}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 542a29a..4a9bf88 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -2584,6 +2584,9 @@
registration -> {
if (registration.getIdentity().getPackageName().equals(
packageName)) {
+ if (D) {
+ Log.d(TAG, "package reset remove registration " + registration);
+ }
registration.remove();
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index bbdac56..c314ab0 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -253,10 +253,10 @@
private static final String MIGRATED_FRP2 = "migrated_frp2";
private static final String MIGRATED_KEYSTORE_NS = "migrated_keystore_namespace";
- private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce";
private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys";
private static final String MIGRATED_WEAVER_DISABLED_ON_UNSECURED_USERS =
"migrated_weaver_disabled_on_unsecured_users";
+ // Note: some other migrated_* strings used to be used and may exist in the database already.
// Duration that LockSettingsService will store the gatekeeper password for. This allows
// multiple biometric enrollments without prompting the user to enter their password via
@@ -347,6 +347,8 @@
private final StorageManagerInternal mStorageManagerInternal;
+ private final Object mGcWorkToken = new Object();
+
// This class manages life cycle events for encrypted users on File Based Encryption (FBE)
// devices. The most basic of these is to show/hide notifications about missing features until
// the user unlocks the account and credential-encrypted storage is available.
@@ -1183,9 +1185,7 @@
// If config_disableWeaverOnUnsecuredUsers=true, then the Weaver HAL may be buggy and
// need multiple retries before it works here to unwrap the SP, if the SP was already
- // protected by Weaver. Note that the problematic HAL can also deadlock if called with
- // the ActivityManagerService lock held, but that should not be a problem here since
- // that lock isn't held here, unlike unlockUserKeyIfUnsecured() where it is.
+ // protected by Weaver.
for (int i = 0; i < 12 && sp == null; i++) {
Slog.e(TAG, "Failed to unwrap synthetic password. Waiting 5 seconds to retry.");
SystemClock.sleep(5000);
@@ -1221,21 +1221,16 @@
Slog.i(TAG, "Synthetic password is already not protected by Weaver");
}
} else if (sp == null) {
- Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
- return;
+ throw new IllegalStateException(
+ "Failed to unwrap synthetic password for unsecured user " + userId);
}
// Call setCeStorageProtection(), to re-encrypt the CE key with the SP if it's currently
- // encrypted by an empty secret. Skip this if it was definitely already done as part of the
- // upgrade to Android 14, since while setCeStorageProtection() is idempotent it does log
- // some error messages when called again. Do not skip this if
- // config_disableWeaverOnUnsecuredUsers=true, since in that case we'd like to recover from
- // the case where an earlier upgrade to Android 14 incorrectly skipped this step.
- if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null
- || isWeaverDisabledOnUnsecuredUsers()) {
- Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
- setCeStorageProtection(userId, sp);
- }
+ // encrypted by an empty secret. If the CE key is already encrypted by the SP, then this is
+ // a no-op except for some log messages.
+ Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId);
+ setCeStorageProtection(userId, sp);
+
Slogf.i(TAG, "Initializing Keystore super keys for user %d", userId);
initKeystoreSuperKeys(userId, sp, /* allowExisting= */ true);
}
@@ -3639,11 +3634,19 @@
* release references to the argument.
*/
private void scheduleGc() {
+ // Cancel any existing GC request first, so that GC requests don't pile up if lockscreen
+ // credential operations are happening very quickly, e.g. as sometimes happens during tests.
+ //
+ // This delays the already-requested GC, but that is fine in practice where lockscreen
+ // operations don't happen very quickly. And the precise time that the sanitization happens
+ // isn't very important; doing it within a minute can be fine, for example.
+ mHandler.removeCallbacksAndMessages(mGcWorkToken);
+
mHandler.postDelayed(() -> {
System.gc();
System.runFinalization();
System.gc();
- }, 2000);
+ }, mGcWorkToken, 2000);
}
private class DeviceProvisionedObserver extends ContentObserver {
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index 980f40e..9b02ed0 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -1597,6 +1597,9 @@
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
if (action.equals(Intent.ACTION_SCREEN_ON)) {
// Keep track of screen on/off state, but do not turn off the notification light
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 69c78eb..f9c1037 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4283,6 +4283,10 @@
if (intent == null) {
return;
}
+ final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
Uri data = intent.getData();
if (data == null) {
return;
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 881bdbd..15fd35e 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -604,6 +604,11 @@
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action == null) {
+ Slog.w(TAG, "Intent broadcast does not contain action: " + intent);
+ return;
+ }
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
if (userId == UserHandle.USER_NULL) {
Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
@@ -615,7 +620,7 @@
Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
return;
}
- switch (intent.getAction()) {
+ switch (action) {
case Intent.ACTION_PACKAGE_REMOVED:
final boolean replacing =
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS
index 2441e77..937699a 100644
--- a/services/core/java/com/android/server/vcn/OWNERS
+++ b/services/core/java/com/android/server/vcn/OWNERS
@@ -1,7 +1,6 @@
set noparent
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected] #{LAST_RESORT_SUGGESTION}
[email protected] #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index baf84cf..3392d03 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -436,6 +436,17 @@
return mPrivilegedPackages.keySet();
}
+ /** Returns all subscription groups */
+ @NonNull
+ public Set<ParcelUuid> getAllSubscriptionGroups() {
+ final Set<ParcelUuid> subGroups = new ArraySet<>();
+ for (SubscriptionInfo subInfo : mSubIdToInfoMap.values()) {
+ subGroups.add(subInfo.getGroupUuid());
+ }
+
+ return subGroups;
+ }
+
/** Checks if the provided package is carrier privileged for the specified sub group. */
public boolean packageHasPermissionsForSubscriptionGroup(
@NonNull ParcelUuid subGrp, @NonNull String packageName) {
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index a492a72..6ce8685 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.net.IpSecTransformState;
import android.net.vcn.FeatureFlags;
import android.net.vcn.FeatureFlagsImpl;
import android.os.Looper;
@@ -70,19 +69,6 @@
return mIsInTestMode;
}
- public boolean isFlagIpSecTransformStateEnabled() {
- // TODO: b/328844044: Ideally this code should gate the behavior by checking the
- // android.net.platform.flags.ipsec_transform_state flag but that flag is not accessible
- // right now. We should either update the code when the flag is accessible or remove the
- // legacy behavior after VIC SDK finalization
- try {
- new IpSecTransformState.Builder();
- return true;
- } catch (Exception e) {
- return false;
- }
- }
-
@NonNull
public FeatureFlags getFeatureFlags() {
return mFeatureFlags;
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 189b608..2d3bc84 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1912,8 +1912,7 @@
// Transforms do not need to be persisted; the IkeSession will keep them alive
mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform);
- if (direction == IpSecManager.DIRECTION_IN
- && mVcnContext.isFlagIpSecTransformStateEnabled()) {
+ if (direction == IpSecManager.DIRECTION_IN) {
mUnderlyingNetworkController.updateInboundTransform(mUnderlying, transform);
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 6f1e15b..16ab51e 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -148,12 +148,6 @@
Objects.requireNonNull(deps, "Missing deps");
- if (!vcnContext.isFlagIpSecTransformStateEnabled()) {
- // Caller error
- logWtf("ipsecTransformState flag disabled");
- throw new IllegalAccessException("ipsecTransformState flag disabled");
- }
-
mHandler = new Handler(getVcnContext().getLooper());
mPowerManager = getVcnContext().getContext().getSystemService(PowerManager.class);
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index 0b9b677..3eeeece 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -204,10 +204,8 @@
List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
mCellBringupCallbacks.clear();
- if (mVcnContext.isFlagIpSecTransformStateEnabled()) {
- for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
- evaluator.close();
- }
+ for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) {
+ evaluator.close();
}
mUnderlyingNetworkRecords.clear();
@@ -429,10 +427,7 @@
if (oldSnapshot
.getAllSubIdsInGroup(mSubscriptionGroup)
.equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) {
-
- if (mVcnContext.isFlagIpSecTransformStateEnabled()) {
- reevaluateNetworks();
- }
+ reevaluateNetworks();
return;
}
registerOrUpdateNetworkRequests();
@@ -445,11 +440,6 @@
*/
public void updateInboundTransform(
@NonNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform) {
- if (!mVcnContext.isFlagIpSecTransformStateEnabled()) {
- logWtf("#updateInboundTransform: unexpected call; flags missing");
- return;
- }
-
Objects.requireNonNull(currentNetwork, "currentNetwork is null");
Objects.requireNonNull(transform, "transform is null");
@@ -572,10 +562,7 @@
@Override
public void onLost(@NonNull Network network) {
- if (mVcnContext.isFlagIpSecTransformStateEnabled()) {
- mUnderlyingNetworkRecords.get(network).close();
- }
-
+ mUnderlyingNetworkRecords.get(network).close();
mUnderlyingNetworkRecords.remove(network);
reevaluateNetworks();
@@ -648,11 +635,6 @@
class NetworkEvaluatorCallbackImpl implements NetworkEvaluatorCallback {
@Override
public void onEvaluationResultChanged() {
- if (!mVcnContext.isFlagIpSecTransformStateEnabled()) {
- logWtf("#onEvaluationResultChanged: unexpected call; flags missing");
- return;
- }
-
mVcnContext.ensureRunningOnLooperThread();
reevaluateNetworks();
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
index 448a7eb..08be11e 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -102,17 +102,15 @@
updatePriorityClass(
underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig);
- if (isIpSecPacketLossDetectorEnabled()) {
- try {
- mMetricMonitors.add(
- mDependencies.newIpSecPacketLossDetector(
- mVcnContext,
- mNetworkRecordBuilder.getNetwork(),
- carrierConfig,
- new MetricMonitorCallbackImpl()));
- } catch (IllegalAccessException e) {
- // No action. Do not add anything to mMetricMonitors
- }
+ try {
+ mMetricMonitors.add(
+ mDependencies.newIpSecPacketLossDetector(
+ mVcnContext,
+ mNetworkRecordBuilder.getNetwork(),
+ carrierConfig,
+ new MetricMonitorCallbackImpl()));
+ } catch (IllegalAccessException e) {
+ // No action. Do not add anything to mMetricMonitors
}
}
@@ -188,22 +186,12 @@
}
}
- private boolean isIpSecPacketLossDetectorEnabled() {
- return isIpSecPacketLossDetectorEnabled(mVcnContext);
- }
-
- private static boolean isIpSecPacketLossDetectorEnabled(VcnContext vcnContext) {
- return vcnContext.isFlagIpSecTransformStateEnabled();
- }
-
/** Get the comparator for UnderlyingNetworkEvaluator */
public static Comparator<UnderlyingNetworkEvaluator> getComparator(VcnContext vcnContext) {
return (left, right) -> {
- if (isIpSecPacketLossDetectorEnabled(vcnContext)) {
- if (left.mIsPenalized != right.mIsPenalized) {
- // A penalized network should have lower priority which means a larger index
- return left.mIsPenalized ? 1 : -1;
- }
+ if (left.mIsPenalized != right.mIsPenalized) {
+ // A penalized network should have lower priority which means a larger index
+ return left.mIsPenalized ? 1 : -1;
}
final int leftIndex = left.mPriorityClass;
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 043470f..9ee42b1 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -84,8 +84,13 @@
mWebViewUpdatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- switch (intent.getAction()) {
+ switch (action) {
case Intent.ACTION_PACKAGE_REMOVED:
// When a package is replaced we will receive two intents, one
// representing the removal of the old package and one representing the
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 5d2c50c..4310231 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -31,3 +31,7 @@
# Files related to tracing
per-file *TransitionTracer.java = file:platform/development:/tools/winscope/OWNERS
+
+# Files related to activity security
+per-file ActivityStarter.java = file:/ACTIVITY_SECURITY_OWNERS
+per-file ActivityTaskManagerService.java = file:/ACTIVITY_SECURITY_OWNERS
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cae941f..52a2fd6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2970,8 +2970,13 @@
|| context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_RTT)) {
t.traceBegin("RangingService");
- mSystemServiceManager.startServiceFromJar(RANGING_SERVICE_CLASS,
- RANGING_APEX_SERVICE_JAR_PATH);
+ // TODO: b/375264320 - Remove after RELEASE_RANGING_STACK is ramped to next.
+ try {
+ mSystemServiceManager.startServiceFromJar(RANGING_SERVICE_CLASS,
+ RANGING_APEX_SERVICE_JAR_PATH);
+ } catch (Throwable e) {
+ Slog.d(TAG, "service-ranging.jar not found, not starting RangingService");
+ }
t.traceEnd();
}
}
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 105147f..4e86888 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -16,6 +16,8 @@
package com.android.server.profcollect;
+import android.Manifest;
+import android.annotation.RequiresPermission;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
@@ -26,6 +28,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.camera2.CameraManager;
+import android.hardware.usb.UsbManager;
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
@@ -67,6 +70,8 @@
private int mUsageSetting;
private boolean mUploadEnabled;
+ private boolean mAdbActive;
+
private IProfCollectd mIProfcollect;
private static ProfcollectForwardingService sSelfService;
private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
@@ -84,6 +89,15 @@
Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
createAndUploadReport(sSelfService);
}
+ if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
+ boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
+ if (isADB) {
+ boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+ Log.d(LOG_TAG, "Received broadcast that ADB became " + connected
+ + ", was " + mAdbActive);
+ mAdbActive = connected;
+ }
+ }
}
};
@@ -108,6 +122,7 @@
final IntentFilter filter = new IntentFilter();
filter.addAction(INTENT_UPLOAD_PROFILES);
+ filter.addAction(UsbManager.ACTION_USB_STATE);
context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
}
@@ -125,7 +140,13 @@
}
@Override
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
public void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ UsbManager usbManager = getContext().getSystemService(UsbManager.class);
+ mAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1);
+ Log.d(LOG_TAG, "ADB is " + mAdbActive + " on system startup");
+ }
if (phase == PHASE_BOOT_COMPLETED) {
if (mIProfcollect == null) {
return;
@@ -281,6 +302,9 @@
if (mIProfcollect == null) {
return;
}
+ if (mAdbActive) {
+ return;
+ }
if (Utils.withFrequency("applaunch_trace_freq", 5)) {
Utils.traceSystem(mIProfcollect, "applaunch");
}
@@ -303,6 +327,9 @@
if (mIProfcollect == null) {
return;
}
+ if (mAdbActive) {
+ return;
+ }
if (Utils.withFrequency("dex2oat_trace_freq", 25)) {
// Dex2oat could take a while before it starts. Add a short delay before start tracing.
Utils.traceSystem(mIProfcollect, "dex2oat", /* delayMs */ 1000);
diff --git a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
index 682ed91..8e10136 100644
--- a/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
+++ b/services/tests/BackgroundInstallControlServiceTests/host/Android.bp
@@ -28,7 +28,7 @@
"compatibility-tradefed",
"compatibility-host-util",
],
- data: [
+ device_common_data: [
":BackgroundInstallControlServiceTestApp",
":BackgroundInstallControlMockApp1",
":BackgroundInstallControlMockApp2",
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index b46a6ff..6ad40f4 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -44,7 +44,7 @@
"block_device_writer_jar",
],
test_suites: ["device-tests"],
- data: [
+ device_common_data: [
":PackageManagerTestApex",
":PackageManagerTestApexApp",
":PackageManagerServiceServerTests",
@@ -53,7 +53,7 @@
"block_device_writer",
"fsverity_multilib",
],
- java_resources: [
+ device_common_java_resources: [
":PackageManagerTestOverlayActor",
":PackageManagerTestOverlay",
":PackageManagerTestOverlayTarget",
@@ -73,7 +73,7 @@
],
}
-genrule {
+java_genrule {
name: "PackageManagerTestAppVersion3Invalid",
tools: [
"soong_zip",
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
index 1d668cd..13cf125 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt
@@ -17,6 +17,7 @@
package com.android.server.pm.parsing
import android.content.pm.PackageManager
+import android.content.pm.parsing.ApkLiteParseUtils
import android.platform.test.annotations.Postsubmit
import com.android.internal.pm.parsing.PackageParserException
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
@@ -81,8 +82,10 @@
val exceptions = buildApks()
.map {
runCatching {
- parser.parsePackage(
+ if (ApkLiteParseUtils.isApkFile(it) || it.isDirectory()) {
+ parser.parsePackage(
it, ParsingPackageUtils.PARSE_IS_SYSTEM_DIR, false /*useCaches*/)
+ }
}
}
.mapNotNull { it.exceptionOrNull() }
diff --git a/services/tests/apexsystemservices/Android.bp b/services/tests/apexsystemservices/Android.bp
index 9dacfea..d0a2eb8 100644
--- a/services/tests/apexsystemservices/Android.bp
+++ b/services/tests/apexsystemservices/Android.bp
@@ -27,7 +27,7 @@
name: "ApexSystemServicesTestCases",
srcs: ["src/**/*.java"],
libs: ["tradefed"],
- java_resources: [
+ device_common_java_resources: [
":test_com.android.server",
],
static_libs: [
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
index 4fac647..809b7bb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
@@ -2,3 +2,4 @@
per-file ApplicationStartInfoTest.java = [email protected], [email protected], [email protected]
per-file CachedAppOptimizerTest.java = file:/PERFORMANCE_OWNERS
+per-file BaseBroadcastQueueTest.java = file:/BROADCASTS_OWNERS
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index cedf9db..631f76a 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -65,6 +65,7 @@
"androidx.annotation_annotation",
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
+ "androidx.test.ext.junit",
"modules-utils-binary-xml",
"flag-junit",
],
diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING
index 4e209f4..d3d3cf6 100644
--- a/services/tests/powerstatstests/TEST_MAPPING
+++ b/services/tests/powerstatstests/TEST_MAPPING
@@ -9,8 +9,7 @@
"name": "PowerStatsTestsRavenwood",
"host": true,
"options": [
- {"include-filter": "com.android.server.power.stats"},
- {"exclude-annotation": "android.platform.test.annotations.DisabledOnRavenwood"}
+ {"include-filter": "com.android.server.power.stats"}
]
}
],
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
deleted file mode 100644
index 096c80b..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity;
-
-import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.AppInstallMetadata;
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.AtomicFormula.LongAtomicFormula;
-import android.content.integrity.AtomicFormula.StringAtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.Rule;
-import android.util.Slog;
-
-import com.android.server.integrity.parser.RuleBinaryParser;
-import com.android.server.integrity.serializer.RuleBinarySerializer;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/** Unit test for {@link IntegrityFileManager} */
-@RunWith(JUnit4.class)
-public class IntegrityFileManagerTest {
- private static final String TAG = "IntegrityFileManagerTest";
-
- private static final String VERSION = "version";
- private static final String RULE_PROVIDER = "rule_provider";
-
- private File mTmpDir;
-
- // under test
- private IntegrityFileManager mIntegrityFileManager;
-
- @Before
- public void setUp() throws Exception {
- mTmpDir = Files.createTempDirectory("IntegrityFileManagerTest").toFile();
- Slog.i(TAG, "Using temp directory " + mTmpDir);
-
- // Use Xml Parser/Serializer to help with debugging since we can just print the file.
- mIntegrityFileManager =
- new IntegrityFileManager(
- new RuleBinaryParser(), new RuleBinarySerializer(), mTmpDir);
- Files.walk(mTmpDir.toPath())
- .forEach(
- path -> {
- Slog.i(TAG, "before " + path);
- });
- }
-
- @After
- public void tearDown() throws Exception {
- Files.walk(mTmpDir.toPath())
- .forEach(
- path -> {
- Slog.i(TAG, "after " + path);
- });
- // Sorting paths in reverse order guarantees that we delete inside files before deleting
- // directory.
- Files.walk(mTmpDir.toPath())
- .sorted(Comparator.reverseOrder())
- .map(Path::toFile)
- .forEach(File::delete);
- }
-
- @Test
- public void testGetMetadata() throws Exception {
- assertThat(mIntegrityFileManager.readMetadata()).isNull();
- mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
-
- assertThat(mIntegrityFileManager.readMetadata()).isNotNull();
- assertThat(mIntegrityFileManager.readMetadata().getVersion()).isEqualTo(VERSION);
- assertThat(mIntegrityFileManager.readMetadata().getRuleProvider()).isEqualTo(RULE_PROVIDER);
- }
-
- @Test
- public void testIsInitialized() throws Exception {
- assertThat(mIntegrityFileManager.initialized()).isFalse();
- mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
- assertThat(mIntegrityFileManager.initialized()).isTrue();
- }
-
- @Test
- public void testGetRules() throws Exception {
- String packageName = "package";
- String packageCert = "cert";
- int version = 123;
- Rule packageNameRule = getPackageNameIndexedRule(packageName);
- Rule packageCertRule = getAppCertificateIndexedRule(packageCert);
- Rule versionCodeRule =
- new Rule(
- new LongAtomicFormula(
- AtomicFormula.VERSION_CODE, AtomicFormula.EQ, version),
- Rule.DENY);
- Rule randomRule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.OR,
- Arrays.asList(
- new StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- "abc",
- /* isHashedValue= */ false),
- new LongAtomicFormula(
- AtomicFormula.VERSION_CODE,
- AtomicFormula.EQ,
- version))),
- Rule.DENY);
-
- List<Rule> rules =
- Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule);
- mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName(packageName)
- .setAppCertificates(Collections.singletonList(packageCert))
- .setAppCertificateLineage(Collections.singletonList(packageCert))
- .setVersionCode(version)
- .setInstallerName("abc")
- .setInstallerCertificates(Collections.singletonList("abc"))
- .setIsPreInstalled(true)
- .build();
- List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
-
- assertThat(rulesFetched)
- .containsExactly(packageNameRule, packageCertRule, versionCodeRule, randomRule);
- }
-
- @Test
- public void testGetRules_indexedForManyRules() throws Exception {
- String packageName = "package";
- String installerName = "installer";
- String appCertificate = "cert";
-
- // Create a rule set with 2500 package name indexed, 2500 app certificate indexed and
- // 500 unindexed rules.
- List<Rule> rules = new ArrayList<>();
- int unindexedRuleCount = 70;
-
- for (int i = 0; i < 2500; i++) {
- rules.add(getPackageNameIndexedRule(String.format("%s%04d", packageName, i)));
- rules.add(getAppCertificateIndexedRule(String.format("%s%04d", appCertificate, i)));
- }
-
- for (int i = 0; i < unindexedRuleCount; i++) {
- rules.add(getInstallerCertificateRule(String.format("%s%04d", installerName, i)));
- }
-
- // Write the rules and get them indexed.
- mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules);
-
- // Read the rules for a specific rule.
- String installedPackageName = String.format("%s%04d", packageName, 264);
- String installedAppCertificate = String.format("%s%04d", appCertificate, 1264);
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName(installedPackageName)
- .setAppCertificates(Collections.singletonList(installedAppCertificate))
- .setAppCertificateLineage(
- Collections.singletonList(installedAppCertificate))
- .setVersionCode(250)
- .setInstallerName("abc")
- .setInstallerCertificates(Collections.singletonList("abc"))
- .setIsPreInstalled(true)
- .build();
- List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
-
- // Verify that we do not load all the rules and we have the necessary rules to evaluate.
- assertThat(rulesFetched.size())
- .isEqualTo(INDEXING_BLOCK_SIZE * 2 + unindexedRuleCount);
- assertThat(rulesFetched)
- .containsAtLeast(
- getPackageNameIndexedRule(installedPackageName),
- getAppCertificateIndexedRule(installedAppCertificate));
- }
-
- private Rule getPackageNameIndexedRule(String packageName) {
- return new Rule(
- new StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME, packageName, /* isHashedValue= */false),
- Rule.DENY);
- }
-
- private Rule getAppCertificateIndexedRule(String appCertificate) {
- return new Rule(
- new StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- appCertificate, /* isHashedValue= */ false),
- Rule.DENY);
- }
-
- private Rule getInstallerCertificateRule(String installerCert) {
- return new Rule(
- new StringAtomicFormula(
- AtomicFormula.INSTALLER_NAME, installerCert, /* isHashedValue= */false),
- Rule.DENY);
- }
-
- @Test
- public void testStagingDirectoryCleared() throws Exception {
- // We must push rules two times to ensure that staging directory is empty because we cleared
- // it, rather than because original rules directory is empty.
- mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
- mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
-
- assertStagingDirectoryCleared();
- }
-
- private void assertStagingDirectoryCleared() {
- File stagingDir = new File(mTmpDir, "integrity_staging");
- assertThat(stagingDir.exists()).isTrue();
- assertThat(stagingDir.isDirectory()).isTrue();
- assertThat(stagingDir.listFiles()).isEmpty();
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
deleted file mode 100644
index 723b6c5..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
-import static com.android.server.integrity.utils.TestUtils.getBits;
-import static com.android.server.integrity.utils.TestUtils.getBytes;
-import static com.android.server.integrity.utils.TestUtils.getValueBits;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.IntegrityUtils;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-@RunWith(JUnit4.class)
-public class BinaryFileOperationsTest {
-
- private static final String IS_NOT_HASHED = "0";
- private static final String IS_HASHED = "1";
- private static final String PACKAGE_NAME = "com.test.app";
- private static final String APP_CERTIFICATE = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
-
- @Test
- public void testGetStringValue() throws IOException {
- byte[] stringBytes =
- getBytes(
- IS_NOT_HASHED
- + getBits(PACKAGE_NAME.length(), VALUE_SIZE_BITS)
- + getValueBits(PACKAGE_NAME));
- BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(stringBytes));
-
- String resultString = getStringValue(inputStream);
-
- assertThat(resultString).isEqualTo(PACKAGE_NAME);
- }
-
- @Test
- public void testGetHashedStringValue() throws IOException {
- byte[] ruleBytes =
- getBytes(
- IS_HASHED
- + getBits(APP_CERTIFICATE.length(), VALUE_SIZE_BITS)
- + getValueBits(APP_CERTIFICATE));
- BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes));
-
- String resultString = getStringValue(inputStream);
-
- assertThat(resultString)
- .isEqualTo(IntegrityUtils.getHexDigest(
- APP_CERTIFICATE.getBytes(StandardCharsets.UTF_8)));
- }
-
- @Test
- public void testGetStringValue_withSizeAndHashingInfo() throws IOException {
- byte[] ruleBytes = getBytes(getValueBits(PACKAGE_NAME));
- BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes));
-
- String resultString = getStringValue(inputStream,
- PACKAGE_NAME.length(), /* isHashedValue= */false);
-
- assertThat(resultString).isEqualTo(PACKAGE_NAME);
- }
-
- @Test
- public void testGetIntValue() throws IOException {
- int randomValue = 15;
- byte[] ruleBytes = getBytes(getBits(randomValue, /* numOfBits= */ 32));
- BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes));
-
- assertThat(getIntValue(inputStream)).isEqualTo(randomValue);
- }
-
- @Test
- public void testGetBooleanValue_true() throws IOException {
- String booleanValue = "1";
- byte[] ruleBytes = getBytes(booleanValue);
- BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes));
-
- assertThat(getBooleanValue(inputStream)).isEqualTo(true);
- }
-
- @Test
- public void testGetBooleanValue_false() throws IOException {
- String booleanValue = "0";
- byte[] ruleBytes = getBytes(booleanValue);
- BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes));
-
- assertThat(getBooleanValue(inputStream)).isEqualTo(false);
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
deleted file mode 100644
index 03363a1..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
- * 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 com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
-import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
-import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
-import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.utils.TestUtils.getBits;
-import static com.android.server.integrity.utils.TestUtils.getBytes;
-import static com.android.server.integrity.utils.TestUtils.getValueBits;
-import static com.android.server.testutils.TestUtils.assertExpectException;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.integrity.AtomicFormula;
-import android.content.integrity.CompoundFormula;
-import android.content.integrity.IntegrityUtils;
-import android.content.integrity.Rule;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(JUnit4.class)
-public class RuleBinaryParserTest {
-
- private static final String COMPOUND_FORMULA_START_BITS =
- getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS);
- private static final String COMPOUND_FORMULA_END_BITS =
- getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
- private static final String ATOMIC_FORMULA_START_BITS =
- getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
- private static final int INVALID_FORMULA_SEPARATOR_VALUE = (1 << SEPARATOR_BITS) - 1;
- private static final String INVALID_FORMULA_SEPARATOR_BITS =
- getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS);
-
- private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS);
- private static final String AND = getBits(CompoundFormula.AND, CONNECTOR_BITS);
- private static final String OR = getBits(CompoundFormula.OR, CONNECTOR_BITS);
- private static final int INVALID_CONNECTOR_VALUE = 3;
- private static final String INVALID_CONNECTOR =
- getBits(INVALID_CONNECTOR_VALUE, CONNECTOR_BITS);
-
- private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS);
- private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS);
- private static final String APP_CERTIFICATE_LINEAGE =
- getBits(AtomicFormula.APP_CERTIFICATE_LINEAGE, KEY_BITS);
- private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS);
- private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS);
- private static final int INVALID_KEY_VALUE = 9;
- private static final String INVALID_KEY = getBits(INVALID_KEY_VALUE, KEY_BITS);
-
- private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
- private static final int INVALID_OPERATOR_VALUE = 5;
- private static final String INVALID_OPERATOR = getBits(INVALID_OPERATOR_VALUE, OPERATOR_BITS);
-
- private static final String IS_NOT_HASHED = "0";
- private static final String IS_HASHED = "1";
-
- private static final String DENY = getBits(Rule.DENY, EFFECT_BITS);
- private static final int INVALID_EFFECT_VALUE = 5;
- private static final String INVALID_EFFECT = getBits(INVALID_EFFECT_VALUE, EFFECT_BITS);
-
- private static final String START_BIT = "1";
- private static final String END_BIT = "1";
- private static final String INVALID_MARKER_BIT = "0";
-
- private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
- getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
-
- private static final List<RuleIndexRange> NO_INDEXING = Collections.emptyList();
-
- @Test
- public void testBinaryStream_validCompoundFormula_noIndexing() throws Exception {
- String packageName = "com.test.app";
- String ruleBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
- Rule expectedRule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.NOT,
- Collections.singletonList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false))),
- Rule.DENY);
-
- List<Rule> rules =
- binaryParser.parse(RandomAccessObject.ofBytes(rule.array()), NO_INDEXING);
-
- assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
- }
-
- @Test
- public void testBinaryString_validCompoundFormula_notConnector_noIndexing() throws Exception {
- String packageName = "com.test.app";
- String ruleBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
- Rule expectedRule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.NOT,
- Collections.singletonList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false))),
- Rule.DENY);
-
- List<Rule> rules = binaryParser.parse(rule.array());
-
- assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
- }
-
- @Test
- public void testBinaryString_validCompoundFormula_andConnector_noIndexing() throws Exception {
- String packageName = "com.test.app";
- String appCertificate = "test_cert";
- String ruleBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + AND
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + ATOMIC_FORMULA_START_BITS
- + APP_CERTIFICATE
- + EQ
- + IS_NOT_HASHED
- + getBits(appCertificate.length(), VALUE_SIZE_BITS)
- + getValueBits(appCertificate)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
- Rule expectedRule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.AND,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false),
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- appCertificate,
- /* isHashedValue= */ false))),
- Rule.DENY);
- List<Rule> rules = binaryParser.parse(rule.array());
-
- assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
- }
-
- @Test
- public void testBinaryString_validCompoundFormula_orConnector_noIndexing() throws Exception {
- String packageName = "com.test.app";
- String appCertificate = "test_cert";
- String ruleBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + OR
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + ATOMIC_FORMULA_START_BITS
- + APP_CERTIFICATE
- + EQ
- + IS_NOT_HASHED
- + getBits(appCertificate.length(), VALUE_SIZE_BITS)
- + getValueBits(appCertificate)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
- Rule expectedRule =
- new Rule(
- new CompoundFormula(
- CompoundFormula.OR,
- Arrays.asList(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false),
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- appCertificate,
- /* isHashedValue= */ false))),
- Rule.DENY);
-
- List<Rule> rules = binaryParser.parse(rule.array());
-
- assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
- }
-
- @Test
- public void testBinaryString_validAtomicFormula_stringValue_noIndexing() throws Exception {
- String packageName = "com.test.app";
- String ruleBits =
- START_BIT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
- Rule expectedRule =
- new Rule(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.PACKAGE_NAME,
- packageName,
- /* isHashedValue= */ false),
- Rule.DENY);
-
- List<Rule> rules = binaryParser.parse(rule.array());
-
- assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
- }
-
- @Test
- public void testBinaryString_validAtomicFormula_hashedValue_noIndexing() throws Exception {
- String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
- String ruleBits =
- START_BIT
- + ATOMIC_FORMULA_START_BITS
- + APP_CERTIFICATE
- + EQ
- + IS_HASHED
- + getBits(appCertificate.length(), VALUE_SIZE_BITS)
- + getValueBits(appCertificate)
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
-
- RuleParser binaryParser = new RuleBinaryParser();
- Rule expectedRule =
- new Rule(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE,
- IntegrityUtils.getHexDigest(
- appCertificate.getBytes(StandardCharsets.UTF_8)),
- /* isHashedValue= */ true),
- Rule.DENY);
-
- List<Rule> rules = binaryParser.parse(rule.array());
-
- assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
- }
-
- @Test
- public void testBinaryString_validAtomicFormulaWithCertificateLineage() throws Exception {
- String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
- String ruleBits =
- START_BIT
- + ATOMIC_FORMULA_START_BITS
- + APP_CERTIFICATE_LINEAGE
- + EQ
- + IS_HASHED
- + getBits(appCertificate.length(), VALUE_SIZE_BITS)
- + getValueBits(appCertificate)
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
-
- RuleParser binaryParser = new RuleBinaryParser();
- Rule expectedRule =
- new Rule(
- new AtomicFormula.StringAtomicFormula(
- AtomicFormula.APP_CERTIFICATE_LINEAGE,
- IntegrityUtils.getHexDigest(
- appCertificate.getBytes(StandardCharsets.UTF_8)),
- /* isHashedValue= */ true),
- Rule.DENY);
-
- List<Rule> rules = binaryParser.parse(rule.array());
-
- assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
- }
-
- @Test
- public void testBinaryString_validAtomicFormula_integerValue_noIndexing() throws Exception {
- int versionCode = 1;
- String ruleBits =
- START_BIT
- + ATOMIC_FORMULA_START_BITS
- + VERSION_CODE
- + EQ
- + getBits(versionCode, /* numOfBits= */ 64)
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
- Rule expectedRule =
- new Rule(
- new AtomicFormula.LongAtomicFormula(
- AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
- Rule.DENY);
-
- List<Rule> rules = binaryParser.parse(rule.array());
-
- assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
- }
-
- @Test
- public void testBinaryString_validAtomicFormula_booleanValue_noIndexing() throws Exception {
- String isPreInstalled = "1";
- String ruleBits =
- START_BIT
- + ATOMIC_FORMULA_START_BITS
- + PRE_INSTALLED
- + EQ
- + isPreInstalled
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
- Rule expectedRule =
- new Rule(
- new AtomicFormula.BooleanAtomicFormula(
- AtomicFormula.PRE_INSTALLED, true),
- Rule.DENY);
-
- List<Rule> rules = binaryParser.parse(rule.array());
-
- assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
- }
-
- @Test
- public void testBinaryString_invalidAtomicFormula_noIndexing() {
- int versionCode = 1;
- String ruleBits =
- START_BIT
- + ATOMIC_FORMULA_START_BITS
- + VERSION_CODE
- + EQ
- + getBits(versionCode, /* numOfBits= */ 64)
- + DENY;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
-
- assertExpectException(
- RuleParseException.class,
- /* expectedExceptionMessageRegex= */ "A rule must end with a '1' bit.",
- () -> binaryParser.parse(rule.array()));
- }
-
- @Test
- public void testBinaryString_withNoRuleList_noIndexing() throws RuleParseException {
- ByteBuffer rule = ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- RuleParser binaryParser = new RuleBinaryParser();
-
- List<Rule> rules = binaryParser.parse(rule.array());
-
- assertThat(rules).isEmpty();
- }
-
- @Test
- public void testBinaryString_withEmptyRule_noIndexing() {
- String ruleBits = START_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
-
- assertExpectException(
- RuleParseException.class,
- /* expectedExceptionMessageRegex */ "",
- () -> binaryParser.parse(rule.array()));
- }
-
- @Test
- public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas_noIndexing() {
- String packageName = "com.test.app";
- String appCertificate = "test_cert";
- String ruleBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + ATOMIC_FORMULA_START_BITS
- + APP_CERTIFICATE
- + EQ
- + IS_NOT_HASHED
- + getBits(appCertificate.length(), VALUE_SIZE_BITS)
- + getValueBits(appCertificate)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
-
- assertExpectException(
- RuleParseException.class,
- /* expectedExceptionMessageRegex */ "Connector NOT must have 1 formula only",
- () -> binaryParser.parse(rule.array()));
- }
-
- @Test
- public void testBinaryString_invalidRule_invalidOperator_noIndexing() {
- int versionCode = 1;
- String ruleBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + VERSION_CODE
- + INVALID_OPERATOR
- + getBits(versionCode, /* numOfBits= */ 64)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
-
- assertExpectException(
- RuleParseException.class,
- /* expectedExceptionMessageRegex */ String.format(
- "Unknown operator: %d", INVALID_OPERATOR_VALUE),
- () -> binaryParser.parse(rule.array()));
- }
-
- @Test
- public void testBinaryString_invalidRule_invalidEffect_noIndexing() {
- String packageName = "com.test.app";
- String ruleBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + COMPOUND_FORMULA_END_BITS
- + INVALID_EFFECT
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
-
- assertExpectException(
- RuleParseException.class,
- /* expectedExceptionMessageRegex */ String.format(
- "Unknown effect: %d", INVALID_EFFECT_VALUE),
- () -> binaryParser.parse(rule.array()));
- }
-
- @Test
- public void testBinaryString_invalidRule_invalidConnector_noIndexing() {
- String packageName = "com.test.app";
- String ruleBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + INVALID_CONNECTOR
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
-
- assertExpectException(
- RuleParseException.class,
- /* expectedExceptionMessageRegex */ String.format(
- "Unknown connector: %d", INVALID_CONNECTOR_VALUE),
- () -> binaryParser.parse(rule.array()));
- }
-
- @Test
- public void testBinaryString_invalidRule_invalidKey_noIndexing() {
- String packageName = "com.test.app";
- String ruleBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + INVALID_KEY
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
-
- assertExpectException(
- RuleParseException.class,
- /* expectedExceptionMessageRegex */ String.format(
- "Unknown key: %d", INVALID_KEY_VALUE),
- () -> binaryParser.parse(rule.array()));
- }
-
- @Test
- public void testBinaryString_invalidRule_invalidSeparator_noIndexing() {
- String packageName = "com.test.app";
- String ruleBits =
- START_BIT
- + INVALID_FORMULA_SEPARATOR_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + END_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
-
- assertExpectException(
- RuleParseException.class,
- /* expectedExceptionMessageRegex */ String.format(
- "Unknown formula separator: %d", INVALID_FORMULA_SEPARATOR_VALUE),
- () -> binaryParser.parse(rule.array()));
- }
-
- @Test
- public void testBinaryString_invalidRule_invalidEndMarker_noIndexing() {
- String packageName = "com.test.app";
- String ruleBits =
- START_BIT
- + COMPOUND_FORMULA_START_BITS
- + NOT
- + ATOMIC_FORMULA_START_BITS
- + PACKAGE_NAME
- + EQ
- + IS_NOT_HASHED
- + getBits(packageName.length(), VALUE_SIZE_BITS)
- + getValueBits(packageName)
- + COMPOUND_FORMULA_END_BITS
- + DENY
- + INVALID_MARKER_BIT;
- byte[] ruleBytes = getBytes(ruleBits);
- ByteBuffer rule =
- ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
- rule.put(DEFAULT_FORMAT_VERSION_BYTES);
- rule.put(ruleBytes);
- RuleParser binaryParser = new RuleBinaryParser();
-
- assertExpectException(
- RuleParseException.class,
- /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit",
- () -> binaryParser.parse(rule.array()));
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
deleted file mode 100644
index 370bd80..0000000
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
-import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
-import static com.android.server.integrity.utils.TestUtils.getBits;
-import static com.android.server.integrity.utils.TestUtils.getBytes;
-import static com.android.server.integrity.utils.TestUtils.getValueBits;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.integrity.AppInstallMetadata;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(JUnit4.class)
-public class RuleIndexingControllerTest {
-
- @Test
- public void verifyIndexRangeSearchIsCorrect() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ddd")
- .setAppCertificates(Collections.singletonList("777"))
- .setAppCertificateLineage(Collections.singletonList("777"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(200, 300),
- new RuleIndexRange(700, 800),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_multipleAppCertificates() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ddd")
- .setAppCertificates(Arrays.asList("777", "999"))
- .setAppCertificateLineage(Arrays.asList("777", "999"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(200, 300),
- new RuleIndexRange(700, 800),
- new RuleIndexRange(800, 900),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_keysInFirstAndLastBlock() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("bbb")
- .setAppCertificates(Collections.singletonList("999"))
- .setAppCertificateLineage(Collections.singletonList("999"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(100, 200),
- new RuleIndexRange(800, 900),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_keysMatchWithValues() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ccc")
- .setAppCertificates(Collections.singletonList("444"))
- .setAppCertificateLineage(Collections.singletonList("444"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(200, 300),
- new RuleIndexRange(700, 800),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_noIndexesAvailable() throws IOException {
- byte[] stringBytes =
- getBytes(
- getKeyValueString(START_INDEXING_KEY, 100)
- + getKeyValueString(END_INDEXING_KEY, 500)
- + getKeyValueString(START_INDEXING_KEY, 500)
- + getKeyValueString(END_INDEXING_KEY, 900)
- + getKeyValueString(START_INDEXING_KEY, 900)
- + getKeyValueString(END_INDEXING_KEY, 945));
- ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
- rule.put(stringBytes);
- InputStream inputStream = new ByteArrayInputStream(rule.array());
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ccc")
- .setAppCertificates(Collections.singletonList("444"))
- .setAppCertificateLineage(Collections.singletonList("444"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(100, 500),
- new RuleIndexRange(500, 900),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexingFileIsCorrupt() throws IOException {
- byte[] stringBytes =
- getBytes(
- getKeyValueString(START_INDEXING_KEY, 100)
- + getKeyValueString("ccc", 200)
- + getKeyValueString(END_INDEXING_KEY, 300)
- + getKeyValueString(END_INDEXING_KEY, 900));
- ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
- rule.put(stringBytes);
- InputStream inputStream = new ByteArrayInputStream(rule.array());
-
- assertThrows(IllegalStateException.class,
- () -> new RuleIndexingController(inputStream));
- }
-
- private static InputStream obtainDefaultIndexingMapForTest() {
- byte[] stringBytes =
- getBytes(
- getKeyValueString(START_INDEXING_KEY, 100)
- + getKeyValueString("ccc", 200)
- + getKeyValueString("eee", 300)
- + getKeyValueString("hhh", 400)
- + getKeyValueString(END_INDEXING_KEY, 500)
- + getKeyValueString(START_INDEXING_KEY, 500)
- + getKeyValueString("111", 600)
- + getKeyValueString("444", 700)
- + getKeyValueString("888", 800)
- + getKeyValueString(END_INDEXING_KEY, 900)
- + getKeyValueString(START_INDEXING_KEY, 900)
- + getKeyValueString(END_INDEXING_KEY, 945));
- ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
- rule.put(stringBytes);
- return new ByteArrayInputStream(rule.array());
- }
-
- private static String getKeyValueString(String key, int value) {
- String isNotHashed = "0";
- return isNotHashed
- + getBits(key.length(), VALUE_SIZE_BITS)
- + getValueBits(key)
- + getBits(value, /* numOfBits= */ 32);
- }
-}
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 76ff231..d99a6e92 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -19,7 +19,7 @@
],
}
-genrule {
+java_genrule {
name: "wmtests.protologsrc",
srcs: [
":protolog-impl",
diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp
index 38cb9869..e14e5fe 100644
--- a/tests/BinaryTransparencyHostTest/Android.bp
+++ b/tests/BinaryTransparencyHostTest/Android.bp
@@ -32,7 +32,7 @@
static_libs: [
"truth",
],
- data: [
+ device_common_data: [
":BinaryTransparencyTestApp",
":EasterEgg",
":FeatureSplitBase",
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
index 37cb850..a0e0477 100644
--- a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
+++ b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
@@ -36,7 +36,7 @@
unit_test: false,
tags: ["mobly"],
},
- data: [
+ device_common_data: [
":cdm_snippet_legacy",
],
version: {
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.bp b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
index 3f2c808..45bbcb4 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/Android.bp
+++ b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
@@ -55,6 +55,8 @@
java_resources: [
":DynamicCodeLoggerTestLibrary",
+ ],
+ device_first_java_resources: [
":DynamicCodeLoggerNativeExecutable",
],
}
diff --git a/tests/FsVerityTest/Android.bp b/tests/FsVerityTest/Android.bp
index 02268c3..c2dfa0f 100644
--- a/tests/FsVerityTest/Android.bp
+++ b/tests/FsVerityTest/Android.bp
@@ -43,7 +43,7 @@
data_device_bins_both: [
"block_device_writer",
],
- data: [
+ device_common_data: [
":FsVerityTestApp",
],
}
diff --git a/tests/OdmApps/Android.bp b/tests/OdmApps/Android.bp
index a5c6d65..9f32d46 100644
--- a/tests/OdmApps/Android.bp
+++ b/tests/OdmApps/Android.bp
@@ -26,7 +26,7 @@
srcs: ["src/**/*.java"],
libs: ["tradefed"],
test_suites: ["device-tests"],
- data: [
+ device_common_data: [
":TestOdmApp",
":TestOdmPrivApp",
],
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 21007ef..766ff4a 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -26,7 +26,11 @@
manifest: "RollbackTest/AndroidManifest.xml",
platform_apis: true,
srcs: ["RollbackTest/src/**/*.java"],
- static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
+ static_libs: [
+ "androidx.test.rules",
+ "cts-rollback-lib",
+ "cts-install-lib",
+ ],
test_suites: ["general-tests"],
test_config: "RollbackTest.xml",
java_resources: [
@@ -48,7 +52,7 @@
],
test_suites: ["general-tests"],
test_config: "StagedRollbackTest.xml",
- data: [
+ device_common_data: [
":com.android.apex.apkrollback.test_v1",
":test.rebootless_apex_v1",
":RollbackTest",
@@ -59,10 +63,13 @@
name: "NetworkStagedRollbackTest",
srcs: ["NetworkStagedRollbackTest/src/**/*.java"],
libs: ["tradefed"],
- static_libs: ["RollbackTestLib", "frameworks-base-hostutils"],
+ static_libs: [
+ "RollbackTestLib",
+ "frameworks-base-hostutils",
+ ],
test_suites: ["general-tests"],
test_config: "NetworkStagedRollbackTest.xml",
- data: [":RollbackTest"],
+ device_common_data: [":RollbackTest"],
}
java_test_host {
@@ -74,7 +81,7 @@
],
test_suites: ["general-tests"],
test_config: "MultiUserRollbackTest.xml",
- data : [":RollbackTest"],
+ device_common_data: [":RollbackTest"],
}
java_library_host {
@@ -84,55 +91,55 @@
}
genrule {
- name: "com.android.apex.apkrollback.test.pem",
- out: ["com.android.apex.apkrollback.test.pem"],
- cmd: "openssl genrsa -out $(out) 4096",
+ name: "com.android.apex.apkrollback.test.pem",
+ out: ["com.android.apex.apkrollback.test.pem"],
+ cmd: "openssl genrsa -out $(out) 4096",
}
genrule {
- name: "com.android.apex.apkrollback.test.pubkey",
- srcs: [":com.android.apex.apkrollback.test.pem"],
- out: ["com.android.apex.apkrollback.test.pubkey"],
- tools: ["avbtool"],
- cmd: "$(location avbtool) extract_public_key --key $(in) --output $(out)",
+ name: "com.android.apex.apkrollback.test.pubkey",
+ srcs: [":com.android.apex.apkrollback.test.pem"],
+ out: ["com.android.apex.apkrollback.test.pubkey"],
+ tools: ["avbtool"],
+ cmd: "$(location avbtool) extract_public_key --key $(in) --output $(out)",
}
apex_key {
- name: "com.android.apex.apkrollback.test.key",
- private_key: ":com.android.apex.apkrollback.test.pem",
- public_key: ":com.android.apex.apkrollback.test.pubkey",
- installable: false,
+ name: "com.android.apex.apkrollback.test.key",
+ private_key: ":com.android.apex.apkrollback.test.pem",
+ public_key: ":com.android.apex.apkrollback.test.pubkey",
+ installable: false,
}
apex {
- name: "com.android.apex.apkrollback.test_v1",
- manifest: "testdata/manifest_v1.json",
- androidManifest: "testdata/AndroidManifest.xml",
- file_contexts: ":apex.test-file_contexts",
- key: "com.android.apex.apkrollback.test.key",
- apps: ["TestAppAv1"],
- installable: false,
- updatable: false,
+ name: "com.android.apex.apkrollback.test_v1",
+ manifest: "testdata/manifest_v1.json",
+ androidManifest: "testdata/AndroidManifest.xml",
+ file_contexts: ":apex.test-file_contexts",
+ key: "com.android.apex.apkrollback.test.key",
+ apps: ["TestAppAv1"],
+ installable: false,
+ updatable: false,
}
apex {
- name: "com.android.apex.apkrollback.test_v2",
- manifest: "testdata/manifest_v2.json",
- androidManifest: "testdata/AndroidManifest.xml",
- file_contexts: ":apex.test-file_contexts",
- key: "com.android.apex.apkrollback.test.key",
- apps: ["TestAppAv2"],
- installable: false,
- updatable: false,
+ name: "com.android.apex.apkrollback.test_v2",
+ manifest: "testdata/manifest_v2.json",
+ androidManifest: "testdata/AndroidManifest.xml",
+ file_contexts: ":apex.test-file_contexts",
+ key: "com.android.apex.apkrollback.test.key",
+ apps: ["TestAppAv2"],
+ installable: false,
+ updatable: false,
}
apex {
- name: "com.android.apex.apkrollback.test_v2Crashing",
- manifest: "testdata/manifest_v2.json",
- androidManifest: "testdata/AndroidManifest.xml",
- file_contexts: ":apex.test-file_contexts",
- key: "com.android.apex.apkrollback.test.key",
- apps: ["TestAppACrashingV2"],
- installable: false,
- updatable: false,
+ name: "com.android.apex.apkrollback.test_v2Crashing",
+ manifest: "testdata/manifest_v2.json",
+ androidManifest: "testdata/AndroidManifest.xml",
+ file_contexts: ":apex.test-file_contexts",
+ key: "com.android.apex.apkrollback.test.key",
+ apps: ["TestAppACrashingV2"],
+ installable: false,
+ updatable: false,
}
diff --git a/tests/SharedLibraryLoadingTest/Android.bp b/tests/SharedLibraryLoadingTest/Android.bp
index 088278d..8027519 100644
--- a/tests/SharedLibraryLoadingTest/Android.bp
+++ b/tests/SharedLibraryLoadingTest/Android.bp
@@ -28,7 +28,7 @@
"junit",
],
test_suites: ["general-tests"],
- data: [
+ device_common_data: [
":SharedLibraryLoadingTests_StandardSharedLibrary",
":SharedLibraryLoadingTests_SharedLibraryLoadedAfter",
":SharedLibraryLoadingTests_SharedLibraryClientTests",
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index 2751141..451870e 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -55,7 +55,7 @@
"frameworks-base-hostutils",
"cts-install-lib-host",
],
- data: [
+ device_common_data: [
":StagedInstallInternalTestApp",
":apex.apexd_test",
":com.android.apex.apkrollback.test_v1",
diff --git a/tests/SystemMemoryTest/host/Android.bp b/tests/SystemMemoryTest/host/Android.bp
index cc8bc45..1535697 100644
--- a/tests/SystemMemoryTest/host/Android.bp
+++ b/tests/SystemMemoryTest/host/Android.bp
@@ -26,7 +26,7 @@
srcs: ["src/**/*.java"],
libs: ["tradefed"],
test_suites: ["general-tests"],
- data: [
+ device_common_data: [
":SystemMemoryTestDevice",
],
}
diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS
index 2441e77..937699a 100644
--- a/tests/vcn/OWNERS
+++ b/tests/vcn/OWNERS
@@ -1,7 +1,6 @@
set noparent
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected] #{LAST_RESORT_SUGGESTION}
[email protected] #{LAST_RESORT_SUGGESTION}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 7e0bbc4..3828a71 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -70,6 +70,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.Uri;
+import android.net.vcn.Flags;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
@@ -84,6 +85,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.test.TestLooper;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
@@ -102,6 +104,7 @@
import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -119,6 +122,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnManagementServiceTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
private static final String TEST_PACKAGE_NAME =
VcnManagementServiceTest.class.getPackage().getName();
@@ -288,6 +293,8 @@
doReturn(Collections.singleton(TRANSPORT_WIFI))
.when(mMockDeps)
.getRestrictedTransports(any(), any(), any());
+
+ mSetFlagsRule.enableFlags(Flags.FLAG_FIX_CONFIG_GARBAGE_COLLECTION);
}
@@ -438,6 +445,14 @@
return subIds;
}).when(snapshot).getAllSubIdsInGroup(any());
+ doAnswer(invocation -> {
+ final Set<ParcelUuid> subGroups = new ArraySet<>();
+ for (Entry<Integer, ParcelUuid> entry : subIdToGroupMap.entrySet()) {
+ subGroups.add(entry.getValue());
+ }
+ return subGroups;
+ }).when(snapshot).getAllSubscriptionGroups();
+
return snapshot;
}
@@ -1483,6 +1498,28 @@
}
@Test
+ public void testGarbageCollectionKeepConfigUntilNewSnapshot() throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+ startAndGetVcnInstance(TEST_UUID_2);
+
+ // Report loss of subscription from mSubMgr
+ doReturn(Collections.emptyList()).when(mSubMgr).getSubscriptionsInGroup(any());
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ TEST_UUID_2,
+ Collections.singleton(TEST_UUID_2),
+ Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_2));
+
+ assertTrue(mVcnMgmtSvc.getConfigs().containsKey(TEST_UUID_2));
+
+ // Report loss of subscription from snapshot
+ triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
+
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ assertFalse(mVcnMgmtSvc.getConfigs().containsKey(TEST_UUID_2));
+ }
+
+ @Test
public void testVcnCarrierConfigChangeUpdatesPolicyListener() throws Exception {
setupActiveSubscription(TEST_UUID_2);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index e045f10..4c7b25a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -223,7 +223,6 @@
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags();
doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled();
- doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled();
doReturn(mUnderlyingNetworkController)
.when(mDeps)
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
index bc7ff47..441b780 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java
@@ -20,7 +20,6 @@
import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName;
import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -127,8 +126,6 @@
false /* isInTestMode */));
doNothing().when(mVcnContext).ensureRunningOnLooperThread();
- doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled();
-
setupSystemService(
mContext,
mConnectivityManager,
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index 6f31d8d..e540932 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -226,7 +226,6 @@
private void resetVcnContext(VcnContext vcnContext) {
reset(vcnContext);
doNothing().when(vcnContext).ensureRunningOnLooperThread();
- doReturn(true).when(vcnContext).isFlagIpSecTransformStateEnabled();
}
// Package private for use in NetworkPriorityClassifierTest
diff --git a/tools/lint/global/integration_tests/Android.bp b/tools/lint/global/integration_tests/Android.bp
index 40281d2..05ba405 100644
--- a/tools/lint/global/integration_tests/Android.bp
+++ b/tools/lint/global/integration_tests/Android.bp
@@ -38,7 +38,7 @@
python_library_host {
name: "AndroidGlobalLintTestNoAidl_py",
- data: [":AndroidGlobalLintTestNoAidl{.lint}"],
+ device_common_data: [":AndroidGlobalLintTestNoAidl{.lint}"],
pkg_path: "no_aidl",
}
@@ -53,7 +53,7 @@
python_library_host {
name: "AndroidGlobalLintTestMissingAnnotation_py",
- data: [":AndroidGlobalLintTestMissingAnnotation{.lint}"],
+ device_common_data: [":AndroidGlobalLintTestMissingAnnotation{.lint}"],
pkg_path: "missing_annotation",
}
diff --git a/tools/preload-check/Android.bp b/tools/preload-check/Android.bp
index 73caac6..24ec12c 100644
--- a/tools/preload-check/Android.bp
+++ b/tools/preload-check/Android.bp
@@ -28,5 +28,5 @@
libs: ["tradefed"],
test_suites: ["general-tests"],
required: ["preload-check-device"],
- data: [":preload-check-device"],
+ device_common_data: [":preload-check-device"],
}
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
index 590f719..e6d0a3d 100644
--- a/tools/systemfeatures/Android.bp
+++ b/tools/systemfeatures/Android.bp
@@ -58,6 +58,7 @@
"junit",
"objenesis",
"mockito",
+ "systemfeatures-gen-lib",
"truth",
],
}
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index 196b5e7..1abe77f 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -71,7 +71,7 @@
println("Usage: SystemFeaturesGenerator <outputClassName> [options]")
println(" Options:")
println(" --readonly=true|false Whether to encode features as build-time constants")
- println(" --feature=\$NAME:\$VER A feature+version pair, where \$VER can be:")
+ println(" --feature=\$NAME:\$VER A feature+version pair, where \$VER can be:")
println(" * blank/empty == undefined (variable API)")
println(" * valid int == enabled (constant API)")
println(" * UNAVAILABLE == disabled (constant API)")
@@ -89,6 +89,17 @@
/** Main entrypoint for build-time system feature codegen. */
@JvmStatic
fun main(args: Array<String>) {
+ generate(args, System.out)
+ }
+
+ /**
+ * Simple API entrypoint for build-time system feature codegen.
+ *
+ * Note: Typically this would be implemented in terms of a proper Builder-type input argument,
+ * but it's primarily used for testing as opposed to direct production usage.
+ */
+ @JvmStatic
+ fun generate(args: Array<String>, output: Appendable) {
if (args.size < 1) {
usage()
return
@@ -155,7 +166,7 @@
.addFileComment("This file is auto-generated. DO NOT MODIFY.\n")
.addFileComment("Args: ${args.joinToString(" \\\n ")}")
.build()
- .writeTo(System.out)
+ .writeTo(output)
}
/*
@@ -171,12 +182,27 @@
return when (featureArgs.getOrNull(1)) {
null, "" -> FeatureInfo(name, null, readonly = false)
"UNAVAILABLE" -> FeatureInfo(name, null, readonly = true)
- else -> FeatureInfo(name, featureArgs[1].toIntOrNull(), readonly = true)
+ else -> {
+ val featureVersion =
+ featureArgs[1].toIntOrNull()
+ ?: throw IllegalArgumentException(
+ "Invalid feature version input for $name: ${featureArgs[1]}"
+ )
+ FeatureInfo(name, featureArgs[1].toInt(), readonly = true)
+ }
}
}
private fun parseFeatureName(name: String): String =
- if (name.startsWith("FEATURE_")) name else "FEATURE_$name"
+ when {
+ name.startsWith("android") ->
+ throw IllegalArgumentException(
+ "Invalid feature name input: \"android\"-namespaced features must be " +
+ "provided as PackageManager.FEATURE_* suffixes, not raw feature strings."
+ )
+ name.startsWith("FEATURE_") -> name
+ else -> "FEATURE_$name"
+ }
/*
* Adds per-feature query methods to the class with the form:
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java
new file mode 100644
index 0000000..f8c585d
--- /dev/null
+++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemfeatures;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.io.IOException;
+
+// Note: This is a very simple argument test to validate certain behaviors for
+// invalid arguments. Correctness and validity is largely exercised by
+// SystemFeaturesGeneratorTest.
+@RunWith(JUnit4.class)
+public class SystemFeaturesGeneratorApiTest {
+
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Mock private Appendable mOut;
+
+ @Test
+ public void testEmpty() throws IOException {
+ final String[] args = new String[] {};
+ // This should just print the commandline and return.
+ SystemFeaturesGenerator.generate(args, mOut);
+ verify(mOut, never()).append(any());
+ }
+
+ @Test
+ public void testBasic() throws IOException {
+ final String[] args = new String[] {
+ "com.foo.Features",
+ "--feature=TELEVISION:0",
+ };
+ SystemFeaturesGenerator.generate(args, mOut);
+ verify(mOut, atLeastOnce()).append(any());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidFeatureVersion() throws IOException {
+ final String[] args = new String[] {
+ "com.foo.Features",
+ "--feature=TELEVISION:blarg",
+ };
+ SystemFeaturesGenerator.generate(args, mOut);
+ verify(mOut, never()).append(any());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidFeatureNameFromAndroidNamespace() throws IOException {
+ final String[] args = new String[] {
+ "com.foo.Features",
+ "--feature=android.hardware.doesntexist:0",
+ };
+ SystemFeaturesGenerator.generate(args, mOut);
+ verify(mOut, never()).append(any());
+ }
+}