Merge "Revert "Adapt to libprocessgroup API changes for polling on cgro..."" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 451175f..489c271 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -15,6 +15,7 @@
aconfig_srcjars = [
":android.content.pm.flags-aconfig-java{.generated_srcjars}",
":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
+ ":android.net.vcn.flags-aconfig-java{.generated_srcjars}",
":android.nfc.flags-aconfig-java{.generated_srcjars}",
":android.os.flags-aconfig-java{.generated_srcjars}",
":android.security.flags-aconfig-java{.generated_srcjars}",
@@ -120,6 +121,18 @@
srcs: ["core/java/android/nfc/*.aconfig"],
}
+cc_aconfig_library {
+ name: "android_nfc_flags_aconfig_c_lib",
+ vendor_available: true,
+ aconfig_declarations: "android.nfc.flags-aconfig",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.nfcservices",
+ "nfc_nci.st21nfc.default",
+ ],
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
java_aconfig_library {
name: "android.nfc.flags-aconfig-java",
aconfig_declarations: "android.nfc.flags-aconfig",
@@ -148,7 +161,6 @@
name: "android.security.flags-aconfig-java-host",
aconfig_declarations: "android.security.flags-aconfig",
host_supported: true,
- test: true,
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -197,3 +209,34 @@
aconfig_declarations: "com.android.net.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Media
+aconfig_declarations {
+ name: "android.media.playback.flags-aconfig",
+ package: "com.android.media.playback.flags",
+ srcs: ["media/jni/playback_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "android.media.playback.flags-aconfig-cc",
+ aconfig_declarations: "android.media.playback.flags-aconfig",
+}
+
+java_aconfig_library {
+ name: "android.media.playback.flags-aconfig-java",
+ aconfig_declarations: "android.media.playback.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// VCN
+aconfig_declarations {
+ name: "android.net.vcn.flags-aconfig",
+ package: "android.net.vcn",
+ srcs: ["core/java/android/net/vcn/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.net.vcn.flags-aconfig-java",
+ aconfig_declarations: "android.net.vcn.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 0c08119..f5c0b6d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -136,6 +136,9 @@
// For the generated R.java and Manifest.java
":framework-res{.aapt.srcjar}",
+ // Java/AIDL sources to be moved out to CrashRecovery module
+ ":framework-crashrecovery-sources",
+
// etc.
":framework-javastream-protos",
":statslog-framework-java-gen", // FrameworkStatsLog.java
@@ -396,6 +399,7 @@
"soundtrigger_middleware-aidl-java",
"modules-utils-binary-xml",
"modules-utils-build",
+ "modules-utils-fastxmlserializer",
"modules-utils-preconditions",
"modules-utils-statemachine",
"modules-utils-synchronous-result-receiver",
@@ -409,13 +413,25 @@
],
}
+// Collection of non updatable unbundled jars. The list here should match
+// |non_updatable_modules| variable in frameworks/base/api/api.go.
+java_library {
+ name: "framework-non-updatable-unbundled-impl-libs",
+ static_libs: [
+ "framework-location.impl",
+ "framework-nfc.impl",
+ ],
+ sdk_version: "core_platform",
+ installable: false,
+}
+
// Separated so framework-minus-apex-defaults can be used without the libs dependency
java_defaults {
name: "framework-minus-apex-with-libs-defaults",
defaults: ["framework-minus-apex-defaults"],
libs: [
"framework-virtualization.stubs.module_lib",
- "framework-location.impl",
+ "framework-non-updatable-unbundled-impl-libs",
],
}
@@ -446,7 +462,7 @@
stem: "framework",
apex_available: ["//apex_available:platform"],
visibility: [
- "//frameworks/base/location",
+ "//frameworks/base:__subpackages__",
],
compile_dex: false,
headers_only: true,
@@ -509,8 +525,8 @@
installable: false, // this lib is a build-only library
static_libs: [
"app-compat-annotations",
- "framework-location.impl",
"framework-minus-apex",
+ "framework-non-updatable-unbundled-impl-libs",
"framework-updatable-stubs-module_libs_api",
],
sdk_version: "core_platform",
@@ -603,23 +619,11 @@
],
}
-packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
filegroup {
name: "android-non-updatable-stub-sources",
srcs: [
":framework-mime-sources", // mimemap builds separately but has no separate droidstubs.
+ ":framework-minus-apex-aconfig-srcjars",
":framework-non-updatable-sources",
":opt-telephony-srcs",
":opt-net-voip-srcs",
@@ -629,200 +633,6 @@
visibility: ["//frameworks/base/api"],
}
-// Defaults for all stubs that include the non-updatable framework. These defaults do not include
-// module symbols, so will not compile correctly on their own. Users must add module APIs to the
-// classpath (or sources) somehow.
-stubs_defaults {
- name: "android-non-updatable-stubs-defaults",
- srcs: [":android-non-updatable-stub-sources"],
- sdk_version: "none",
- system_modules: "none",
- java_version: "1.8",
- arg_files: [":frameworks-base-core-AndroidManifest.xml"],
- aidl: {
- include_dirs: [
- "frameworks/av/aidl",
- "frameworks/base/media/aidl",
- "frameworks/base/telephony/java",
- "frameworks/native/libs/permission/aidl",
- "packages/modules/Bluetooth/framework/aidl-export",
- "packages/modules/Connectivity/framework/aidl-export",
- "packages/modules/Media/apex/aidl/stable",
- "hardware/interfaces/biometrics/common/aidl",
- "hardware/interfaces/biometrics/fingerprint/aidl",
- "hardware/interfaces/graphics/common/aidl",
- "hardware/interfaces/keymaster/aidl",
- "system/hardware/interfaces/media/aidl",
- ],
- },
- // These are libs from framework-internal-utils that are required (i.e. being referenced)
- // from framework-non-updatable-sources. Add more here when there's a need.
- // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
- // dependencies gets bigger.
- libs: [
- "android.hardware.cas-V1.2-java",
- "android.hardware.health-V1.0-java-constants",
- "android.hardware.radio-V1.5-java",
- "android.hardware.radio-V1.6-java",
- "android.hardware.thermal-V1.0-java-constants",
- "android.hardware.thermal-V2.0-java",
- "android.hardware.tv.input-V1.0-java-constants",
- "android.hardware.usb-V1.0-java-constants",
- "android.hardware.usb-V1.1-java-constants",
- "android.hardware.usb.gadget-V1.0-java",
- "android.hardware.vibrator-V1.3-java",
- "framework-protos",
- ],
- flags: [
- "--api-lint-ignore-prefix android.icu.",
- "--api-lint-ignore-prefix java.",
- "--api-lint-ignore-prefix junit.",
- "--api-lint-ignore-prefix org.",
- "--error NoSettingsProvider",
- "--error UnhiddenSystemApi",
- "--error UnflaggedApi",
- "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*",
- "--hide BroadcastBehavior",
- "--hide CallbackInterface",
- "--hide DeprecationMismatch",
- "--hide HiddenSuperclass",
- "--hide HiddenTypeParameter",
- "--hide MissingPermission",
- "--hide RequiresPermission",
- "--hide SdkConstant",
- "--hide Todo",
- "--hide UnavailableSymbol",
- "--hide-package android.audio.policy.configuration.V7_0",
- "--hide-package com.android.server",
- "--manifest $(location :frameworks-base-core-AndroidManifest.xml)",
- ],
- filter_packages: packages_to_document,
- high_mem: true, // Lots of sources => high memory use, see b/170701554
- installable: false,
- annotations_enabled: true,
- previous_api: ":android.api.public.latest",
- merge_annotations_dirs: ["metalava-manual"],
- defaults_visibility: ["//frameworks/base/api"],
- visibility: ["//frameworks/base/api"],
-}
-
-// Defaults with module APIs in the classpath (mostly from prebuilts).
-// Suitable for compiling android-non-updatable.
-stubs_defaults {
- name: "module-classpath-stubs-defaults",
- aidl: {
- include_dirs: [
- "packages/modules/Bluetooth/framework/aidl-export",
- "packages/modules/Connectivity/framework/aidl-export",
- "packages/modules/Media/apex/aidl/stable",
- ],
- },
- libs: [
- "art.module.public.api",
- "sdk_module-lib_current_framework-tethering",
- "sdk_module-lib_current_framework-connectivity-t",
- "sdk_public_current_framework-bluetooth",
- // There are a few classes from modules used by the core that
- // need to be resolved by metalava. We use a prebuilt stub of the
- // full sdk to ensure we can resolve them. If a new class gets added,
- // the prebuilts/sdk/current needs to be updated.
- "sdk_system_current_android",
- // NOTE: The below can be removed once the prebuilt stub contains IKE.
- "sdk_system_current_android.net.ipsec.ike",
- ],
-}
-
-// Defaults for the java_sdk_libraries of unbundled jars from framework.
-// java_sdk_libraries using these defaults should also add themselves to the
-// non_updatable_modules list in frameworks/base/api/api.go
-java_defaults {
- name: "framework-non-updatable-unbundled-defaults",
- defaults: ["framework-non-updatable-lint-defaults"],
-
- sdk_version: "core_platform",
-
- // Api scope settings
- public: {
- enabled: true,
- sdk_version: "module_current",
- libs: ["android_module_lib_stubs_current"],
- },
- system: {
- enabled: true,
- sdk_version: "module_current",
- libs: ["android_module_lib_stubs_current"],
- },
- module_lib: {
- enabled: true,
- sdk_version: "module_current",
- libs: ["android_module_lib_stubs_current"],
- },
- test: {
- enabled: true,
- sdk_version: "test_frameworks_core_current",
- libs: ["android_test_frameworks_core_stubs_current"],
- },
-
- stub_only_libs: [
- "framework-protos",
- ],
- impl_only_libs: [
- "framework-minus-apex-headers", // full access to framework-minus-apex including hidden API
- "framework-annotations-lib",
- ],
- visibility: ["//visibility:public"],
- stubs_library_visibility: ["//visibility:public"],
- stubs_source_visibility: ["//visibility:private"],
- impl_library_visibility: [
- ":__pkg__",
- "//frameworks/base",
- "//frameworks/base/api", // For framework-all
- ],
- defaults_visibility: [
- "//frameworks/base/location",
- ],
- plugins: [
- "error_prone_android_framework",
- ],
- errorprone: {
- javacflags: [
- "-Xep:AndroidFrameworkCompatChange:ERROR",
- "-Xep:AndroidFrameworkUid:ERROR",
- ],
- },
-
- // Include manual annotations in API txt files
- merge_annotations_dirs: ["metalava-manual"],
-
- // Use the source of annotations that affect metalava doc generation, since
- // the relevant generation instructions are themselves in javadoc, which is
- // not present in class files.
- api_srcs: [":framework-metalava-annotations"],
-
- // Framework modules are not generally shared libraries, i.e. they are not
- // intended, and must not be allowed, to be used in a <uses-library> manifest
- // entry.
- shared_library: false,
-
- // Prevent dependencies that do not specify an sdk_version from accessing the
- // implementation library by default and force them to use stubs instead.
- default_to_stubs: true,
-
- // Subdirectory for the artifacts that are copied to the dist directory
- dist_group: "android",
-
- droiddoc_options: [
- "--error UnhiddenSystemApi " +
- "--hide CallbackInterface " +
- "--hide HiddenTypedefConstant " +
- "--hide RequiresPermission " +
- "--enhance-documentation " +
- "--hide-package com.android.server ",
- ],
-
- annotations_enabled: true,
-}
-
build = [
"AconfigFlags.bp",
"ProtoLibraries.bp",
diff --git a/BAL_OWNERS b/BAL_OWNERS
new file mode 100644
index 0000000..d56a1d4
--- /dev/null
+++ b/BAL_OWNERS
@@ -0,0 +1,5 @@
+brufino@google.com
+achim@google.com
+topjohnwu@google.com
+lus@google.com
+
diff --git a/INPUT_OWNERS b/INPUT_OWNERS
index e02ba77..44b2f38 100644
--- a/INPUT_OWNERS
+++ b/INPUT_OWNERS
@@ -5,3 +5,5 @@
prabirmsp@google.com
svv@google.com
vdevmurari@google.com
+
+per-file Virtual*=file:/services/companion/java/com/android/server/companion/virtual/OWNERS
diff --git a/OWNERS b/OWNERS
index 023bdef..733157f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -37,3 +37,5 @@
per-file *ravenwood* = file:ravenwood/OWNERS
per-file *Ravenwood* = file:ravenwood/OWNERS
+
+per-file PERFORMANCE_OWNERS = file:/PERFORMANCE_OWNERS
diff --git a/PERFORMANCE_OWNERS b/PERFORMANCE_OWNERS
index 9452ea3..48a0201 100644
--- a/PERFORMANCE_OWNERS
+++ b/PERFORMANCE_OWNERS
@@ -3,3 +3,6 @@
dualli@google.com
carmenjackson@google.com
philipcuadra@google.com
+shayba@google.com
+jdduke@google.com
+shombert@google.com
diff --git a/THERMAL_OWNERS b/THERMAL_OWNERS
new file mode 100644
index 0000000..b95b7e8
--- /dev/null
+++ b/THERMAL_OWNERS
@@ -0,0 +1,3 @@
+lpy@google.com
+wvw@google.com
+xwxw@google.com
diff --git a/apct-tests/perftests/OWNERS b/apct-tests/perftests/OWNERS
index 4c57e64..8ff3f9b 100644
--- a/apct-tests/perftests/OWNERS
+++ b/apct-tests/perftests/OWNERS
@@ -1,12 +1,11 @@
-balejs@google.com
carmenjackson@google.com
-cfijalkovich@google.com
dualli@google.com
edgararriaga@google.com
-jpakaravoor@google.com
+jdduke@google.com
jreck@google.com #{LAST_RESORT_SUGGESTION}
kevinjeon@google.com
philipcuadra@google.com
+shayba@google.com
shombert@google.com
timmurray@google.com
wessam@google.com
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index 9366ff2d..e1b3241 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -66,6 +66,7 @@
errorprone: {
javacflags: [
"-Xep:ReturnValueIgnored:WARN",
+ "-Xep:UnnecessaryStringBuilder:OFF",
],
},
}
diff --git a/tests/SilkFX/assets/gainmaps/fountain_night.jpg b/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg
similarity index 100%
copy from tests/SilkFX/assets/gainmaps/fountain_night.jpg
copy to apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg
Binary files differ
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
index f84a0d0..e5a06c9 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
@@ -16,20 +16,29 @@
package android.graphics.perftests;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Color;
+import android.graphics.ImageDecoder;
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
+import com.android.perftests.core.R;
+
import org.junit.Rule;
import org.junit.Test;
+import java.io.IOException;
+
@LargeTest
public class CanvasPerfTest {
@Rule
@@ -93,4 +102,38 @@
node.end(canvas);
}
}
+
+ @Test
+ public void testCreateScaledBitmap() throws IOException {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ Bitmap source = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
+ (decoder, info, source1) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ source.setGainmap(null);
+
+ while (state.keepRunning()) {
+ Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
+ .recycle();
+ }
+ }
+
+ @Test
+ public void testCreateScaledBitmapWithGainmap() throws IOException {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ Bitmap source = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
+ (decoder, info, source1) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ });
+ assertTrue(source.hasGainmap());
+
+ while (state.keepRunning()) {
+ Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
+ .recycle();
+ }
+ }
}
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index a4a0b4b..83ad736 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -26,6 +26,10 @@
"unsupportedappusage",
],
+ static_libs: [
+ "modules-utils-fastxmlserializer",
+ ],
+
// Rename classes shared with the framework
jarjar_rules: "jarjar-rules.txt",
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
index 4d646de..1076465 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java
@@ -21,7 +21,7 @@
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.RTC_WAKEUP;
-import static com.android.server.alarm.AlarmManagerService.clampPositive;
+import static com.android.server.alarm.AlarmManagerService.addClampPositive;
import android.app.AlarmManager;
import android.app.IAlarmListener;
@@ -146,7 +146,7 @@
mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] = requestedWhenElapsed;
mWhenElapsed = requestedWhenElapsed;
this.windowLength = windowLength;
- mMaxWhenElapsed = clampPositive(requestedWhenElapsed + windowLength);
+ mMaxWhenElapsed = addClampPositive(requestedWhenElapsed, windowLength);
repeatInterval = interval;
operation = op;
listener = rec;
@@ -241,8 +241,8 @@
final long oldMaxWhenElapsed = mMaxWhenElapsed;
// windowLength should always be >= 0 here.
- final long maxRequestedElapsed = clampPositive(
- mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] + windowLength);
+ final long maxRequestedElapsed = addClampPositive(
+ mPolicyWhenElapsed[REQUESTER_POLICY_INDEX], windowLength);
mMaxWhenElapsed = Math.max(maxRequestedElapsed, mWhenElapsed);
return (oldWhenElapsed != mWhenElapsed) || (oldMaxWhenElapsed != mMaxWhenElapsed);
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index b96354c..8cc3134 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -1498,15 +1498,15 @@
if (futurity < MIN_FUZZABLE_INTERVAL) {
futurity = 0;
}
- long maxElapsed = triggerAtTime + (long) (0.75 * futurity);
+ long maxElapsed = addClampPositive(triggerAtTime, (long) (0.75 * futurity));
// For non-repeating alarms, window is capped at a maximum of one hour from the requested
// delivery time. This allows for inexact-while-idle alarms to be slightly more reliable.
// In practice, the delivery window should generally be much smaller than that
// when the device is not idling.
if (interval == 0) {
- maxElapsed = Math.min(maxElapsed, triggerAtTime + INTERVAL_HOUR);
+ maxElapsed = Math.min(maxElapsed, addClampPositive(triggerAtTime, INTERVAL_HOUR));
}
- return clampPositive(maxElapsed);
+ return maxElapsed;
}
// The RTC clock has moved arbitrarily, so we need to recalculate all the RTC alarm deliveries.
@@ -1593,6 +1593,18 @@
return (val >= 0) ? val : Long.MAX_VALUE;
}
+ static long addClampPositive(long val1, long val2) {
+ long val = val1 + val2;
+ if (val >= 0) {
+ return val;
+ } else if (val1 >= 0 && val2 >= 0) {
+ /* Both are +ve, so overflow happened. */
+ return Long.MAX_VALUE;
+ } else {
+ return 0;
+ }
+ }
+
/**
* Sends alarms that were blocked due to user applied background restrictions - either because
* the user lifted those or the uid came to foreground.
diff --git a/api/Android.bp b/api/Android.bp
index 6986ac0..68babc3 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -83,14 +83,15 @@
"framework-configinfrastructure",
"framework-connectivity",
"framework-connectivity-t",
- "framework-crashrecovery",
"framework-devicelock",
"framework-graphics",
"framework-healthfitness",
"framework-location",
"framework-media",
"framework-mediaprovider",
+ "framework-nfc",
"framework-ondevicepersonalization",
+ "framework-pdf",
"framework-permission",
"framework-permission-s",
"framework-scheduling",
@@ -106,7 +107,6 @@
system_server_classpath: [
"service-art",
"service-configinfrastructure",
- "service-crashrecovery",
"service-healthfitness",
"service-media-s",
"service-permission",
@@ -262,7 +262,205 @@
cmd: "cat $(in) | md5sum | cut -d' ' -f1 > $(out)",
}
+packages_to_document = [
+ "android",
+ "dalvik",
+ "java",
+ "javax",
+ "junit",
+ "org.apache.http",
+ "org.json",
+ "org.w3c.dom",
+ "org.xml.sax",
+ "org.xmlpull",
+]
+
+// Defaults for all stubs that include the non-updatable framework. These defaults do not include
+// module symbols, so will not compile correctly on their own. Users must add module APIs to the
+// classpath (or sources) somehow.
+stubs_defaults {
+ name: "android-non-updatable-stubs-defaults",
+ srcs: [":android-non-updatable-stub-sources"],
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
+ arg_files: [":frameworks-base-core-AndroidManifest.xml"],
+ aidl: {
+ include_dirs: [
+ "frameworks/av/aidl",
+ "frameworks/base/media/aidl",
+ "frameworks/base/telephony/java",
+ "frameworks/native/libs/permission/aidl",
+ "packages/modules/Bluetooth/framework/aidl-export",
+ "packages/modules/Connectivity/framework/aidl-export",
+ "packages/modules/Media/apex/aidl/stable",
+ "hardware/interfaces/biometrics/common/aidl",
+ "hardware/interfaces/biometrics/fingerprint/aidl",
+ "hardware/interfaces/graphics/common/aidl",
+ "hardware/interfaces/keymaster/aidl",
+ "system/hardware/interfaces/media/aidl",
+ ],
+ },
+ // These are libs from framework-internal-utils that are required (i.e. being referenced)
+ // from framework-non-updatable-sources. Add more here when there's a need.
+ // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
+ // dependencies gets bigger.
+ libs: [
+ "android.hardware.cas-V1.2-java",
+ "android.hardware.health-V1.0-java-constants",
+ "android.hardware.radio-V1.5-java",
+ "android.hardware.radio-V1.6-java",
+ "android.hardware.thermal-V1.0-java-constants",
+ "android.hardware.thermal-V2.0-java",
+ "android.hardware.tv.input-V1.0-java-constants",
+ "android.hardware.usb-V1.0-java-constants",
+ "android.hardware.usb-V1.1-java-constants",
+ "android.hardware.usb.gadget-V1.0-java",
+ "android.hardware.vibrator-V1.3-java",
+ "framework-protos",
+ ],
+ flags: [
+ "--api-lint-ignore-prefix android.icu.",
+ "--api-lint-ignore-prefix java.",
+ "--api-lint-ignore-prefix junit.",
+ "--api-lint-ignore-prefix org.",
+ "--error NoSettingsProvider",
+ "--error UnhiddenSystemApi",
+ "--error UnflaggedApi",
+ "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*",
+ "--hide BroadcastBehavior",
+ "--hide CallbackInterface",
+ "--hide DeprecationMismatch",
+ "--hide HiddenSuperclass",
+ "--hide MissingPermission",
+ "--hide RequiresPermission",
+ "--hide SdkConstant",
+ "--hide Todo",
+ "--hide-package android.audio.policy.configuration.V7_0",
+ "--hide-package com.android.server",
+ "--manifest $(location :frameworks-base-core-AndroidManifest.xml)",
+ ],
+ filter_packages: packages_to_document,
+ high_mem: true, // Lots of sources => high memory use, see b/170701554
+ installable: false,
+ annotations_enabled: true,
+ previous_api: ":android.api.public.latest",
+ merge_annotations_dirs: ["metalava-manual"],
+ defaults_visibility: ["//frameworks/base/api"],
+ visibility: ["//frameworks/base/api"],
+}
+
+// We resolve dependencies on APIs in modules by depending on a prebuilt of the whole
+// platform (sdk_system_current_android). That prebuilt does not include module-lib APIs,
+// so use the prebuilt module-lib stubs for modules that export module-lib stubs that the
+// non-updatable part depends on.
+non_updatable_api_deps_on_modules = [
+ "sdk_module-lib_current_framework-tethering",
+ "sdk_module-lib_current_framework-connectivity-t",
+ "sdk_system_current_android",
+]
+
+// Defaults with module APIs in the classpath (mostly from prebuilts).
+// Suitable for compiling android-non-updatable.
+stubs_defaults {
+ name: "module-classpath-stubs-defaults",
+ aidl: {
+ include_dirs: [
+ "packages/modules/Bluetooth/framework/aidl-export",
+ "packages/modules/Connectivity/framework/aidl-export",
+ "packages/modules/Media/apex/aidl/stable",
+ ],
+ },
+ libs: non_updatable_api_deps_on_modules,
+}
+
+// Defaults for the java_sdk_libraries of unbundled jars from framework.
+// java_sdk_libraries using these defaults should also add themselves to the
+// non_updatable_modules list in frameworks/base/api/api.go
+java_defaults {
+ name: "framework-non-updatable-unbundled-defaults",
+ defaults: [
+ "framework-non-updatable-lint-defaults",
+ "non-updatable-framework-module-defaults",
+ ],
+ public: {
+ libs: ["android_module_lib_stubs_current"],
+ },
+ system: {
+ libs: ["android_module_lib_stubs_current"],
+ },
+ module_lib: {
+ libs: ["android_module_lib_stubs_current"],
+ },
+ test: {
+ libs: ["android_test_frameworks_core_stubs_current"],
+ },
+ sdk_version: "core_platform",
+ stub_only_libs: ["framework-protos"],
+ impl_only_libs: ["framework-minus-apex-headers"], // the framework, including hidden API
+ impl_library_visibility: ["//frameworks/base"],
+ defaults_visibility: [
+ "//frameworks/base/location",
+ "//frameworks/base/nfc",
+ ],
+ plugins: ["error_prone_android_framework"],
+ errorprone: {
+ javacflags: [
+ "-Xep:AndroidFrameworkCompatChange:ERROR",
+ "-Xep:AndroidFrameworkUid:ERROR",
+ ],
+ },
+ // Include manual annotations in API txt files
+ merge_annotations_dirs: ["metalava-manual"],
+}
+
build = [
"ApiDocs.bp",
"StubLibraries.bp",
]
+
+genrule_defaults {
+ name: "flag-api-mapping-generation-defaults",
+ cmd: "$(location extract-flagged-apis) $(in) $(out)",
+ tools: ["extract-flagged-apis"],
+}
+
+genrule {
+ name: "flag-api-mapping-PublicApi",
+ defaults: ["flag-api-mapping-generation-defaults"],
+ srcs: [":frameworks-base-api-current.txt"],
+ out: ["flag_api_map.textproto"],
+ dist: {
+ targets: ["droid"],
+ },
+}
+
+genrule {
+ name: "flag-api-mapping-SystemApi",
+ defaults: ["flag-api-mapping-generation-defaults"],
+ srcs: [":frameworks-base-api-system-current.txt"],
+ out: ["system_flag_api_map.textproto"],
+ dist: {
+ targets: ["droid"],
+ },
+}
+
+genrule {
+ name: "flag-api-mapping-ModuleLibApi",
+ defaults: ["flag-api-mapping-generation-defaults"],
+ srcs: [":frameworks-base-api-module-lib-current.txt"],
+ out: ["module_lib_flag_api_map.textproto"],
+ dist: {
+ targets: ["droid"],
+ },
+}
+
+genrule {
+ name: "flag-api-mapping-SystemServerApi",
+ defaults: ["flag-api-mapping-generation-defaults"],
+ srcs: [":frameworks-base-api-system-server-current.txt"],
+ out: ["system_server_flag_api_map.textproto"],
+ dist: {
+ targets: ["droid"],
+ },
+}
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index d566552..28b2d4b 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -29,9 +29,6 @@
droidstubs {
name: "api-stubs-docs-non-updatable",
- srcs: [
- ":framework-minus-apex-aconfig-srcjars",
- ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
@@ -91,9 +88,6 @@
droidstubs {
name: "system-api-stubs-docs-non-updatable",
- srcs: [
- ":framework-minus-apex-aconfig-srcjars",
- ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
@@ -134,9 +128,6 @@
droidstubs {
name: "test-api-stubs-docs-non-updatable",
- srcs: [
- ":framework-minus-apex-aconfig-srcjars",
- ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
@@ -184,9 +175,6 @@
droidstubs {
name: "module-lib-api-stubs-docs-non-updatable",
- srcs: [
- ":framework-minus-apex-aconfig-srcjars",
- ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
@@ -363,17 +351,7 @@
"android-non-updatable_from_source_defaults",
],
srcs: [":module-lib-api-stubs-docs-non-updatable"],
- libs: [
- // We cannot depend on all-modules-module-lib-stubs, because the module-lib stubs
- // depend on this stub. We resolve dependencies on APIs in modules by depending
- // on a prebuilt of the whole platform (sdk_system_current_android).
- // That prebuilt does not include module-lib APIs, so use the prebuilt module-lib
- // stubs for modules that export module-lib stubs that the non-updatable part
- // depends on.
- "sdk_module-lib_current_framework-tethering",
- "sdk_module-lib_current_framework-connectivity-t",
- "sdk_system_current_android",
- ],
+ libs: non_updatable_api_deps_on_modules,
dist: {
dir: "apistubs/android/module-lib",
},
@@ -657,6 +635,7 @@
api_contributions: [
"framework-virtualization.stubs.source.test.api.contribution",
"framework-location.stubs.source.test.api.contribution",
+ "framework-nfc.stubs.source.test.api.contribution",
],
}
diff --git a/api/api.go b/api/api.go
index 8df6dab..71b1e10 100644
--- a/api/api.go
+++ b/api/api.go
@@ -32,6 +32,7 @@
const i18n = "i18n.module.public.api"
const virtualization = "framework-virtualization"
const location = "framework-location"
+const nfc = "framework-nfc"
var core_libraries_modules = []string{art, conscrypt, i18n}
@@ -43,7 +44,7 @@
// APIs.
// In addition, the modules in this list are allowed to contribute to test APIs
// stubs.
-var non_updatable_modules = []string{virtualization, location}
+var non_updatable_modules = []string{virtualization, location, nfc}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
diff --git a/api/coverage/tools/Android.bp b/api/coverage/tools/Android.bp
new file mode 100644
index 0000000..3e16912
--- /dev/null
+++ b/api/coverage/tools/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_binary_host {
+ name: "extract-flagged-apis",
+ srcs: ["ExtractFlaggedApis.kt"],
+ main_class: "android.platform.coverage.ExtractFlaggedApisKt",
+ static_libs: [
+ "metalava-signature-reader",
+ "extract_flagged_apis_proto",
+ ],
+}
+
+java_library_host {
+ name: "extract_flagged_apis_proto",
+ srcs: ["extract_flagged_apis.proto"],
+ static_libs: ["libprotobuf-java-full"],
+ proto: {
+ type: "full",
+ },
+}
diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt
new file mode 100644
index 0000000..9ffb704
--- /dev/null
+++ b/api/coverage/tools/ExtractFlaggedApis.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.platform.coverage
+
+import com.android.tools.metalava.model.text.ApiFile
+import java.io.File
+import java.io.FileWriter
+
+/** Usage: extract-flagged-apis <api text file> <output .pb file> */
+fun main(args: Array<String>) {
+ var cb = ApiFile.parseApi(listOf(File(args[0])))
+ val flagToApi = mutableMapOf<String, MutableList<String>>()
+ cb.getPackages()
+ .allClasses()
+ .filter { it.methods().size > 0 }
+ .forEach {
+ for (method in it.methods()) {
+ val flagValue =
+ method.modifiers
+ .findAnnotation("android.annotation.FlaggedApi")
+ ?.findAttribute("value")
+ ?.value
+ ?.value()
+ if (flagValue != null && flagValue is String) {
+ val methodQualifiedName = "${it.qualifiedName()}.${method.name()}"
+ if (flagToApi.containsKey(flagValue)) {
+ flagToApi.get(flagValue)?.add(methodQualifiedName)
+ } else {
+ flagToApi.put(flagValue, mutableListOf(methodQualifiedName))
+ }
+ }
+ }
+ }
+ var builder = FlagApiMap.newBuilder()
+ for (flag in flagToApi.keys) {
+ var flaggedApis = FlaggedApis.newBuilder()
+ for (method in flagToApi.get(flag).orEmpty()) {
+ flaggedApis.addFlaggedApi(FlaggedApi.newBuilder().setQualifiedName(method))
+ }
+ builder.putFlagToApi(flag, flaggedApis.build())
+ }
+ val flagApiMap = builder.build()
+ FileWriter(args[1]).use { it.write(flagApiMap.toString()) }
+}
diff --git a/api/coverage/tools/extract_flagged_apis.proto b/api/coverage/tools/extract_flagged_apis.proto
new file mode 100644
index 0000000..a858108
--- /dev/null
+++ b/api/coverage/tools/extract_flagged_apis.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.platform.coverage;
+
+option java_multiple_files = true;
+
+message FlagApiMap {
+ map<string, FlaggedApis> flag_to_api = 1;
+}
+
+message FlaggedApis {
+ repeated FlaggedApi flagged_api = 1;
+}
+
+message FlaggedApi {
+ string qualified_name = 1;
+}
+
diff --git a/api/gen_combined_removed_dex.sh b/api/gen_combined_removed_dex.sh
index 71f366a..e0153f7 100755
--- a/api/gen_combined_removed_dex.sh
+++ b/api/gen_combined_removed_dex.sh
@@ -6,6 +6,6 @@
# Convert each removed.txt to the "dex format" equivalent, and print all output.
for f in "$@"; do
- "$metalava_path" "$f" --dex-api "${tmp_dir}/tmp"
+ "$metalava_path" signature-to-dex "$f" "${tmp_dir}/tmp"
cat "${tmp_dir}/tmp"
done
diff --git a/boot/Android.bp b/boot/Android.bp
index b33fab6..8a3d35e 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -84,10 +84,6 @@
module: "com.android.conscrypt-bootclasspath-fragment",
},
{
- apex: "com.android.crashrecovery",
- module: "com.android.crashrecovery-bootclasspath-fragment",
- },
- {
apex: "com.android.devicelock",
module: "com.android.devicelock-bootclasspath-fragment",
},
diff --git a/core/api/current.txt b/core/api/current.txt
index fd4da0d..f0c01eea 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4456,6 +4456,7 @@
method public boolean onPreparePanel(int, @Nullable android.view.View, @NonNull android.view.Menu);
method public void onProvideAssistContent(android.app.assist.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
+ method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int);
method public android.net.Uri onProvideReferrer();
method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[]);
method @CallSuper protected void onRestart();
@@ -13770,6 +13771,7 @@
method public String getColumnName(int);
method public android.os.Bundle getExtras();
method public android.net.Uri getNotificationUri();
+ method public java.util.List<android.net.Uri> getNotificationUris();
method public final int getPosition();
method public int getType(int);
method @Deprecated protected Object getUpdatedField(int);
@@ -13795,6 +13797,7 @@
method public android.os.Bundle respond(android.os.Bundle);
method public void setExtras(android.os.Bundle);
method public void setNotificationUri(android.content.ContentResolver, android.net.Uri);
+ method public void setNotificationUris(@NonNull android.content.ContentResolver, @NonNull java.util.List<android.net.Uri>);
method public void unregisterContentObserver(android.database.ContentObserver);
method public void unregisterDataSetObserver(android.database.DataSetObserver);
field @Deprecated protected boolean mClosed;
@@ -13926,6 +13929,7 @@
method public boolean hasNext();
method public java.util.Iterator<android.database.CursorJoiner.Result> iterator();
method public android.database.CursorJoiner.Result next();
+ method public void remove();
}
public enum CursorJoiner.Result {
@@ -13993,6 +13997,7 @@
method public int getInt(int);
method public long getLong(int);
method public android.net.Uri getNotificationUri();
+ method public java.util.List<android.net.Uri> getNotificationUris();
method public int getPosition();
method public short getShort(int);
method public String getString(int);
@@ -14017,6 +14022,7 @@
method public android.os.Bundle respond(android.os.Bundle);
method public void setExtras(android.os.Bundle);
method public void setNotificationUri(android.content.ContentResolver, android.net.Uri);
+ method public void setNotificationUris(android.content.ContentResolver, java.util.List<android.net.Uri>);
method public void unregisterContentObserver(android.database.ContentObserver);
method public void unregisterDataSetObserver(android.database.DataSetObserver);
}
@@ -18291,11 +18297,13 @@
public class BiometricManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate();
method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate(int);
+ method @FlaggedApi("android.hardware.biometrics.last_authentication_time") @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public long getLastAuthenticationTime(int);
method @NonNull @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public android.hardware.biometrics.BiometricManager.Strings getStrings(int);
field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
+ field @FlaggedApi("android.hardware.biometrics.last_authentication_time") public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
}
@@ -18341,6 +18349,7 @@
field public static final int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
field public static final int BIOMETRIC_ERROR_USER_CANCELED = 10; // 0xa
field public static final int BIOMETRIC_ERROR_VENDOR = 8; // 0x8
+ field @FlaggedApi("android.hardware.biometrics.last_authentication_time") public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
}
public abstract static class BiometricPrompt.AuthenticationCallback {
@@ -22641,6 +22650,9 @@
ctor public MediaCodec.CryptoException(int, @Nullable String);
method @Nullable public android.media.MediaCodec.CryptoInfo getCryptoInfo();
method public int getErrorCode();
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8
field @Deprecated public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4
field @Deprecated public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7
@@ -23153,6 +23165,9 @@
public final class MediaCryptoException extends java.lang.Exception implements android.media.MediaDrmThrowable {
ctor public MediaCryptoException(@Nullable String);
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
}
public abstract class MediaDataSource implements java.io.Closeable {
@@ -23377,6 +23392,9 @@
public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable {
method @NonNull public String getDiagnosticInfo();
method public int getErrorCode();
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
method public boolean isTransient();
}
@@ -23450,6 +23468,9 @@
public static final class MediaDrm.SessionException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable {
ctor public MediaDrm.SessionException(int, @Nullable String);
method @Deprecated public int getErrorCode();
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
method public boolean isTransient();
field @Deprecated public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1
field @Deprecated public static final int ERROR_UNKNOWN = 0; // 0x0
@@ -23457,6 +23478,9 @@
public class MediaDrmException extends java.lang.Exception implements android.media.MediaDrmThrowable {
ctor public MediaDrmException(String);
+ method public int getErrorContext();
+ method public int getOemError();
+ method public int getVendorError();
}
public class MediaDrmResetException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable {
@@ -28951,6 +28975,7 @@
method @NonNull public long[] getRetryIntervalsMillis();
method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
method public boolean hasGatewayOption(int);
+ method @FlaggedApi("android.net.vcn.safe_mode_config") public boolean isSafeModeEnabled();
field public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0; // 0x0
}
@@ -28964,6 +28989,7 @@
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
+ method @FlaggedApi("android.net.vcn.safe_mode_config") @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setSafeModeEnabled(boolean);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
}
@@ -29079,14 +29105,17 @@
}
public final class NfcAdapter {
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean allowTransaction();
method public void disableForegroundDispatch(android.app.Activity);
method public void disableReaderMode(android.app.Activity);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean disallowTransaction();
method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]);
method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
method public boolean isEnabled();
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean isObserveModeSupported();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionEnabled();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported();
method public boolean isSecureNfcEnabled();
@@ -29194,6 +29223,7 @@
method public boolean removeAidsForService(android.content.ComponentName, String);
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String);
method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setServiceObserveModeDefault(@NonNull android.content.ComponentName, boolean);
method public boolean supportsAidPrefixRegistration();
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName);
method public boolean unsetPreferredService(android.app.Activity);
@@ -29213,9 +29243,20 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onDeactivated(int);
method public abstract byte[] processCommandApdu(byte[], android.os.Bundle);
+ method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.os.Bundle>);
method public final void sendResponseApdu(byte[]);
field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_A = 65; // 0x0041 'A'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_B = 66; // 0x0042 'B'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_F = 70; // 0x0046 'F'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE";
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_OFF = 88; // 0x0058 'X'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_ON = 79; // 0x004f 'O'
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x0055 'U'
field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
}
@@ -32322,6 +32363,7 @@
method public void surfaceCreated(android.view.SurfaceHolder);
method public void surfaceDestroyed(android.view.SurfaceHolder);
method @Deprecated public void surfaceRedrawNeeded(android.view.SurfaceHolder);
+ method public void surfaceRedrawNeededAsync(android.view.SurfaceHolder, Runnable);
field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1
field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2
field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1
@@ -32662,7 +32704,7 @@
field public static final int S_V2 = 32; // 0x20
field public static final int TIRAMISU = 33; // 0x21
field public static final int UPSIDE_DOWN_CAKE = 34; // 0x22
- field public static final int VANILLA_ICE_CREAM = 10000; // 0x2710
+ field @FlaggedApi("android.os.android_os_build_vanilla_ice_cream") public static final int VANILLA_ICE_CREAM = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -33194,7 +33236,7 @@
method public static android.os.Message obtain(android.os.Handler, int, Object);
method public static android.os.Message obtain(android.os.Handler, int, int, int);
method public static android.os.Message obtain(android.os.Handler, int, int, int, Object);
- method public android.os.Bundle peekData();
+ method @Nullable public android.os.Bundle peekData();
method public void recycle();
method public void sendToTarget();
method public void setAsynchronous(boolean);
@@ -39536,7 +39578,7 @@
method @Nullable public java.util.Date getKeyValidityStart();
method @NonNull public String getKeystoreAlias();
method public int getMaxUsageCount();
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -39544,7 +39586,7 @@
method public boolean isDevicePropertiesAttestationIncluded();
method @NonNull public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified();
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public boolean isMgf1DigestsSpecified();
method public boolean isRandomizedEncryptionRequired();
method public boolean isStrongBoxBacked();
method public boolean isUnlockedDeviceRequired();
@@ -39576,7 +39618,7 @@
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int);
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@Nullable java.lang.String...);
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@NonNull java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
@@ -39681,14 +39723,14 @@
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
method public int getMaxUsageCount();
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
method public int getUserAuthenticationValidityDurationSeconds();
method public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified();
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public boolean isMgf1DigestsSpecified();
method public boolean isRandomizedEncryptionRequired();
method public boolean isUnlockedDeviceRequired();
method public boolean isUserAuthenticationRequired();
@@ -39710,7 +39752,7 @@
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int);
- method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...);
+ method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
@@ -47781,6 +47823,7 @@
method public boolean hasNext();
method public java.util.Iterator<java.lang.String> iterator();
method public String next();
+ method public void remove();
method public void setString(String);
}
@@ -49235,6 +49278,7 @@
method public void ensureCapacity(int);
method public java.util.Set<java.util.Map.Entry<K,V>> entrySet();
method public boolean equals(@Nullable Object);
+ method public void forEach(java.util.function.BiConsumer<? super K,? super V>);
method public V get(Object);
method public int hashCode();
method public int indexOfKey(Object);
@@ -49248,6 +49292,7 @@
method public V remove(Object);
method public boolean removeAll(java.util.Collection<?>);
method public V removeAt(int);
+ method public void replaceAll(java.util.function.BiFunction<? super K,? super V,? extends V>);
method public boolean retainAll(java.util.Collection<?>);
method public V setValueAt(int, V);
method public int size();
@@ -49278,6 +49323,7 @@
method public boolean removeAll(android.util.ArraySet<? extends E>);
method public boolean removeAll(java.util.Collection<?>);
method public E removeAt(int);
+ method public boolean removeIf(java.util.function.Predicate<? super E>);
method public boolean retainAll(java.util.Collection<?>);
method public int size();
method public Object[] toArray();
@@ -53132,6 +53178,7 @@
method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
method protected boolean drawChild(@NonNull android.graphics.Canvas, android.view.View, long);
method public void endViewTransition(android.view.View);
+ method @Nullable public android.window.OnBackInvokedDispatcher findOnBackInvokedDispatcherForChild(@NonNull android.view.View, @NonNull android.view.View);
method public android.view.View focusSearch(android.view.View, int);
method public void focusableViewAvailable(android.view.View);
method protected android.view.ViewGroup.LayoutParams generateDefaultLayoutParams();
@@ -53173,6 +53220,7 @@
method public void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
method public final void offsetDescendantRectToMyCoords(android.view.View, android.graphics.Rect);
method public final void offsetRectIntoDescendantCoords(android.view.View, android.graphics.Rect);
+ method @CallSuper public void onDescendantInvalidated(@NonNull android.view.View, @NonNull android.view.View);
method public boolean onInterceptHoverEvent(android.view.MotionEvent);
method public boolean onInterceptTouchEvent(android.view.MotionEvent);
method protected abstract void onLayout(boolean, int, int, int, int);
@@ -55332,12 +55380,14 @@
method @Nullable public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method @Nullable public android.os.Handler getHandler();
method @Nullable public CharSequence getSelectedText(int);
+ method @Nullable public android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
method public static final void removeComposingSpans(@NonNull android.text.Spannable);
+ method public boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
method public boolean sendKeyEvent(android.view.KeyEvent);
@@ -55345,6 +55395,7 @@
method public static void setComposingSpans(@NonNull android.text.Spannable);
method public boolean setComposingText(CharSequence, int);
method public boolean setSelection(int, int);
+ method @Nullable public android.view.inputmethod.TextSnapshot takeSnapshot();
}
public final class CompletionInfo implements android.os.Parcelable {
@@ -55679,6 +55730,7 @@
method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle);
method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo);
method public boolean commitText(CharSequence, int);
+ method public boolean commitText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean deleteSurroundingText(int, int);
method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
@@ -55687,18 +55739,29 @@
method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
method public android.os.Handler getHandler();
method public CharSequence getSelectedText(int);
+ method @Nullable public android.view.inputmethod.SurroundingText getSurroundingText(int, int, int);
method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
+ method public void performHandwritingGesture(@NonNull android.view.inputmethod.HandwritingGesture, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.IntConsumer);
method public boolean performPrivateCommand(String, android.os.Bundle);
+ method public boolean performSpellCheck();
+ method public boolean previewHandwritingGesture(@NonNull android.view.inputmethod.PreviewableHandwritingGesture, @Nullable android.os.CancellationSignal);
+ method public boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
+ method public boolean requestCursorUpdates(int, int);
+ method public void requestTextBoundsInfo(@NonNull android.graphics.RectF, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.inputmethod.TextBoundsInfoResult>);
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
+ method public boolean setComposingRegion(int, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean setComposingText(CharSequence, int);
+ method public boolean setComposingText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
+ method public boolean setImeConsumesInput(boolean);
method public boolean setSelection(int, int);
method public void setTarget(android.view.inputmethod.InputConnection);
+ method @Nullable public android.view.inputmethod.TextSnapshot takeSnapshot();
}
public final class InputContentInfo implements android.os.Parcelable {
@@ -58265,6 +58328,7 @@
public abstract class BaseAdapter implements android.widget.ListAdapter android.widget.SpinnerAdapter {
ctor public BaseAdapter();
method public boolean areAllItemsEnabled();
+ method public CharSequence[] getAutofillOptions();
method public android.view.View getDropDownView(int, android.view.View, android.view.ViewGroup);
method public int getItemViewType(int);
method public int getViewTypeCount();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 7cfa1e3..1a22e9b 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -14,7 +14,7 @@
@UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public final boolean addDumpable(@NonNull android.util.Dumpable);
- method public final boolean isResumed();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public final boolean isResumed();
}
public class ActivityManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b6c9678..95dc07f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -298,7 +298,7 @@
field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
- field public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
+ field @FlaggedApi("com.android.net.flags.register_nsd_offload_engine") public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE";
field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM";
field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER";
@@ -357,6 +357,7 @@
field public static final String SYSTEM_APPLICATION_OVERLAY = "android.permission.SYSTEM_APPLICATION_OVERLAY";
field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
+ field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED";
field public static final String TIS_EXTENSION_INTERFACE = "android.permission.TIS_EXTENSION_INTERFACE";
field public static final String TOGGLE_AUTOMOTIVE_PROJECTION = "android.permission.TOGGLE_AUTOMOTIVE_PROJECTION";
field public static final String TRIGGER_LOST_MODE = "android.permission.TRIGGER_LOST_MODE";
@@ -10244,6 +10245,7 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState();
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
@@ -10254,6 +10256,7 @@
method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderMode(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff
field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0
field public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2; // 0xfffffffe
@@ -10301,6 +10304,7 @@
method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean hasCategory(@NonNull String);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOnHost();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOtherServiceEnabled();
method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager);
method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager);
method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
@@ -10311,10 +10315,15 @@
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOtherServiceEnabled(boolean);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
}
+ public final class CardEmulation {
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int);
+ }
+
@FlaggedApi("android.nfc.enable_nfc_mainline") public final class NfcFServiceInfo implements android.os.Parcelable {
ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
@@ -10789,6 +10798,7 @@
ctor public ParcelableHolder(int);
method public int describeContents();
method @Nullable public <T extends android.os.Parcelable> T getParcelable(@NonNull Class<T>);
+ method public int getStability();
method public void readFromParcel(@NonNull android.os.Parcel);
method public void setParcelable(@Nullable android.os.Parcelable);
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 26b7581..d5acf42 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -226,7 +226,6 @@
"com/android/internal/util/ConcurrentUtils.java",
"com/android/internal/util/DumpUtils.java",
"com/android/internal/util/FastPrintWriter.java",
- "com/android/internal/util/FastXmlSerializer.java",
"com/android/internal/util/FunctionalUtils.java",
"com/android/internal/util/ParseUtils.java",
"com/android/internal/util/RingBufferIndices.java",
@@ -414,6 +413,10 @@
backend: {
rust: {
enabled: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
},
},
}
@@ -455,7 +458,6 @@
"com/android/internal/util/AsyncChannel.java",
"com/android/internal/util/AsyncService.java",
"com/android/internal/util/BitwiseInputStream.java",
- "com/android/internal/util/FastXmlSerializer.java",
"com/android/internal/util/HexDump.java",
"com/android/internal/util/IndentingPrintWriter.java",
"com/android/internal/util/UserIcons.java",
@@ -505,7 +507,6 @@
"android/net/InterfaceConfiguration.java",
"android/util/BackupUtils.java",
"android/util/Rational.java",
- "com/android/internal/util/FastXmlSerializer.java",
"com/android/internal/util/HexDump.java",
"com/android/internal/util/MessageUtils.java",
"com/android/internal/util/WakeupMessage.java",
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2590869..06c139f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -31,6 +31,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.LayoutRes;
@@ -79,6 +80,7 @@
import android.media.AudioManager;
import android.media.session.MediaController;
import android.net.Uri;
+import android.nfc.Flags;
import android.os.BadParcelableException;
import android.os.Build;
import android.os.Bundle;
@@ -8915,6 +8917,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final boolean isResumed() {
return mResumed;
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 2d55403..c072feb 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -679,10 +679,11 @@
* <p>
* Specifically, this returns {@code true} if at least one of the following is true:
* <ul>
- * <li>The {@link Context}'s user has a secure lock screen. A full user has a secure lock
- * screen if its lock screen is set to PIN, pattern, or password, as opposed to swipe or none.
- * A profile that uses a unified challenge is considered to have a secure lock screen if and
- * only if its parent user has a secure lock screen.</li>
+ * <li>The {@link Context}'s user has a secure lock screen. A full user or a profile that uses
+ * a separate challenge has a secure lock screen if its lock screen is set to PIN, pattern, or
+ * password, as opposed to swipe or none. A profile that uses a unified challenge is
+ * considered to have a secure lock screen if and only if its parent user has a secure lock
+ * screen.</li>
* <li>At least one SIM card is currently locked and requires a PIN.</li>
* </ul>
* <p>
@@ -729,8 +730,15 @@
* <p>
* For a user that is not the current user but can be switched to (usually this means "another
* full user"), and that has a PIN, pattern, or password, the device is always considered
- * locked. For a profile with a unified challenge, the device is considered locked if and only
- * if the device is locked for the parent user.
+ * locked.
+ * <p>
+ * For a profile with a unified challenge, the device locked state is the same as that of the
+ * parent user.
+ * <p>
+ * For a profile with a separate challenge, the device becomes unlocked when the profile's PIN,
+ * pattern, password, or biometric is verified. It becomes locked when the parent user becomes
+ * locked, the screen turns off, the device reboots, the device policy controller locks the
+ * profile, or the timeout set by the device policy controller expires.
*
* @return {@code true} if the device is currently locked for the user
* @see #isKeyguardLocked()
@@ -757,9 +765,10 @@
* Returns whether the user has a secure lock screen.
* <p>
* This returns {@code true} if the {@link Context}'s user has a secure lock screen. A full user
- * has a secure lock screen if its lock screen is set to PIN, pattern, or password, as opposed
- * to swipe or none. A profile that uses a unified challenge is considered to have a secure lock
- * screen if and only if its parent user has a secure lock screen.
+ * or a profile that uses a separate challenge has a secure lock screen if its lock screen is
+ * set to PIN, pattern, or password, as opposed to swipe or none. A profile that uses a unified
+ * challenge is considered to have a secure lock screen if and only if its parent user has a
+ * secure lock screen.
* <p>
* This method does not consider whether the lock screen is currently showing or not.
* <p>
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e1c45d9..164bdbe 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -36,6 +36,7 @@
per-file GameState* = file:/GAME_MANAGER_OWNERS
per-file IGameManager* = file:/GAME_MANAGER_OWNERS
per-file IGameMode* = file:/GAME_MANAGER_OWNERS
+per-file BackgroundStartPrivileges.java = file:/BAL_OWNERS
# ActivityThread
per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 4f5da99..325758b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -113,6 +113,7 @@
import android.hardware.lights.LightsManager;
import android.hardware.lights.SystemLightsManager;
import android.hardware.location.ContextHubManager;
+import android.hardware.location.IContextHubService;
import android.hardware.radio.RadioManager;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbManager;
@@ -1125,8 +1126,12 @@
new CachedServiceFetcher<ContextHubManager>() {
@Override
public ContextHubManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- return new ContextHubManager(ctx.getOuterContext(),
- ctx.mMainThread.getHandler().getLooper());
+ IBinder b = ServiceManager.getService(Context.CONTEXTHUB_SERVICE);
+ if (b == null) {
+ return null;
+ }
+ return new ContextHubManager(IContextHubService.Stub.asInterface(b),
+ ctx.mMainThread.getHandler().getLooper());
}});
registerService(Context.INCIDENT_SERVICE, IncidentManager.class,
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index cd45f4d..b4f4a7e 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -212,6 +212,11 @@
}
/** @hide */
+ public AttributionSource withDefaultToken() {
+ return withToken(sDefaultToken);
+ }
+
+ /** @hide */
public AttributionSource withPid(int pid) {
return new AttributionSource(getUid(), pid, getPackageName(), getAttributionTag(),
getToken(), mAttributionSourceState.renouncedPermissions, getNext());
@@ -520,16 +525,28 @@
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AttributionSource that = (AttributionSource) o;
- return mAttributionSourceState.uid == that.mAttributionSourceState.uid
+ return equalsExceptToken(that) && Objects.equals(
+ mAttributionSourceState.token, that.mAttributionSourceState.token);
+ }
+
+ /**
+ * We store trusted attribution sources without their token (the token is the key to the map)
+ * to avoid having a strong reference to the token. This means, when checking the equality of a
+ * supplied AttributionSource in PermissionManagerService.isTrustedAttributionSource, we want to
+ * compare everything except the token.
+ *
+ * @hide
+ */
+ public boolean equalsExceptToken(@Nullable AttributionSource o) {
+ if (o == null) return false;
+ return mAttributionSourceState.uid == o.mAttributionSourceState.uid
&& Objects.equals(mAttributionSourceState.packageName,
- that.mAttributionSourceState.packageName)
+ o.mAttributionSourceState.packageName)
&& Objects.equals(mAttributionSourceState.attributionTag,
- that.mAttributionSourceState.attributionTag)
- && Objects.equals(mAttributionSourceState.token,
- that.mAttributionSourceState.token)
+ o.mAttributionSourceState.attributionTag)
&& Arrays.equals(mAttributionSourceState.renouncedPermissions,
- that.mAttributionSourceState.renouncedPermissions)
- && Objects.equals(getNext(), that.getNext());
+ o.mAttributionSourceState.renouncedPermissions)
+ && Objects.equals(getNext(), o.getNext());
}
@Override
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c3fd744..0fd0e15 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1161,6 +1161,11 @@
* numbers. Applications can <strong>dial</strong> emergency numbers using
* {@link #ACTION_DIAL}, however.
*
+ * <p>Note: This Intent can only be used to dial call forwarding MMI codes if the application
+ * using this intent is set as the default or system dialer. The system will treat any other
+ * application using this Intent for the purpose of dialing call forwarding MMI codes as if the
+ * {@link #ACTION_DIAL} Intent was used instead.
+ *
* <p>Note: An app filling the {@link android.app.role.RoleManager#ROLE_DIALER} role should use
* {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls rather than
* relying on this intent.
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 943eee4..61d8702 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -18,6 +18,7 @@
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -296,4 +297,15 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({BIOMETRIC_LOCKOUT_NONE, BIOMETRIC_LOCKOUT_TIMED, BIOMETRIC_LOCKOUT_PERMANENT})
@interface LockoutMode {}
+
+ //
+ // Other miscellaneous constants
+ //
+
+ /**
+ * Returned from {@link BiometricManager#getLastAuthenticationTime(int)} when there has
+ * been no successful authentication for the given authenticator since boot.
+ */
+ @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
+ long BIOMETRIC_NO_AUTHENTICATION = -1;
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 82694ee..90bbca8 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -23,6 +23,8 @@
import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_BIOMETRIC_MANAGER_CAN_AUTHENTICATE;
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +32,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.app.KeyguardManager;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
@@ -86,6 +89,17 @@
BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
/**
+ * Returned from {@link BiometricManager#getLastAuthenticationTime(int)} when no matching
+ * successful authentication has been performed since boot.
+ */
+ @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
+ public static final long BIOMETRIC_NO_AUTHENTICATION =
+ BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+
+ private static final int GET_LAST_AUTH_TIME_ALLOWED_AUTHENTICATORS =
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_STRONG;
+
+ /**
* @hide
*/
@IntDef({BIOMETRIC_SUCCESS,
@@ -637,5 +651,58 @@
}
}
+
+ /**
+ * Gets the last time the user successfully authenticated using one of the given authenticators.
+ * The returned value is time in
+ * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()} (time since
+ * boot, including sleep).
+ * <p>
+ * {@link BiometricManager#BIOMETRIC_NO_AUTHENTICATION} is returned in the case where there
+ * has been no successful authentication using any of the given authenticators since boot.
+ * <p>
+ * Currently, only {@link Authenticators#DEVICE_CREDENTIAL} and
+ * {@link Authenticators#BIOMETRIC_STRONG} are accepted. {@link IllegalArgumentException} will
+ * be thrown if {@code authenticators} contains other authenticator types.
+ * <p>
+ * Note that this may return successful authentication times even if the device is currently
+ * locked. You may use {@link KeyguardManager#isDeviceLocked()} to determine if the device
+ * is unlocked or not. Additionally, this method may return valid times for an authentication
+ * method that is no longer available. For instance, if the user unlocked the device with a
+ * {@link Authenticators#BIOMETRIC_STRONG} authenticator but then deleted that authenticator
+ * (e.g., fingerprint data), this method will still return the time of that unlock for
+ * {@link Authenticators#BIOMETRIC_STRONG} if it is the most recent successful event. The caveat
+ * is that {@link BiometricManager#BIOMETRIC_NO_AUTHENTICATION} will be returned if the device
+ * no longer has a secure lock screen at all, even if there were successful authentications
+ * performed before the lock screen was made insecure.
+ *
+ * @param authenticators bit field consisting of constants defined in {@link Authenticators}.
+ * @return the time of last authentication or
+ * {@link BiometricManager#BIOMETRIC_NO_AUTHENTICATION}
+ * @throws IllegalArgumentException if {@code authenticators} contains values other than
+ * {@link Authenticators#DEVICE_CREDENTIAL} and {@link Authenticators#BIOMETRIC_STRONG} or is
+ * 0.
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ @ElapsedRealtimeLong
+ @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
+ public long getLastAuthenticationTime(
+ @BiometricManager.Authenticators.Types int authenticators) {
+ if (authenticators == 0
+ || (GET_LAST_AUTH_TIME_ALLOWED_AUTHENTICATORS & authenticators) != authenticators) {
+ throw new IllegalArgumentException(
+ "Only BIOMETRIC_STRONG and DEVICE_CREDENTIAL authenticators may be used.");
+ }
+
+ if (mService != null) {
+ try {
+ return mService.getLastAuthenticationTime(UserHandle.myUserId(), authenticators);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ return BIOMETRIC_NO_AUTHENTICATION;
+ }
+ }
}
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index c2e5c0b..5bdbe2b5 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -57,6 +57,9 @@
// Checks if biometrics can be used.
int canAuthenticate(String opPackageName, int userId, int authenticators);
+ // Gets the time of last authentication for the given user and authenticators.
+ long getLastAuthenticationTime(int userId, int authenticators);
+
// Checks if any biometrics are enrolled.
boolean hasEnrolledBiometrics(int userId, String opPackageName);
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 18c8d1b..058f302 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -53,6 +53,10 @@
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
int canAuthenticate(String opPackageName, int userId, int callingUserId, int authenticators);
+ // Gets the time of last authentication for the given user and authenticators.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ long getLastAuthenticationTime(int userId, int authenticators);
+
// Checks if any biometrics are enrolled.
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
boolean hasEnrolledBiometrics(int userId, String opPackageName);
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 0924e0d1..c370ad6 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -1,6 +1,13 @@
package: "android.hardware.biometrics"
flag {
+ name: "last_authentication_time"
+ namespace: "wallet_integration"
+ description: "Feature flag for adding getLastAuthenticationTime API to BiometricManager"
+ bug: "301979982"
+}
+
+flag {
name: "add_key_agreement_crypto_object"
namespace: "biometrics"
description: "Feature flag for adding KeyAgreement api to CryptoObject."
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 01ce7b9..481ec72 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -15,6 +15,8 @@
*/
package android.hardware.location;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -1111,12 +1113,12 @@
}
};
- /** @throws ServiceNotFoundException
- * @hide */
- public ContextHubManager(Context context, Looper mainLooper) throws ServiceNotFoundException {
+ /** @hide */
+ public ContextHubManager(@NonNull IContextHubService service, @NonNull Looper mainLooper) {
+ requireNonNull(service, "service cannot be null");
+ requireNonNull(mainLooper, "mainLooper cannot be null");
+ mService = service;
mMainLooper = mainLooper;
- mService = IContextHubService.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.CONTEXTHUB_SERVICE));
try {
mService.registerCallback(mClientCallback);
} catch (RemoteException e) {
diff --git a/core/java/android/net/INetworkManagementEventObserver.aidl b/core/java/android/net/INetworkManagementEventObserver.aidl
index 0a6be20..eda80c8 100644
--- a/core/java/android/net/INetworkManagementEventObserver.aidl
+++ b/core/java/android/net/INetworkManagementEventObserver.aidl
@@ -85,14 +85,14 @@
/**
* Interface data activity status is changed.
*
- * @param transportType The transport type of the data activity change.
+ * @param label label of the data activity change.
* @param active True if the interface is actively transmitting data, false if it is idle.
* @param tsNanos Elapsed realtime in nanos when the state of the network interface changed.
* @param uid Uid of this event. It represents the uid that was responsible for waking the
* radio. For those events that are reported by system itself, not from specific uid,
* use -1 for the events which means no uid.
*/
- void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos, int uid);
+ void interfaceClassDataActivityChanged(int label, boolean active, long tsNanos, int uid);
/**
* Information about available DNS servers has been received.
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index a40fb15..6f11d3a 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,10 +16,12 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+import static android.net.vcn.Flags.FLAG_SAFE_MODE_CONFIG;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -235,6 +237,9 @@
"mMinUdpPort4500NatTimeoutSeconds";
private final int mMinUdpPort4500NatTimeoutSeconds;
+ private static final String IS_SAFE_MODE_DISABLED_KEY = "mIsSafeModeDisabled";
+ private final boolean mIsSafeModeDisabled;
+
private static final String GATEWAY_OPTIONS_KEY = "mGatewayOptions";
@NonNull private final Set<Integer> mGatewayOptions;
@@ -247,6 +252,7 @@
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu,
@NonNull int minUdpPort4500NatTimeoutSeconds,
+ boolean isSafeModeDisabled,
@NonNull Set<Integer> gatewayOptions) {
mGatewayConnectionName = gatewayConnectionName;
mTunnelConnectionParams = tunnelConnectionParams;
@@ -255,6 +261,7 @@
mMaxMtu = maxMtu;
mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
mGatewayOptions = Collections.unmodifiableSet(new ArraySet(gatewayOptions));
+ mIsSafeModeDisabled = isSafeModeDisabled;
mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates);
if (mUnderlyingNetworkTemplates.isEmpty()) {
@@ -317,6 +324,7 @@
in.getInt(
MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS_KEY,
MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET);
+ mIsSafeModeDisabled = in.getBoolean(IS_SAFE_MODE_DISABLED_KEY);
validate();
}
@@ -483,6 +491,16 @@
}
/**
+ * Check whether safe mode is enabled
+ *
+ * @see Builder#setSafeModeEnabled(boolean)
+ */
+ @FlaggedApi(FLAG_SAFE_MODE_CONFIG)
+ public boolean isSafeModeEnabled() {
+ return !mIsSafeModeDisabled;
+ }
+
+ /**
* Checks if the given VCN gateway option is enabled.
*
* @param option the option to check.
@@ -528,6 +546,7 @@
result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
result.putInt(MAX_MTU_KEY, mMaxMtu);
result.putInt(MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS_KEY, mMinUdpPort4500NatTimeoutSeconds);
+ result.putBoolean(IS_SAFE_MODE_DISABLED_KEY, mIsSafeModeDisabled);
return result;
}
@@ -542,6 +561,7 @@
Arrays.hashCode(mRetryIntervalsMs),
mMaxMtu,
mMinUdpPort4500NatTimeoutSeconds,
+ mIsSafeModeDisabled,
mGatewayOptions);
}
@@ -559,6 +579,7 @@
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
&& mMaxMtu == rhs.mMaxMtu
&& mMinUdpPort4500NatTimeoutSeconds == rhs.mMinUdpPort4500NatTimeoutSeconds
+ && mIsSafeModeDisabled == rhs.mIsSafeModeDisabled
&& mGatewayOptions.equals(rhs.mGatewayOptions);
}
@@ -577,6 +598,7 @@
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
private int mMaxMtu = DEFAULT_MAX_MTU;
private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
+ private boolean mIsSafeModeDisabled = false;
@NonNull private final Set<Integer> mGatewayOptions = new ArraySet<>();
@@ -789,6 +811,27 @@
}
/**
+ * Enable/disable safe mode
+ *
+ * <p>If a VCN fails to provide connectivity within a system-provided timeout, it will enter
+ * safe mode. In safe mode, the VCN Network will be torn down and the system will restore
+ * connectivity by allowing underlying cellular or WiFi networks to be used as default. At
+ * the same time, VCN will continue to retry until it succeeds.
+ *
+ * <p>When safe mode is disabled and VCN connection fails to provide connectivity, end users
+ * might not have connectivity, and may not have access to carrier-owned underlying
+ * networks.
+ *
+ * @param enabled whether safe mode should be enabled. Defaults to {@code true}
+ */
+ @FlaggedApi(FLAG_SAFE_MODE_CONFIG)
+ @NonNull
+ public Builder setSafeModeEnabled(boolean enabled) {
+ mIsSafeModeDisabled = !enabled;
+ return this;
+ }
+
+ /**
* Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
@@ -803,6 +846,7 @@
mRetryIntervalsMs,
mMaxMtu,
mMinUdpPort4500NatTimeoutSeconds,
+ mIsSafeModeDisabled,
mGatewayOptions);
}
}
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
new file mode 100644
index 0000000..6956916
--- /dev/null
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.net.vcn"
+
+flag {
+ name: "safe_mode_config"
+ namespace: "vcn"
+ description: "Feature flag for safe mode configurability"
+ bug: "276358140"
+}
\ No newline at end of file
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 0c95c2e..f6beec1 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -84,4 +84,6 @@
boolean isReaderOptionSupported();
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
boolean enableReaderOption(boolean enable);
+ boolean isObserveModeSupported();
+ boolean setObserveMode(boolean enabled);
}
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index 53843fe..191385a 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -30,6 +30,7 @@
boolean isDefaultServiceForAid(int userHandle, in ComponentName service, String aid);
boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
boolean setDefaultForNextTap(int userHandle, in ComponentName service);
+ boolean setServiceObserveModeDefault(int userId, in android.content.ComponentName service, boolean enable);
boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
boolean unsetOffHostForService(int userHandle, in ComponentName service);
@@ -40,5 +41,6 @@
boolean unsetPreferredService();
boolean supportsAidPrefixRegistration();
ApduServiceInfo getPreferredPaymentService(int userHandle);
+ boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
boolean isDefaultPaymentRegistered();
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 4a7bd3f..98a980f 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -378,6 +378,8 @@
* <p>An external NFC field detected when device locked and SecureNfc enabled.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC =
"android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
@@ -944,7 +946,8 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public int getAdapterState() {
try {
return sService.getState();
@@ -1078,6 +1081,61 @@
}
}
+
+ /**
+ * Returns whether the device supports observer mode or not. When observe
+ * mode is enabled, the NFC hardware will listen for NFC readers, but not
+ * respond to them. When observe mode is disabled, the NFC hardware will
+ * resoond to the reader and proceed with the transaction.
+ * @return true if the mode is supported, false otherwise.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean isObserveModeSupported() {
+ try {
+ return sService.isObserveModeSupported();
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
+ /**
+ * Disables observe mode to allow the transaction to proceed. See
+ * {@link #isObserveModeSupported()} for a description of observe mode and
+ * use {@link #disallowTransaction()} to enable observe mode and block
+ * transactions again.
+ *
+ * @return boolean indicating success or failure.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean allowTransaction() {
+ try {
+ return sService.setObserveMode(false);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
+ /**
+ * Signals that the transaction has completed and observe mode may be
+ * reenabled. See {@link #isObserveModeSupported()} for a description of
+ * observe mode and use {@link #allowTransaction()} to disable observe
+ * mode and allow transactions to proceed.
+ *
+ * @return boolean indicating success or failure.
+ */
+
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean disallowTransaction() {
+ try {
+ return sService.setObserveMode(true);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ return false;
+ }
+ }
+
/**
* Resumes default polling for the current device state if polling is paused. Calling
* this while polling is not paused is a no-op.
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 665b753..e331c95 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -21,6 +21,7 @@
package android.nfc.cardemulation;
import android.annotation.FlaggedApi;
+import android.compat.annotation.UnsupportedAppUsage;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -127,15 +128,34 @@
private final String mSettingsActivityName;
/**
+ * State of the service for CATEGORY_OTHER selection
+ */
+ private boolean mOtherServiceEnabled;
+
+ /**
* @hide
*/
+ @UnsupportedAppUsage
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
- List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
+ ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
boolean requiresUnlock, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost) {
this(info, onHost, description, staticAidGroups, dynamicAidGroups,
+ requiresUnlock, bannerResource, uid, settingsActivityName,
+ offHost, staticOffHost, false);
+ }
+
+ /**
+ * @hide
+ */
+ public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+ ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
+ boolean requiresUnlock, int bannerResource, int uid,
+ String settingsActivityName, String offHost, String staticOffHost,
+ boolean isEnabled) {
+ this(info, onHost, description, staticAidGroups, dynamicAidGroups,
requiresUnlock, onHost ? true : false, bannerResource, uid,
- settingsActivityName, offHost, staticOffHost);
+ settingsActivityName, offHost, staticOffHost, isEnabled);
}
/**
@@ -144,7 +164,7 @@
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
- String settingsActivityName, String offHost, String staticOffHost) {
+ String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled) {
this.mService = info;
this.mDescription = description;
this.mStaticAidGroups = new HashMap<String, AidGroup>();
@@ -163,6 +183,8 @@
this.mBannerResourceId = bannerResource;
this.mUid = uid;
this.mSettingsActivityName = settingsActivityName;
+ this.mOtherServiceEnabled = isEnabled;
+
}
/**
@@ -351,6 +373,9 @@
}
// Set uid
mUid = si.applicationInfo.uid;
+
+ mOtherServiceEnabled = false; // support other category
+
}
/**
@@ -720,43 +745,47 @@
dest.writeInt(mBannerResourceId);
dest.writeInt(mUid);
dest.writeString(mSettingsActivityName);
+
+ dest.writeInt(mOtherServiceEnabled ? 1 : 0);
};
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public static final @NonNull Parcelable.Creator<ApduServiceInfo> CREATOR =
new Parcelable.Creator<ApduServiceInfo>() {
- @Override
- public ApduServiceInfo createFromParcel(Parcel source) {
- ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
- String description = source.readString();
- boolean onHost = source.readInt() != 0;
- String offHostName = source.readString();
- String staticOffHostName = source.readString();
- ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>();
- int numStaticGroups = source.readInt();
- if (numStaticGroups > 0) {
- source.readTypedList(staticAidGroups, AidGroup.CREATOR);
- }
- ArrayList<AidGroup> dynamicAidGroups = new ArrayList<AidGroup>();
- int numDynamicGroups = source.readInt();
- if (numDynamicGroups > 0) {
- source.readTypedList(dynamicAidGroups, AidGroup.CREATOR);
- }
- boolean requiresUnlock = source.readInt() != 0;
- boolean requiresScreenOn = source.readInt() != 0;
- int bannerResource = source.readInt();
- int uid = source.readInt();
- String settingsActivityName = source.readString();
- return new ApduServiceInfo(info, onHost, description, staticAidGroups,
- dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
- settingsActivityName, offHostName, staticOffHostName);
- }
+ @Override
+ public ApduServiceInfo createFromParcel(Parcel source) {
+ ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
+ String description = source.readString();
+ boolean onHost = source.readInt() != 0;
+ String offHostName = source.readString();
+ String staticOffHostName = source.readString();
+ ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>();
+ int numStaticGroups = source.readInt();
+ if (numStaticGroups > 0) {
+ source.readTypedList(staticAidGroups, AidGroup.CREATOR);
+ }
+ ArrayList<AidGroup> dynamicAidGroups = new ArrayList<AidGroup>();
+ int numDynamicGroups = source.readInt();
+ if (numDynamicGroups > 0) {
+ source.readTypedList(dynamicAidGroups, AidGroup.CREATOR);
+ }
+ boolean requiresUnlock = source.readInt() != 0;
+ boolean requiresScreenOn = source.readInt() != 0;
+ int bannerResource = source.readInt();
+ int uid = source.readInt();
+ String settingsActivityName = source.readString();
+ boolean isEnabled = source.readInt() != 0;
+ return new ApduServiceInfo(info, onHost, description, staticAidGroups,
+ dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
+ settingsActivityName, offHostName, staticOffHostName,
+ isEnabled);
+ }
- @Override
- public ApduServiceInfo[] newArray(int size) {
- return new ApduServiceInfo[size];
- }
- };
+ @Override
+ public ApduServiceInfo[] newArray(int size) {
+ return new ApduServiceInfo[size];
+ }
+ };
/**
* Dump contents for debugging.
@@ -779,14 +808,16 @@
}
pw.println(" Static AID groups:");
for (AidGroup group : mStaticAidGroups.values()) {
- pw.println(" Category: " + group.getCategory());
+ pw.println(" Category: " + group.getCategory()
+ + "(enabled: " + mOtherServiceEnabled + ")");
for (String aid : group.getAids()) {
pw.println(" AID: " + aid);
}
}
pw.println(" Dynamic AID groups:");
for (AidGroup group : mDynamicAidGroups.values()) {
- pw.println(" Category: " + group.getCategory());
+ pw.println(" Category: " + group.getCategory()
+ + "(enabled: " + mOtherServiceEnabled + ")");
for (String aid : group.getAids()) {
pw.println(" AID: " + aid);
}
@@ -796,6 +827,28 @@
pw.println(" Requires Device ScreenOn: " + mRequiresDeviceScreenOn);
}
+
+ /**
+ * Enable or disable this CATEGORY_OTHER service.
+ *
+ * @param enabled true to indicate if user has enabled this service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void setOtherServiceEnabled(boolean enabled) {
+ mOtherServiceEnabled = enabled;
+ }
+
+
+ /**
+ * Returns whether this CATEGORY_OTHER service is enabled or not.
+ *
+ * @return true to indicate if user has enabled this service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public boolean isOtherServiceEnabled() {
+ return mOtherServiceEnabled;
+ }
+
/**
* Dump debugging info as ApduServiceInfoProto.
*
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 32c2a1b..58b6179 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -16,17 +16,21 @@
package android.nfc.cardemulation;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.nfc.Constants;
+import android.nfc.Flags;
import android.nfc.INfcCardEmulation;
import android.nfc.NfcAdapter;
import android.os.RemoteException;
@@ -324,6 +328,24 @@
return SELECTION_MODE_ASK_IF_CONFLICT;
}
}
+ /**
+ * Sets whether the system should default to observe mode or not when
+ * the service is in the foreground or the default payment service.
+ *
+ * @param service The component name of the service
+ * @param enable Whether the servic should default to observe mode or not
+ * @return whether the change was successful.
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean setServiceObserveModeDefault(@NonNull ComponentName service, boolean enable) {
+ try {
+ return sService.setServiceObserveModeDefault(mContext.getUser().getIdentifier(),
+ service, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ }
+ return false;
+ }
/**
* Registers a list of AIDs for a specific category for the
@@ -877,9 +899,16 @@
}
/**
+ * Retrieves list of services registered of the provided category for the provided user.
+ *
+ * @param category Category string, one of {@link #CATEGORY_PAYMENT} or {@link #CATEGORY_OTHER}
+ * @param userId the user handle of the user whose information is being requested.
* @hide
*/
- public List<ApduServiceInfo> getServices(String category, int userId) {
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public List<ApduServiceInfo> getServices(@NonNull String category, @UserIdInt int userId) {
try {
return sService.getServices(userId, category);
} catch (RemoteException e) {
@@ -936,6 +965,39 @@
return true;
}
+ /**
+ * Allows to set or unset preferred service (category other) to avoid AID Collision.
+ *
+ * @param service The ComponentName of the service
+ * @param status true to enable, false to disable
+ * @return set service for the category and true if service is already set return false.
+ *
+ * @hide
+ */
+ public boolean setServiceEnabledForCategoryOther(ComponentName service, boolean status) {
+ if (service == null) {
+ throw new NullPointerException("activity or service or category is null");
+ }
+ int userId = mContext.getUser().getIdentifier();
+
+ try {
+ return sService.setServiceEnabledForCategoryOther(userId, service, status);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.setServiceEnabledForCategoryOther(userId, service, status);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
void recoverService() {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
sService = adapter.getCardEmulationService();
diff --git a/core/java/android/nfc/cardemulation/HostApduService.java b/core/java/android/nfc/cardemulation/HostApduService.java
index 55d0e73..7cd2533 100644
--- a/core/java/android/nfc/cardemulation/HostApduService.java
+++ b/core/java/android/nfc/cardemulation/HostApduService.java
@@ -16,11 +16,14 @@
package android.nfc.cardemulation;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -29,6 +32,9 @@
import android.os.RemoteException;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* <p>HostApduService is a convenience {@link Service} class that can be
* extended to emulate an NFC card inside an Android
@@ -230,9 +236,99 @@
/**
* @hide
*/
+ public static final int MSG_POLLING_LOOP = 4;
+
+ /**
+ * @hide
+ */
public static final String KEY_DATA = "data";
/**
+ * POLLING_LOOP_TYPE_KEY is the Bundle key for the type of
+ * polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_TYPE_KEY = "android.nfc.cardemulation.TYPE";
+
+ /**
+ * POLLING_LOOP_TYPE_A is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop is for NFC-A.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_A = 'A';
+
+ /**
+ * POLLING_LOOP_TYPE_B is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop is for NFC-B.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_B = 'B';
+
+ /**
+ * POLLING_LOOP_TYPE_F is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop is for NFC-F.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_F = 'F';
+
+ /**
+ * POLLING_LOOP_TYPE_ON is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop turns on.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_ON = 'O';
+
+ /**
+ * POLLING_LOOP_TYPE_OFF is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop turns off.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_OFF = 'X';
+
+ /**
+ * POLLING_LOOP_TYPE_UNKNOWN is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the polling loop frame isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final char POLLING_LOOP_TYPE_UNKNOWN = 'U';
+
+ /**
+ * POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
+ * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the frame type isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_DATA_KEY = "android.nfc.cardemulation.DATA";
+
+ /**
+ * POLLING_LOOP_GAIN_KEY is the Bundle key for the field strength of
+ * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the frame type isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_GAIN_KEY = "android.nfc.cardemulation.GAIN";
+
+ /**
+ * POLLING_LOOP_TIMESTAMP_KEY is the Bundle key for the timestamp of
+ * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
+ * when the frame type isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String POLLING_LOOP_TIMESTAMP_KEY = "android.nfc.cardemulation.TIMESTAMP";
+
+ /**
+ * @hide
+ */
+ public static final String POLLING_LOOP_FRAMES_BUNDLE_KEY =
+ "android.nfc.cardemulation.POLLING_FRAMES";
+
+ /**
* Messenger interface to NfcService for sending responses.
* Only accessed on main thread by the message handler.
*
@@ -255,6 +351,7 @@
byte[] apdu = dataBundle.getByteArray(KEY_DATA);
if (apdu != null) {
+ HostApduService has = HostApduService.this;
byte[] responseApdu = processCommandApdu(apdu, null);
if (responseApdu != null) {
if (mNfcService == null) {
@@ -306,6 +403,12 @@
Log.e(TAG, "RemoteException calling into NfcService.");
}
break;
+ case MSG_POLLING_LOOP:
+ ArrayList<Bundle> frames =
+ msg.getData().getParcelableArrayList(POLLING_LOOP_FRAMES_BUNDLE_KEY,
+ Bundle.class);
+ processPollingFrames(frames);
+ break;
default:
super.handleMessage(msg);
}
@@ -366,6 +469,21 @@
}
}
+ /**
+ * This method is called when a polling frame has been received from a
+ * remote device. If the device is in observe mode, the service should
+ * call {@link NfcAdapter#allowTransaction()} once it is ready to proceed
+ * with the transaction. If the device is not in observe mode, the service
+ * can use this polling frame information to determine how to proceed if it
+ * subsequently has {@link #processCommandApdu(byte[], Bundle)} called. The
+ * service must override this method inorder to receive polling frames,
+ * otherwise the base implementation drops the frame.
+ *
+ * @param frame A description of the polling frame.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public void processPollingFrames(@NonNull List<Bundle> frame) {
+ }
/**
* <p>This method will be called when a command APDU has been received
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index cd50ace..17e0427 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -20,3 +20,31 @@
description: "Flag for NFC user restriction"
bug: "291187960"
}
+
+flag {
+ name: "nfc_observe_mode"
+ namespace: "nfc"
+ description: "Enable NFC Observe Mode"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_read_polling_loop"
+ namespace: "nfc"
+ description: "Enable NFC Polling Loop Notifications"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_observe_mode_st_shim"
+ namespace: "nfc"
+ description: "Enable NFC Observe Mode ST shim"
+ bug: "294217286"
+}
+
+flag {
+ name: "nfc_read_polling_loop_st_shim"
+ namespace: "nfc"
+ description: "Enable NFC Polling Loop Notifications ST shim"
+ bug: "294217286"
+}
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 6a4ec9b..25fba60 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -21,6 +21,7 @@
import android.Manifest.permission;
import android.annotation.FlaggedApi;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -236,6 +237,7 @@
OsProtoEnums.CHARGING_POLICY_ADAPTIVE_LONGLIFE; // = 4
/** @hide */
+ @SuppressLint("UnflaggedApi") // TestApi without associated feature.
@TestApi
public static final int BATTERY_PLUGGED_ANY =
BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 509c3b8..a9b7257 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -17,6 +17,7 @@
package android.os;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -1227,6 +1228,7 @@
/**
* Vanilla Ice Cream.
*/
+ @FlaggedApi(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM)
public static final int VANILLA_ICE_CREAM = CUR_DEVELOPMENT;
}
diff --git a/core/java/android/os/DeadObjectException.java b/core/java/android/os/DeadObjectException.java
index e06b0f9..61aa222 100644
--- a/core/java/android/os/DeadObjectException.java
+++ b/core/java/android/os/DeadObjectException.java
@@ -19,7 +19,29 @@
/**
* The object you are calling has died, because its hosting process
- * no longer exists.
+ * no longer exists, or there has been a low-level binder error.
+ *
+ * If you get this exception from a system service, the error is
+ * usually nonrecoverable as the framework will restart. If you
+ * receive this error from an app, at a minimum, you should
+ * recover by resetting the connection. For instance, you should
+ * drop the binder, clean up associated state, and reset your
+ * connection to the service which through this error. In order
+ * to simplify your error recovery paths, you may also want to
+ * "simply" restart your process. However, this may not be an
+ * option if the service you are talking to is unreliable or
+ * crashes frequently.
+ *
+ * If this isn't from a service death and is instead from a
+ * low-level binder error, it will be from:
+ * - a oneway call queue filling up (too many oneway calls)
+ * - from the binder buffer being filled up, so that the transaction
+ * is rejected.
+ *
+ * In these cases, more information about the error will be
+ * logged. However, there isn't a good way to differentiate
+ * this information at runtime. So, you should handle the
+ * error, as if the service died.
*/
public class DeadObjectException extends RemoteException {
public DeadObjectException() {
diff --git a/core/java/android/os/DeadSystemRuntimeException.java b/core/java/android/os/DeadSystemRuntimeException.java
index 1e86924..3b10798 100644
--- a/core/java/android/os/DeadSystemRuntimeException.java
+++ b/core/java/android/os/DeadSystemRuntimeException.java
@@ -18,9 +18,12 @@
/**
* Exception thrown when a call into system_server resulted in a
- * DeadObjectException, meaning that the system_server has died. There's
- * nothing apps can do at this point - the system will automatically restart -
- * so there's no point in catching this.
+ * DeadObjectException, meaning that the system_server has died or
+ * experienced a low-level binder error. There's nothing apps can
+ * do at this point - the system will automatically restart - so
+ * there's no point in catching this.
+ *
+ * See {@link android.os.DeadObjectException}.
*
* @hide
*/
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index a07735e..8fcff78 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -117,8 +117,6 @@
private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
private static final String SYSTEM_ANGLE_STRING = "system";
- private static final String PROPERTY_RO_ANGLE_SUPPORTED = "ro.gfx.angle.supported";
-
private ClassLoader mClassLoader;
private String mLibrarySearchPaths;
private String mLibraryPermittedPaths;
@@ -620,8 +618,7 @@
}
/**
- * Attempt to set up ANGLE from system, if the apk can be found, pass ANGLE details to
- * the C++ GraphicsEnv class.
+ * Set up ANGLE from system.
*
* @param context - Context of the application.
* @param bundle - Bundle of the application.
@@ -630,14 +627,8 @@
* false: can not set up to use system ANGLE because it doesn't exist.
*/
private boolean setupAngleFromSystem(Context context, Bundle bundle, String packageName) {
- final boolean systemAngleSupported = SystemProperties
- .getBoolean(PROPERTY_RO_ANGLE_SUPPORTED, false);
- if (!systemAngleSupported) {
- return false;
- }
-
- // If we make it to here, system ANGLE will be used. Call nativeSetAngleInfo() with
- // the application package name and ANGLE features to use.
+ // System ANGLE always exists, call nativeSetAngleInfo() with the application package
+ // name and ANGLE features to use.
final String[] features = getAngleEglFeatures(context, bundle);
nativeSetAngleInfo(SYSTEM_ANGLE_STRING, false, packageName, features);
return true;
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 72fb4ae..cc18482 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -436,6 +437,7 @@
* @see #getData()
* @see #setData(Bundle)
*/
+ @Nullable
public Bundle peekData() {
return data;
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index d9a9266..463be37 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -83,3 +83,8 @@
# PerformanceHintManager
per-file PerformanceHintManager.java = file:/ADPF_OWNERS
+
+# IThermal interfaces
+per-file IThermal* = file:/THERMAL_OWNERS
+per-file CoolingDevice.java = file:/THERMAL_OWNERS
+per-file Temperature.java = file:/THERMAL_OWNERS
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 90a4071..d12e3b2 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2023,9 +2023,13 @@
return;
}
+ // Temporarily disable checks so that explicit GC is allowed.
+ final int oldMask = getThreadPolicyMask();
+ setThreadPolicyMask(0);
System.gc();
System.runFinalization();
System.gc();
+ setThreadPolicyMask(oldMask);
// Note: classInstanceLimit is immutable, so this is lock-free
// Create the classes array.
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index cc54266..5c4aa4a 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -52,8 +52,20 @@
],
"name": "FrameworksServicesTests",
"options": [
- { "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
- { "include-filter": "com.android.server.power.stats.BatteryStatsTests" }
+ { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }
+ ]
+ },
+ {
+ "file_patterns": [
+ "BatteryStats[^/]*\\.java",
+ "BatteryUsageStats[^/]*\\.java",
+ "PowerComponents\\.java",
+ "[^/]*BatteryConsumer[^/]*\\.java"
+ ],
+ "name": "FrameworksServicesTests",
+ "options": [
+ { "include-filter": "com.android.server.power.stats" },
+ { "exclude-filter": "com.android.server.power.stats.BatteryStatsTests" }
]
},
{
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 77229c4..4031153 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -1,6 +1,13 @@
package: "android.os"
flag {
+ name: "android_os_build_vanilla_ice_cream"
+ namespace: "build"
+ description: "Feature flag for adding the VANILLA_ICE_CREAM constant."
+ bug: "264658905"
+}
+
+flag {
name: "state_of_health_public"
namespace: "system_sw_battery"
description: "Feature flag for making state_of_health a public api."
diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS
index e5b76f6..bf22dcc 100644
--- a/core/java/android/os/storage/OWNERS
+++ b/core/java/android/os/storage/OWNERS
@@ -4,14 +4,16 @@
# Android Storage Team
alukin@google.com
-corinac@google.com
+ankitavyas@google.com
dipankarb@google.com
+gargshivam@google.com
krishang@google.com
+riyaghai@google.com
sahanas@google.com
sergeynv@google.com
+shikhamalhotra@google.com
shubhisaxena@google.com
tylersaunders@google.com
maco@google.com
nandana@google.com
-narayan@google.com
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 8e72e13..fa42fed 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1729,23 +1729,6 @@
return RoSystemProperties.CRYPTO_FILE_ENCRYPTED;
}
- /** {@hide}
- * @deprecated Use {@link #isFileEncrypted} instead, since emulated FBE is no longer supported.
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @Deprecated
- public static boolean isFileEncryptedNativeOnly() {
- return isFileEncrypted();
- }
-
- /** {@hide}
- * @deprecated Use {@link #isFileEncrypted} instead, since emulated FBE is no longer supported.
- */
- @Deprecated
- public static boolean isFileEncryptedNativeOrEmulated() {
- return isFileEncrypted();
- }
-
/** {@hide} */
public static boolean hasAdoptable() {
switch (SystemProperties.get(PROP_ADOPTABLE)) {
diff --git a/core/java/android/preference/OWNERS b/core/java/android/preference/OWNERS
index 827134e..b4cb9ec 100644
--- a/core/java/android/preference/OWNERS
+++ b/core/java/android/preference/OWNERS
@@ -1,3 +1,5 @@
lpf@google.com
pavlis@google.com
clarabayarri@google.com
+
+per-file SeekBarVolumizer.java = jmtrivi@google.com
\ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2f86efe..e516956 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3445,7 +3445,6 @@
public ArrayMap<String, String> getStringsForPrefix(ContentResolver cr, String prefix,
List<String> names) {
String namespace = prefix.substring(0, prefix.length() - 1);
- Config.enforceReadPermission(namespace);
ArrayMap<String, String> keyValues = new ArrayMap<>();
int currentGeneration = -1;
boolean needsGenerationTracker = false;
@@ -19288,21 +19287,6 @@
.getApplicationContext().checkCallingOrSelfPermission(permission);
}
- /**
- * Enforces READ_DEVICE_CONFIG permission if namespace is not one of public namespaces.
- * @hide
- */
- public static void enforceReadPermission(String namespace) {
- if (ActivityThread.currentApplication().getApplicationContext()
- .checkCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG)
- != PackageManager.PERMISSION_GRANTED) {
- if (!DeviceConfig.getPublicNamespaces().contains(namespace)) {
- throw new SecurityException("Permission denial: reading from settings requires:"
- + Manifest.permission.READ_DEVICE_CONFIG);
- }
- }
- }
-
private static void setMonitorCallbackAsUser(
@NonNull @CallbackExecutor Executor executor,
@NonNull ContentResolver resolver, @UserIdInt int userHandle,
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 96c0be7..533d459 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -8,3 +8,4 @@
per-file Confirmation*.java = file:/keystore/OWNERS
per-file FileIntegrityManager.java = file:platform/system/security:/fsverity/OWNERS
per-file IFileIntegrityService.aidl = file:platform/system/security:/fsverity/OWNERS
+per-file *.aconfig = victorhsieh@google.com,eranm@google.com
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 5aa3097..13612f6 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -8,7 +8,14 @@
}
flag {
- name: "fix_unlocked_device_required_keys"
+ name: "mgf1_digest_setter"
+ namespace: "hardware_backed_security"
+ description: "Feature flag for mgf1 digest setter in key generation and import parameters."
+ bug: "308378912"
+}
+
+flag {
+ name: "fix_unlocked_device_required_keys_v2"
namespace: "hardware_backed_security"
description: "Fix bugs in behavior of UnlockedDeviceRequired keystore keys"
bug: "296464083"
diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS
index bb0e6ab..cb0b5fa 100644
--- a/core/java/android/service/notification/OWNERS
+++ b/core/java/android/service/notification/OWNERS
@@ -2,6 +2,7 @@
juliacr@google.com
yurilin@google.com
+matiashe@google.com
jeffdq@google.com
dsandler@android.com
dsandler@google.com
diff --git a/core/java/android/tracing/OWNERS b/core/java/android/tracing/OWNERS
index 079d4c5..2ebe2e9 100644
--- a/core/java/android/tracing/OWNERS
+++ b/core/java/android/tracing/OWNERS
@@ -1,3 +1,6 @@
carmenjackson@google.com
kevinjeon@google.com
+pablogamito@google.com
+natanieljr@google.com
+keanmariotti@google.com
include platform/external/perfetto:/OWNERS
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 42ac74c..ad326e4 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -11,6 +11,7 @@
roosa@google.com
jreck@google.com
siyamed@google.com
+mount@google.com
# Autofill
per-file ViewStructure.java = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 03a6a00..73d18f9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -22444,6 +22444,22 @@
}
/**
+ * Configure the {@link android.graphics.RenderEffect} to apply to the backdrop contents of this
+ * View. This will apply a visual effect to the result of the backdrop contents of this View
+ * before it is drawn. For example if
+ * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
+ * is provided, the previous content behind this View will be blurred before this View is drawn.
+ * @param renderEffect to be applied to the View. Passing null clears the previously configured
+ * {@link RenderEffect}
+ * @hide
+ */
+ public void setBackdropRenderEffect(@Nullable RenderEffect renderEffect) {
+ if (mRenderNode.setBackdropRenderEffect(renderEffect)) {
+ invalidateViewProperty(true, true);
+ }
+ }
+
+ /**
* Updates the {@link Paint} object used with the current layer (used only if the current
* layer type is not set to {@link #LAYER_TYPE_NONE}). Changed properties of the Paint
* provided to {@link #setLayerType(int, android.graphics.Paint)} will be used the next time
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 3160057..14c5348 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1367,7 +1367,10 @@
* the system default value will be used.
*
* <p>If the user-agent is overridden in this way, the values of the User-Agent Client Hints
- * headers and {@code navigator.userAgentData} for this WebView will be empty.
+ * headers and {@code navigator.userAgentData} for this WebView could be changed.
+ * <p> See <a href="{@docRoot}reference/androidx/webkit/WebSettingsCompat
+ * #setUserAgentMetadata(WebSettings,UserAgentMetadata)">androidx.webkit.WebSettingsCompat
+ * #setUserAgentMetadata(WebSettings,UserAgentMetadata)</a> for details.
*
* <p>Note that starting from {@link android.os.Build.VERSION_CODES#KITKAT} Android
* version, changing the user-agent while loading a web page causes WebView
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 7f0a651..e20357fa 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -12,6 +12,6 @@
per-file SpellChecker.java = file:../view/inputmethod/OWNERS
-per-file RemoteViews* = file:../appwidget/OWNERS
+per-file Remote* = file:../appwidget/OWNERS
per-file Toast.java = juliacr@google.com, jeffdq@google.com
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index 25bf85c..ec79219 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -21,11 +21,13 @@
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.IBinder;
import android.os.InputConfig;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
+import android.util.SparseArray;
import android.view.InputWindowHandle;
import java.util.ArrayList;
@@ -119,13 +121,13 @@
"Exception thrown while waiting for listener to be called with "
+ "initial state");
}
- consumer.accept(buildWindowInfos(windowHandles));
+ consumer.accept(buildWindowInfos(windowHandles, displayInfos));
}
};
mListeners.put(consumer, listener);
Pair<InputWindowHandle[], WindowInfosListener.DisplayInfo[]> initialState =
listener.register();
- consumer.accept(buildWindowInfos(initialState.first));
+ consumer.accept(buildWindowInfos(initialState.first, initialState.second));
calledWithInitialState.countDown();
}
@@ -140,11 +142,28 @@
listener.unregister();
}
- private static List<WindowInfo> buildWindowInfos(InputWindowHandle[] windowHandles) {
+ private static List<WindowInfo> buildWindowInfos(
+ InputWindowHandle[] windowHandles, WindowInfosListener.DisplayInfo[] displayInfos) {
var windowInfos = new ArrayList<WindowInfo>(windowHandles.length);
+
+ var displayInfoById = new SparseArray<WindowInfosListener.DisplayInfo>(displayInfos.length);
+ for (var displayInfo : displayInfos) {
+ displayInfoById.put(displayInfo.mDisplayId, displayInfo);
+ }
+
+ var tmp = new RectF();
for (var handle : windowHandles) {
var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight,
handle.frameBottom);
+
+ // Transform bounds from physical display coordinates to logical display coordinates.
+ var display = displayInfoById.get(handle.displayId);
+ if (display != null) {
+ tmp.set(bounds);
+ display.mTransform.mapRect(tmp);
+ tmp.round(bounds);
+ }
+
windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, handle.displayId,
bounds, handle.inputConfig));
}
diff --git a/core/java/android/window/flags/OWNERS b/core/java/android/window/flags/OWNERS
new file mode 100644
index 0000000..fa81ee3
--- /dev/null
+++ b/core/java/android/window/flags/OWNERS
@@ -0,0 +1 @@
+per-file responsible_apis.aconfig = file:/BAL_OWNERS
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
index a5e775a..389576d 100644
--- a/core/java/com/android/internal/app/SuspendedAppActivity.java
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -80,7 +80,8 @@
// Suspension conditions were modified, dismiss any related visible dialogs.
final String[] modified = intent.getStringArrayExtra(
Intent.EXTRA_CHANGED_PACKAGE_LIST);
- if (ArrayUtils.contains(modified, mSuspendedPackage)) {
+ if (ArrayUtils.contains(modified, mSuspendedPackage)
+ && !isPackageSuspended(mSuspendedPackage)) {
if (!isFinishing()) {
Slog.w(TAG, "Package " + mSuspendedPackage + " has modified"
+ " suspension conditions while dialog was visible. Finishing.");
@@ -92,6 +93,15 @@
}
};
+ private boolean isPackageSuspended(String packageName) {
+ try {
+ return mPm.isPackageSuspended(packageName);
+ } catch (PackageManager.NameNotFoundException ne) {
+ Slog.e(TAG, "Package " + packageName + " not found", ne);
+ }
+ return false;
+ }
+
private CharSequence getAppLabel(String packageName) {
try {
return mPm.getApplicationInfoAsUser(packageName, 0, mUserId).loadLabel(mPm);
diff --git a/core/java/com/android/internal/net/TEST_MAPPING b/core/java/com/android/internal/net/TEST_MAPPING
new file mode 100644
index 0000000..971ad36
--- /dev/null
+++ b/core/java/com/android/internal/net/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksNetTests",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 0947ec1..f62094d 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -618,4 +618,14 @@
public int describeContents() {
return 0;
}
+
+ @Override
+ public VpnProfile clone() {
+ try {
+ return (VpnProfile) super.clone();
+ } catch (CloneNotSupportedException e) {
+ Log.wtf(TAG, e);
+ return null;
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index 70514c3..01c91ba 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -337,6 +337,12 @@
@UnsupportedAppUsage
public void update() {
+ synchronized (this) {
+ updateLocked();
+ }
+ }
+
+ private void updateLocked() {
if (DEBUG) Slog.v(TAG, "Update: " + this);
final long nowUptime = SystemClock.uptimeMillis();
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 965277c..1c5f4f0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -868,6 +868,11 @@
args.mPkgDataInfoList, args.mAllowlistedDataInfoList,
args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
+ // While `specializeAppProcess` sets the thread name on the process's main thread, this
+ // is distinct from the app process name which appears in stack traces, as the latter is
+ // sourced from the argument buffer of the Process class. Set the app process name here.
+ Zygote.setAppProcessName(args, TAG);
+
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 993e4e7..5fe086d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -296,7 +296,6 @@
} else {
// child; result is a Runnable.
zygoteServer.setForkChild();
- Zygote.setAppProcessName(parsedArgs, TAG); // ??? Necessary?
return result;
}
}
diff --git a/core/java/com/android/internal/pm/OWNERS b/core/java/com/android/internal/pm/OWNERS
new file mode 100644
index 0000000..6ef34e2
--- /dev/null
+++ b/core/java/com/android/internal/pm/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36137
+
+file:/PACKAGE_MANAGER_OWNERS
+
diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java
deleted file mode 100644
index 929c9e8..0000000
--- a/core/java/com/android/internal/util/FastXmlSerializer.java
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import android.compat.annotation.UnsupportedAppUsage;
-
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
-import java.nio.charset.CodingErrorAction;
-import java.nio.charset.IllegalCharsetNameException;
-import java.nio.charset.UnsupportedCharsetException;
-
-/**
- * This is a quick and dirty implementation of XmlSerializer that isn't horribly
- * painfully slow like the normal one. It only does what is needed for the
- * specific XML files being written with it.
- */
-public class FastXmlSerializer implements XmlSerializer {
- private static final String ESCAPE_TABLE[] = new String[] {
- "�", "", "", "", "", "", "", "", // 0-7
- "", "	", " ", "", "", " ", "", "", // 8-15
- "", "", "", "", "", "", "", "", // 16-23
- "", "", "", "", "", "", "", "", // 24-31
- null, null, """, null, null, null, "&", null, // 32-39
- null, null, null, null, null, null, null, null, // 40-47
- null, null, null, null, null, null, null, null, // 48-55
- null, null, null, null, "<", null, ">", null, // 56-63
- };
-
- private static final int DEFAULT_BUFFER_LEN = 32*1024;
-
- private static String sSpace = " ";
-
- private final int mBufferLen;
- private final char[] mText;
- private int mPos;
-
- private Writer mWriter;
-
- private OutputStream mOutputStream;
- private CharsetEncoder mCharset;
- private ByteBuffer mBytes;
-
- private boolean mIndent = false;
- private boolean mInTag;
-
- private int mNesting = 0;
- private boolean mLineStart = true;
-
- @UnsupportedAppUsage
- public FastXmlSerializer() {
- this(DEFAULT_BUFFER_LEN);
- }
-
- /**
- * Allocate a FastXmlSerializer with the given internal output buffer size. If the
- * size is zero or negative, then the default buffer size will be used.
- *
- * @param bufferSize Size in bytes of the in-memory output buffer that the writer will use.
- */
- public FastXmlSerializer(int bufferSize) {
- mBufferLen = (bufferSize > 0) ? bufferSize : DEFAULT_BUFFER_LEN;
- mText = new char[mBufferLen];
- mBytes = ByteBuffer.allocate(mBufferLen);
- }
-
- private void append(char c) throws IOException {
- int pos = mPos;
- if (pos >= (mBufferLen-1)) {
- flush();
- pos = mPos;
- }
- mText[pos] = c;
- mPos = pos+1;
- }
-
- private void append(String str, int i, final int length) throws IOException {
- if (length > mBufferLen) {
- final int end = i + length;
- while (i < end) {
- int next = i + mBufferLen;
- append(str, i, next<end ? mBufferLen : (end-i));
- i = next;
- }
- return;
- }
- int pos = mPos;
- if ((pos+length) > mBufferLen) {
- flush();
- pos = mPos;
- }
- str.getChars(i, i+length, mText, pos);
- mPos = pos + length;
- }
-
- private void append(char[] buf, int i, final int length) throws IOException {
- if (length > mBufferLen) {
- final int end = i + length;
- while (i < end) {
- int next = i + mBufferLen;
- append(buf, i, next<end ? mBufferLen : (end-i));
- i = next;
- }
- return;
- }
- int pos = mPos;
- if ((pos+length) > mBufferLen) {
- flush();
- pos = mPos;
- }
- System.arraycopy(buf, i, mText, pos, length);
- mPos = pos + length;
- }
-
- private void append(String str) throws IOException {
- append(str, 0, str.length());
- }
-
- private void appendIndent(int indent) throws IOException {
- indent *= 4;
- if (indent > sSpace.length()) {
- indent = sSpace.length();
- }
- append(sSpace, 0, indent);
- }
-
- private void escapeAndAppendString(final String string) throws IOException {
- final int N = string.length();
- final char NE = (char)ESCAPE_TABLE.length;
- final String[] escapes = ESCAPE_TABLE;
- int lastPos = 0;
- int pos;
- for (pos=0; pos<N; pos++) {
- char c = string.charAt(pos);
- if (c >= NE) continue;
- String escape = escapes[c];
- if (escape == null) continue;
- if (lastPos < pos) append(string, lastPos, pos-lastPos);
- lastPos = pos + 1;
- append(escape);
- }
- if (lastPos < pos) append(string, lastPos, pos-lastPos);
- }
-
- private void escapeAndAppendString(char[] buf, int start, int len) throws IOException {
- final char NE = (char)ESCAPE_TABLE.length;
- final String[] escapes = ESCAPE_TABLE;
- int end = start+len;
- int lastPos = start;
- int pos;
- for (pos=start; pos<end; pos++) {
- char c = buf[pos];
- if (c >= NE) continue;
- String escape = escapes[c];
- if (escape == null) continue;
- if (lastPos < pos) append(buf, lastPos, pos-lastPos);
- lastPos = pos + 1;
- append(escape);
- }
- if (lastPos < pos) append(buf, lastPos, pos-lastPos);
- }
-
- public XmlSerializer attribute(String namespace, String name, String value) throws IOException,
- IllegalArgumentException, IllegalStateException {
- append(' ');
- if (namespace != null) {
- append(namespace);
- append(':');
- }
- append(name);
- append("=\"");
-
- escapeAndAppendString(value);
- append('"');
- mLineStart = false;
- return this;
- }
-
- public void cdsect(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void comment(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void docdecl(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException {
- flush();
- }
-
- public XmlSerializer endTag(String namespace, String name) throws IOException,
- IllegalArgumentException, IllegalStateException {
- mNesting--;
- if (mInTag) {
- append(" />\n");
- } else {
- if (mIndent && mLineStart) {
- appendIndent(mNesting);
- }
- append("</");
- if (namespace != null) {
- append(namespace);
- append(':');
- }
- append(name);
- append(">\n");
- }
- mLineStart = true;
- mInTag = false;
- return this;
- }
-
- public void entityRef(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- private void flushBytes() throws IOException {
- int position;
- if ((position = mBytes.position()) > 0) {
- mBytes.flip();
- mOutputStream.write(mBytes.array(), 0, position);
- mBytes.clear();
- }
- }
-
- public void flush() throws IOException {
- //Log.i("PackageManager", "flush mPos=" + mPos);
- if (mPos > 0) {
- if (mOutputStream != null) {
- CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
- CoderResult result = mCharset.encode(charBuffer, mBytes, true);
- while (true) {
- if (result.isError()) {
- throw new IOException(result.toString());
- } else if (result.isOverflow()) {
- flushBytes();
- result = mCharset.encode(charBuffer, mBytes, true);
- continue;
- }
- break;
- }
- flushBytes();
- mOutputStream.flush();
- } else {
- mWriter.write(mText, 0, mPos);
- mWriter.flush();
- }
- mPos = 0;
- }
- }
-
- public int getDepth() {
- throw new UnsupportedOperationException();
- }
-
- public boolean getFeature(String name) {
- throw new UnsupportedOperationException();
- }
-
- public String getName() {
- throw new UnsupportedOperationException();
- }
-
- public String getNamespace() {
- throw new UnsupportedOperationException();
- }
-
- public String getPrefix(String namespace, boolean generatePrefix)
- throws IllegalArgumentException {
- throw new UnsupportedOperationException();
- }
-
- public Object getProperty(String name) {
- throw new UnsupportedOperationException();
- }
-
- public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void processingInstruction(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void setFeature(String name, boolean state) throws IllegalArgumentException,
- IllegalStateException {
- if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) {
- mIndent = true;
- return;
- }
- throw new UnsupportedOperationException();
- }
-
- public void setOutput(OutputStream os, String encoding) throws IOException,
- IllegalArgumentException, IllegalStateException {
- if (os == null)
- throw new IllegalArgumentException();
- if (true) {
- try {
- mCharset = Charset.forName(encoding).newEncoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
- } catch (IllegalCharsetNameException e) {
- throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
- encoding).initCause(e));
- } catch (UnsupportedCharsetException e) {
- throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
- encoding).initCause(e));
- }
- mOutputStream = os;
- } else {
- setOutput(
- encoding == null
- ? new OutputStreamWriter(os)
- : new OutputStreamWriter(os, encoding));
- }
- }
-
- public void setOutput(Writer writer) throws IOException, IllegalArgumentException,
- IllegalStateException {
- mWriter = writer;
- }
-
- public void setPrefix(String prefix, String namespace) throws IOException,
- IllegalArgumentException, IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void setProperty(String name, Object value) throws IllegalArgumentException,
- IllegalStateException {
- throw new UnsupportedOperationException();
- }
-
- public void startDocument(String encoding, Boolean standalone) throws IOException,
- IllegalArgumentException, IllegalStateException {
- append("<?xml version='1.0' encoding='utf-8'");
- if (standalone != null) {
- append(" standalone='" + (standalone ? "yes" : "no") + "'");
- }
- append(" ?>\n");
- mLineStart = true;
- }
-
- public XmlSerializer startTag(String namespace, String name) throws IOException,
- IllegalArgumentException, IllegalStateException {
- if (mInTag) {
- append(">\n");
- }
- if (mIndent) {
- appendIndent(mNesting);
- }
- mNesting++;
- append('<');
- if (namespace != null) {
- append(namespace);
- append(':');
- }
- append(name);
- mInTag = true;
- mLineStart = false;
- return this;
- }
-
- public XmlSerializer text(char[] buf, int start, int len) throws IOException,
- IllegalArgumentException, IllegalStateException {
- if (mInTag) {
- append(">");
- mInTag = false;
- }
- escapeAndAppendString(buf, start, len);
- if (mIndent) {
- mLineStart = buf[start+len-1] == '\n';
- }
- return this;
- }
-
- public XmlSerializer text(String text) throws IOException, IllegalArgumentException,
- IllegalStateException {
- if (mInTag) {
- append(">");
- mInTag = false;
- }
- escapeAndAppendString(text);
- if (mIndent) {
- mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n');
- }
- return this;
- }
-
-}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 8d11672..28fd2b4 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1105,10 +1105,9 @@
@UnsupportedAppUsage
public long setLockoutAttemptDeadline(int userId, int timeoutMs) {
final long deadline = SystemClock.elapsedRealtime() + timeoutMs;
- if (isSpecialUserId(userId)) {
- // For secure password storage (that is required for special users such as FRP), the
- // underlying storage also enforces the deadline. Since we cannot store settings
- // for special users, don't.
+ if (userId == USER_FRP) {
+ // For secure password storage (that is required for FRP), the underlying storage also
+ // enforces the deadline. Since we cannot store settings for the FRP user, don't.
return deadline;
}
mLockoutDeadlines.put(userId, deadline);
@@ -1937,7 +1936,8 @@
* If the user is not secured, ie doesn't have an LSKF, then decrypt the user's synthetic
* password and use it to unlock various cryptographic keys associated with the user. This
* primarily includes unlocking the user's credential-encrypted (CE) storage. It also includes
- * deriving or decrypting the vendor auth secret and sending it to the AuthSecret HAL.
+ * unlocking the user's Keystore super keys, and deriving or decrypting the vendor auth secret
+ * and sending it to the AuthSecret HAL in order to unlock Secure Element firmware updates.
* <p>
* These tasks would normally be done when the LSKF is verified. This method is where these
* tasks are done when the user doesn't have an LSKF. It's called when the user is started.
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index 139b88b..61e017d 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -64,7 +64,7 @@
}
@Override
- public void interfaceClassDataActivityChanged(int transportType, boolean active, long tsNanos,
+ public void interfaceClassDataActivityChanged(int label, boolean active, long tsNanos,
int uid) {
// default no-op
}
diff --git a/core/java/com/android/server/pm/OWNERS b/core/java/com/android/server/pm/OWNERS
new file mode 100644
index 0000000..6ef34e2
--- /dev/null
+++ b/core/java/com/android/server/pm/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36137
+
+file:/PACKAGE_MANAGER_OWNERS
+
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index cc2d2e2..05117ce 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -457,9 +457,4 @@
],
},
},
-
- // Workaround Clang LTO crash.
- lto: {
- never: true,
- },
}
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index b1dab85..f9d00ed 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -212,14 +212,11 @@
return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
}
- size_t bytesPerSample = audio_bytes_per_sample(format);
-
if (buffSizeInBytes == 0) {
ALOGE("Error creating AudioRecord: frameCount is 0.");
return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
}
- size_t frameSize = channelCount * bytesPerSample;
- size_t frameCount = buffSizeInBytes / frameSize;
+ size_t frameCount = buffSizeInBytes / audio_bytes_per_frame(channelCount, format);
// create an uninitialized AudioRecord object
Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
@@ -574,7 +571,7 @@
if (result != NO_ERROR) {
return -1;
}
- return frameCount * channelCount * audio_bytes_per_sample(format);
+ return frameCount * audio_bytes_per_frame(channelCount, format);
}
static jboolean android_media_AudioRecord_setInputDevice(
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 18c60a7..91dfc60 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1245,7 +1245,7 @@
void android_os_Process_removeAllProcessGroups(JNIEnv* env, jobject clazz)
{
- return removeAllProcessGroups();
+ return removeAllEmptyProcessGroups();
}
static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, jint flags) {
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 0f41229..efdfce9 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -184,11 +184,11 @@
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
- int fd = mInputConsumer.getChannel()->getFd();
+ auto&& fd = mInputConsumer.getChannel()->getFd();
if (events) {
- mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
+ mMessageQueue->getLooper()->addFd(fd.get(), 0, events, this, nullptr);
} else {
- mMessageQueue->getLooper()->removeFd(fd);
+ mMessageQueue->getLooper()->removeFd(fd.get());
}
}
}
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 8d39ddf..694b28b 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -101,8 +101,8 @@
}
status_t NativeInputEventSender::initialize() {
- int receiveFd = mInputPublisher.getChannel()->getFd();
- mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
+ auto&& receiveFd = mInputPublisher.getChannel()->getFd();
+ mMessageQueue->getLooper()->addFd(receiveFd.get(), 0, ALOOPER_EVENT_INPUT, this, NULL);
return OK;
}
@@ -111,7 +111,7 @@
ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName().c_str());
}
- mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd());
+ mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd().get());
}
status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
diff --git a/core/res/Android.bp b/core/res/Android.bp
index b71995f..6fa70d8 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -151,6 +151,8 @@
"simulated_device_launcher",
],
},
+
+ generate_product_characteristics_rro: true,
}
java_genrule {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7d9d991..f7b6a7b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2225,6 +2225,13 @@
<permission android:name="android.permission.MANAGE_LOWPAN_INTERFACES"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows changing Thread network state and access to Thread network
+ credentials such as Network Key and PSKc.
+ <p>Not for use by third-party applications.
+ @FlaggedApi("com.android.net.thread.flags.thread_enabled") -->
+ <permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED"
+ android:protectionLevel="signature|privileged" />
+
<!-- #SystemApi @hide Allows an app to bypass Private DNS.
<p>Not for use by third-party applications.
TODO: publish as system API in next API release. -->
@@ -2271,6 +2278,7 @@
<!-- Allows system apps to call methods to register itself as a mDNS offload engine.
<p>Not for use by third-party or privileged applications.
@SystemApi
+ @FlaggedApi("com.android.net.flags.register_nsd_offload_engine")
@hide This should only be used by system apps.
-->
<permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE"
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 0df7c20..f24c3f5 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -1,5 +1,6 @@
adamp@google.com
asc@google.com
+austindelgado@google.com
cinek@google.com
dsandler@android.com
dsandler@google.com
@@ -8,6 +9,7 @@
hackbod@google.com
ilyamaty@google.com
jaggies@google.com
+jbolinger@google.com
jsharkey@android.com
jsharkey@google.com
juliacr@google.com
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6f7bc53..3413282 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4303,6 +4303,9 @@
<!-- Whether the device must be screen on before routing data to this service.
The default is true.-->
<attr name="requireDeviceScreenOn" format="boolean"/>
+ <!-- Whether the device should default to observe mode when this service is
+ default or in the foreground. -->
+ <attr name="defaultToObserveMode" format="boolean"/>
</declare-styleable>
<!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that
@@ -4327,6 +4330,9 @@
<!-- Whether the device must be screen on before routing data to this service.
The default is false.-->
<attr name="requireDeviceScreenOn"/>
+ <!-- Whether the device should default to observe mode when this service is
+ default or in the foreground. -->
+ <attr name="defaultToObserveMode"/>
</declare-styleable>
<!-- Specify one or more <code>aid-group</code> elements inside a
diff --git a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
index fac3a0e..d34c91e 100644
--- a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
+++ b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
@@ -86,16 +86,6 @@
GameModeInfo gameModeInfo = mGameManager.getGameModeInfo(mPackageName);
assertNotNull(gameModeInfo);
assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM));
- GameModeConfiguration unsupportedFpsConfig =
- new GameModeConfiguration.Builder().setFpsOverride(
- 70).setScalingFactor(0.5f).build();
- mGameManager.updateCustomGameModeConfiguration(mPackageName, unsupportedFpsConfig);
- gameModeInfo = mGameManager.getGameModeInfo(mPackageName);
- assertNotNull(gameModeInfo);
- // TODO(b/243448953): update to non-zero FPS when matching is implemented
- assertEquals(new GameModeConfiguration.Builder().setFpsOverride(
- GameModeConfiguration.FPS_OVERRIDE_NONE).setScalingFactor(0.5f).build(),
- gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM));
GameModeConfiguration supportedFpsConfig =
new GameModeConfiguration.Builder().setFpsOverride(
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index 64f5e6c..5636f9b 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -10,3 +10,7 @@
# KeyguardManagerTest
per-file KeyguardManagerTest.java = file:/services/core/java/com/android/server/locksettings/OWNERS
+
+# Files related to background activity launches
+per-file Background*Start* = file:/BAL_OWNERS
+
diff --git a/core/tests/coretests/src/android/content/BrickDeniedTest.java b/core/tests/coretests/src/android/content/BrickDeniedTest.java
deleted file mode 100644
index d8c9baa..0000000
--- a/core/tests/coretests/src/android/content/BrickDeniedTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.test.AndroidTestCase;
-
-import androidx.test.filters.SmallTest;
-
-/** Test to make sure brick intents <b>don't</b> work without permission. */
-public class BrickDeniedTest extends AndroidTestCase {
- @SmallTest
- public void testBrick() {
- // Try both the old and new brick intent names. Neither should work,
- // since this test application doesn't have the required permission.
- // If it does work, well, the test certainly won't pass.
- getContext().sendBroadcast(new Intent("SHES_A_BRICK_HOUSE"));
- getContext().sendBroadcast(new Intent("android.intent.action.BRICK"));
- }
-}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp
index d0645b0..23a2a7fb 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp
@@ -28,7 +28,7 @@
srcs: ["src/**/*.java"],
- sdk_version: "16",
+ sdk_version: "19",
javacflags: ["-nowarn"],
@@ -43,5 +43,5 @@
enabled: false,
},
- min_sdk_version: "16",
+ min_sdk_version: "19",
}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp
index c0c8aba..4ddaeef 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp
@@ -40,7 +40,7 @@
enabled: false,
},
- min_sdk_version: "16",
+ min_sdk_version: "19",
}
android_test_helper_app {
@@ -66,5 +66,5 @@
enabled: false,
},
- min_sdk_version: "16",
+ min_sdk_version: "19",
}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp
index fe29416..afb111d 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp
@@ -28,7 +28,7 @@
javacflags: ["-nowarn"],
- min_sdk_version: "16",
+ min_sdk_version: "19",
instrumentation_for: "MultiDexLegacyTestApp",
}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp
index c558153..8c57df6 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp
@@ -31,7 +31,7 @@
javacflags: ["-nowarn"],
- min_sdk_version: "16",
+ min_sdk_version: "19",
instrumentation_for: "MultiDexLegacyTestApp",
}
@@ -51,7 +51,7 @@
javacflags: ["-nowarn"],
- min_sdk_version: "16",
+ min_sdk_version: "19",
instrumentation_for: "MultiDexLegacyTestApp",
}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
index 9a4c3c5..840daab 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/AndroidManifest.xml
@@ -5,8 +5,8 @@
android:versionCode="1"
android:versionName="1.0">
- <uses-sdk android:minSdkVersion="18"
- android:targetSdkVersion="18"/>
+ <uses-sdk android:minSdkVersion="19"
+ android:targetSdkVersion="19"/>
<application android:name="androidx.multidex.MultiDexApplication"
android:allowBackup="true"
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp
index 75c753c..2244a83 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp
@@ -24,5 +24,5 @@
libs: ["android-support-multidex"],
static_libs: ["androidx.test.rules"],
- sdk_version: "16",
+ sdk_version: "19",
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index b05507e..c1018f5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -438,6 +438,8 @@
<permission name="android.permission.MANAGE_WIFI_NETWORK_SELECTION" />
<!-- Permission needed for CTS test - ConcurrencyTest#testP2pSetWfdInfo -->
<permission name="android.permission.CONFIGURE_WIFI_DISPLAY" />
+ <!-- Permission required for CTS test - CtsThreadNetworkTestCases -->
+ <permission name="android.permission.THREAD_NETWORK_PRIVILEGED"/>
<!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
<!-- Permission required for CTS test - MusicRecognitionManagerTest -->
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
index 07f1d4a..8dc9579 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java
@@ -63,7 +63,7 @@
@Override
public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) {
- final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree);
+ final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree, state);
final String sourceCode = state.getSourceCode().toString();
for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) {
for (Tokens.Comment comment : token.comments()) {
@@ -112,9 +112,9 @@
}
- private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree) {
+ private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree, VisitorState state) {
Map<Integer, Tree> javadoccableTrees = new HashMap<>();
- new SuppressibleTreePathScanner<Void, Void>() {
+ new SuppressibleTreePathScanner<Void, Void>(state) {
@Override
public Void visitClass(ClassTree classTree, Void unused) {
javadoccableTrees.put(getStartPosition(classTree), classTree);
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 2ec4524..d659ddd 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -402,8 +402,8 @@
}
@Override
- public final void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
- @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+ public final void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,
+ @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) {
nDrawDoubleRoundRect(mNativeCanvasWrapper,
outer.left, outer.top, outer.right, outer.bottom, outerRadii,
inner.left, inner.top, inner.right, inner.bottom, innerRadii,
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 2e91c24..15d26eb 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -971,6 +971,23 @@
}
/**
+ * Configure the {@link android.graphics.RenderEffect} to apply to the backdrop contents of
+ * this RenderNode. This will apply a visual effect to the result of the backdrop contents
+ * of this RenderNode before the RenderNode is drawn into the destination. For example if
+ * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
+ * is provided, the previous content behind this RenderNode will be blurred before the
+ * RenderNode is drawn in to the destination.
+ * @param renderEffect to be applied to the backdrop contents of this RenderNode. Passing
+ * null clears all previously configured RenderEffects
+ * @return True if the value changed, false if the new value was the same as the previous value.
+ * @hide
+ */
+ public boolean setBackdropRenderEffect(@Nullable RenderEffect renderEffect) {
+ return nSetBackdropRenderEffect(mNativeRenderNode,
+ renderEffect != null ? renderEffect.getNativeInstance() : 0);
+ }
+
+ /**
* Returns the translucency level of this display list.
*
* @return A value between 0.0f and 1.0f
@@ -1797,6 +1814,9 @@
private static native boolean nSetRenderEffect(long renderNode, long renderEffect);
@CriticalNative
+ private static native boolean nSetBackdropRenderEffect(long renderNode, long renderEffect);
+
+ @CriticalNative
private static native boolean nSetHasOverlappingRendering(long renderNode,
boolean hasOverlappingRendering);
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index b7ea04f..2beb434 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -51,7 +51,7 @@
* @return 0 if successful or a {@code ResponseCode}
* @hide
*/
- public static int onUserAdded(@NonNull int userId) {
+ public static int onUserAdded(int userId) {
StrictMode.noteDiskWrite();
try {
getService().onUserAdded(userId);
@@ -66,6 +66,30 @@
}
/**
+ * Tells Keystore to create a user's super keys and store them encrypted by the given secret.
+ *
+ * @param userId - Android user id of the user
+ * @param password - a secret derived from the user's synthetic password
+ * @param allowExisting - true if the keys already existing should not be considered an error
+ * @return 0 if successful or a {@code ResponseCode}
+ * @hide
+ */
+ public static int initUserSuperKeys(int userId, @NonNull byte[] password,
+ boolean allowExisting) {
+ StrictMode.noteDiskWrite();
+ try {
+ getService().initUserSuperKeys(userId, password, allowExisting);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "initUserSuperKeys failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
+
+ /**
* Informs Keystore 2.0 about removing a user
*
* @param userId - Android user id of the user being removed
@@ -110,6 +134,28 @@
}
/**
+ * Tells Keystore that a user's LSKF is being removed, ie the user's lock screen is changing to
+ * Swipe or None. Keystore uses this notification to delete the user's auth-bound keys.
+ *
+ * @param userId - Android user id of the user
+ * @return 0 if successful or a {@code ResponseCode}
+ * @hide
+ */
+ public static int onUserLskfRemoved(int userId) {
+ StrictMode.noteDiskWrite();
+ try {
+ getService().onUserLskfRemoved(userId);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "onUserLskfRemoved failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
+
+ /**
* Informs Keystore 2.0 that an app was uninstalled and the corresponding namespace is to
* be cleared.
*/
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index 2d2dd24..4ec5e1b 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -18,13 +18,14 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.StrictMode;
import android.security.authorization.IKeystoreAuthorization;
-import android.security.authorization.LockScreenEvent;
import android.system.keystore2.ResponseCode;
import android.util.Log;
@@ -37,7 +38,10 @@
public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
- private static IKeystoreAuthorization getService() {
+ /**
+ * @return an instance of IKeystoreAuthorization
+ */
+ public static IKeystoreAuthorization getService() {
return IKeystoreAuthorization.Stub.asInterface(
ServiceManager.checkService("android.security.authorization"));
}
@@ -71,26 +75,17 @@
}
/**
- * Informs keystore2 about lock screen event.
+ * Tells Keystore that the device is now unlocked for a user.
*
- * @param locked - whether it is a lock (true) or unlock (false) event
- * @param syntheticPassword - if it is an unlock event with the password, pass the synthetic
- * password provided by the LockSettingService
- * @param unlockingSids - KeyMint secure user IDs that should be permitted to unlock
- * UNLOCKED_DEVICE_REQUIRED keys.
- *
+ * @param userId - the user's Android user ID
+ * @param password - a secret derived from the user's synthetic password, if the unlock method
+ * is LSKF (or equivalent) and thus has made the synthetic password available
* @return 0 if successful or a {@code ResponseCode}.
*/
- public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
- @Nullable byte[] syntheticPassword, @Nullable long[] unlockingSids) {
+ public static int onDeviceUnlocked(int userId, @Nullable byte[] password) {
StrictMode.noteDiskWrite();
try {
- if (locked) {
- getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null, unlockingSids);
- } else {
- getService().onLockScreenEvent(
- LockScreenEvent.UNLOCK, userId, syntheticPassword, unlockingSids);
- }
+ getService().onDeviceUnlocked(userId, password);
return 0;
} catch (RemoteException | NullPointerException e) {
Log.w(TAG, "Can not connect to keystore", e);
@@ -100,4 +95,44 @@
}
}
+ /**
+ * Tells Keystore that the device is now locked for a user.
+ *
+ * @param userId - the user's Android user ID
+ * @param unlockingSids - list of biometric SIDs with which the device may be unlocked again
+ * @return 0 if successful or a {@code ResponseCode}.
+ */
+ public static int onDeviceLocked(int userId, @NonNull long[] unlockingSids) {
+ StrictMode.noteDiskWrite();
+ try {
+ getService().onDeviceLocked(userId, unlockingSids);
+ return 0;
+ } catch (RemoteException | NullPointerException e) {
+ Log.w(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ } catch (ServiceSpecificException e) {
+ return e.errorCode;
+ }
+ }
+
+ /**
+ * Gets the last authentication time of the given user and authenticators.
+ *
+ * @param userId user id
+ * @param authenticatorTypes an array of {@link HardwareAuthenticatorType}.
+ * @return the last authentication time or
+ * {@link BiometricConstants#BIOMETRIC_NO_AUTHENTICATION}.
+ */
+ public static long getLastAuthenticationTime(
+ long userId, @HardwareAuthenticatorType int[] authenticatorTypes) {
+ try {
+ return getService().getLastAuthTime(userId, authenticatorTypes);
+ } catch (RemoteException | NullPointerException e) {
+ Log.w(TAG, "Can not connect to keystore", e);
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ } catch (ServiceSpecificException e) {
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ }
+ }
+
}
diff --git a/keystore/java/android/security/GateKeeper.java b/keystore/java/android/security/GateKeeper.java
index af188a9..464714f 100644
--- a/keystore/java/android/security/GateKeeper.java
+++ b/keystore/java/android/security/GateKeeper.java
@@ -45,8 +45,19 @@
@UnsupportedAppUsage
public static long getSecureUserId() throws IllegalStateException {
+ return getSecureUserId(UserHandle.myUserId());
+ }
+
+ /**
+ * Return the secure user id for a given user id
+ * @param userId the user id, e.g. 0
+ * @return the secure user id or {@link GateKeeper#INVALID_SECURE_USER_ID} if no such mapping
+ * for the given user id is found.
+ * @throws IllegalStateException if there is an error retrieving the secure user id
+ */
+ public static long getSecureUserId(int userId) throws IllegalStateException {
try {
- return getService().getSecureUserId(UserHandle.myUserId());
+ return getService().getSecureUserId(userId);
} catch (RemoteException e) {
throw new IllegalStateException(
"Failed to obtain secure user ID from gatekeeper", e);
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 5e16bce..dd703f5 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -33,7 +33,6 @@
import android.util.Log;
import java.util.Calendar;
-import java.util.Objects;
/**
* @hide This should not be made public in its present form because it
@@ -139,13 +138,25 @@
return new KeyStore2();
}
+ /**
+ * Gets the {@link IKeystoreService} that should be started in early_hal in Android.
+ *
+ * @throws IllegalStateException if the KeystoreService is not available or has not
+ * been initialized when called. This is a state that should not happen and indicates
+ * and error somewhere in the stack or with the calling processes access permissions.
+ */
@NonNull private synchronized IKeystoreService getService(boolean retryLookup) {
if (mBinder == null || retryLookup) {
mBinder = IKeystoreService.Stub.asInterface(ServiceManager
- .getService(KEYSTORE2_SERVICE_NAME));
- Binder.allowBlocking(mBinder.asBinder());
+ .getService(KEYSTORE2_SERVICE_NAME));
}
- return Objects.requireNonNull(mBinder);
+ if (mBinder == null) {
+ throw new IllegalStateException(
+ "Could not connect to Keystore service. Keystore may have crashed or not been"
+ + " initialized");
+ }
+ Binder.allowBlocking(mBinder.asBinder());
+ return mBinder;
}
void delete(KeyDescriptor descriptor) throws KeyStoreException {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 1ba41b1..4982f37 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -618,7 +618,7 @@
* @see #isMgf1DigestsSpecified()
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
if (mMgf1Digests.isEmpty()) {
throw new IllegalStateException("Mask generation function (MGF) not specified");
@@ -633,7 +633,7 @@
* @see #getMgf1Digests()
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public boolean isMgf1DigestsSpecified() {
return !mMgf1Digests.isEmpty();
}
@@ -1282,15 +1282,18 @@
* function (MGF1) with a digest.
* The default digest for MGF1 is {@code SHA-1}, which will be specified during key creation
* time if no digests have been explicitly provided.
- * When using the key, the caller may not specify any digests that were not provided during
- * key creation time. The caller may specify the default digest, {@code SHA-1}, if no
+ * {@code null} may not be specified as a parameter to this method: It is not possible to
+ * disable MGF1 digest, a default must be present for when the caller tries to use it.
+ *
+ * <p>When using the key, the caller may not specify any digests that were not provided
+ * during key creation time. The caller may specify the default digest, {@code SHA-1}, if no
* digests were explicitly provided during key creation (but it is not necessary to do so).
*
* <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
- public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
+ public Builder setMgf1Digests(@NonNull @KeyProperties.DigestEnum String... mgf1Digests) {
mMgf1Digests = Set.of(mgf1Digests);
return this;
}
@@ -1596,6 +1599,8 @@
* {@link #getAttestationChallenge()} returns non-null and the spec is used to generate a
* symmetric (AES or HMAC) key, {@link javax.crypto.KeyGenerator#generateKey()} will throw
* {@link java.security.InvalidAlgorithmParameterException}.
+ *
+ * <p>The challenge may be up to 128 bytes.
*/
@NonNull
public Builder setAttestationChallenge(byte[] attestationChallenge) {
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index c1e3bab..7b6b2d1 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -401,7 +401,7 @@
* @see #isMgf1DigestsSpecified()
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
if (mMgf1Digests.isEmpty()) {
throw new IllegalStateException("Mask generation function (MGF) not specified");
@@ -416,7 +416,7 @@
* @see #getMgf1Digests()
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public boolean isMgf1DigestsSpecified() {
return !mMgf1Digests.isEmpty();
}
@@ -799,7 +799,7 @@
* <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
- @FlaggedApi("MGF1_DIGEST_SETTER")
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
mMgf1Digests = Set.of(mgf1Digests);
return this;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index ed4b485..9c05a3a 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -28,6 +28,7 @@
import android.hardware.security.keymint.Tag;
import android.os.Build;
import android.os.StrictMode;
+import android.security.Flags;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore2;
import android.security.KeyStoreException;
@@ -853,6 +854,22 @@
KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mgf1Digest
));
});
+
+ /* If the MGF1 Digest setter is not set, fall back to the previous behaviour:
+ * Add, as MGF1 Digest function, all the primary digests.
+ * Avoid adding the default MGF1 digest as it will have been included in the
+ * mKeymasterMgf1Digests field.
+ */
+ if (!getMgf1DigestSetterFlag()) {
+ final int defaultMgf1Digest = KeyProperties.Digest.toKeymaster(
+ DEFAULT_MGF1_DIGEST);
+ ArrayUtils.forEach(mKeymasterDigests, (digest) -> {
+ if (digest != defaultMgf1Digest) {
+ params.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, digest));
+ }
+ });
+ }
}
});
ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> {
@@ -928,6 +945,16 @@
return params;
}
+ private static boolean getMgf1DigestSetterFlag() {
+ try {
+ return Flags.mgf1DigestSetter();
+ } catch (SecurityException e) {
+ Log.w(TAG, "Cannot read MGF1 Digest setter flag value", e);
+ return false;
+ }
+ }
+
+
private void addAlgorithmSpecificParameters(List<KeyParameter> params) {
switch (mKeymasterAlgorithm) {
case KeymasterDefs.KM_ALGORITHM_RSA:
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index ddbd93e..2d8c5a3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -25,6 +25,7 @@
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.SecurityLevel;
import android.os.StrictMode;
+import android.security.Flags;
import android.security.GateKeeper;
import android.security.KeyStore2;
import android.security.KeyStoreParameter;
@@ -256,6 +257,15 @@
}
}
+ private static boolean getMgf1DigestSetterFlag() {
+ try {
+ return Flags.mgf1DigestSetter();
+ } catch (SecurityException e) {
+ Log.w(NAME, "Cannot read MGF1 Digest setter flag value", e);
+ return false;
+ }
+ }
+
@Override
public Date engineGetCreationDate(String alias) {
KeyEntryResponse response = getKeyMetadata(alias);
@@ -537,11 +547,31 @@
/* Because of default MGF1 digest is SHA-1. It has to be added in Key
* characteristics. Otherwise, crypto operations will fail with Incompatible
* MGF1 digest.
+ * If the MGF1 Digest setter flag isn't set, then the condition in the
+ * if clause above must be false (cannot have MGF1 digests specified if the
+ * flag was off). In that case, in addition to adding the default MGF1
+ * digest, we have to add all the other digests as MGF1 Digests.
+ *
*/
importArgs.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
));
+ if (!getMgf1DigestSetterFlag()) {
+ final int defaultMgf1Digest = KeyProperties.Digest.toKeymaster(
+ DEFAULT_MGF1_DIGEST);
+ for (String digest : spec.getDigests()) {
+ int digestToAddAsMgf1Digest = KeyProperties.Digest.toKeymaster(
+ digest);
+ // Do not add the default MGF1 digest as it has been added above.
+ if (digestToAddAsMgf1Digest != defaultMgf1Digest) {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
+ digestToAddAsMgf1Digest
+ ));
+ }
+ }
+ }
}
}
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index db58147..b5e6f94 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -514,6 +514,7 @@
"canvas/CanvasOpRasterizer.cpp",
"effects/StretchEffect.cpp",
"effects/GainmapRenderer.cpp",
+ "pipeline/skia/BackdropFilterDrawable.cpp",
"pipeline/skia/HolePunch.cpp",
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaRecordingCanvas.cpp",
diff --git a/libs/hwui/Mesh.cpp b/libs/hwui/Mesh.cpp
index e59bc95..37a7d74 100644
--- a/libs/hwui/Mesh.cpp
+++ b/libs/hwui/Mesh.cpp
@@ -90,8 +90,8 @@
FAIL_MESH_VALIDATE("%s mode requires at least %zu vertices but vertex count is %zu.",
modeToStr(meshMode), min_vcount_for_mode(meshMode), mVertexCount);
}
- SkASSERT(!fICount);
- SkASSERT(!fIOffset);
+ LOG_ALWAYS_FATAL_IF(mIndexCount != 0);
+ LOG_ALWAYS_FATAL_IF(mIndexOffset != 0);
}
if (!sm.ok()) {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 06aed63..09d3f05 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -35,7 +35,7 @@
#ifndef __ANDROID__ // Layoutlib does not compile HWUIProperties.sysprop as it depends on cutils properties
std::optional<bool> use_vulkan() {
- return base::GetBoolProperty("ro.hwui.use_vulkan", false);
+ return base::GetBoolProperty("ro.hwui.use_vulkan", true);
}
std::optional<std::int32_t> render_ahead() {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 1c39db3..1dd22cf 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -260,6 +260,12 @@
pushStagingDisplayListChanges(observer, info);
}
+ // always damageSelf when filtering backdrop content, or else the BackdropFilterDrawable will
+ // get a wrong snapshot of previous content.
+ if (mProperties.layerProperties().getBackdropImageFilter()) {
+ damageSelf(info);
+ }
+
if (mDisplayList) {
info.out.hasFunctors |= mDisplayList.hasFunctor();
mHasHolePunches = mDisplayList.hasHolePunches();
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 0589f13..c537123 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -55,6 +55,12 @@
return true;
}
+bool LayerProperties::setBackdropImageFilter(SkImageFilter* imageFilter) {
+ if (mBackdropImageFilter.get() == imageFilter) return false;
+ mBackdropImageFilter = sk_ref_sp(imageFilter);
+ return true;
+}
+
bool LayerProperties::setFromPaint(const SkPaint* paint) {
bool changed = false;
changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
@@ -70,6 +76,7 @@
setXferMode(other.xferMode());
setColorFilter(other.getColorFilter());
setImageFilter(other.getImageFilter());
+ setBackdropImageFilter(other.getBackdropImageFilter());
mStretchEffect = other.mStretchEffect;
return *this;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 064ba7a..e358b57 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -97,8 +97,12 @@
bool setImageFilter(SkImageFilter* imageFilter);
+ bool setBackdropImageFilter(SkImageFilter* imageFilter);
+
SkImageFilter* getImageFilter() const { return mImageFilter.get(); }
+ SkImageFilter* getBackdropImageFilter() const { return mBackdropImageFilter.get(); }
+
const StretchEffect& getStretchEffect() const { return mStretchEffect; }
StretchEffect& mutableStretchEffect() { return mStretchEffect; }
@@ -129,6 +133,7 @@
SkBlendMode mMode;
sk_sp<SkColorFilter> mColorFilter;
sk_sp<SkImageFilter> mImageFilter;
+ sk_sp<SkImageFilter> mBackdropImageFilter;
StretchEffect mStretchEffect;
};
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 8394c3c..ea9b6c9 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -588,10 +588,40 @@
// Canvas draw operations: Bitmaps
// ----------------------------------------------------------------------------
+bool SkiaCanvas::useGainmapShader(Bitmap& bitmap) {
+ // If the bitmap doesn't have a gainmap, don't use the gainmap shader
+ if (!bitmap.hasGainmap()) return false;
+
+ // If we don't have an owned canvas, then we're either hardware accelerated or drawing
+ // to a picture - use the gainmap shader out of caution. Ideally a picture canvas would
+ // use a drawable here instead to defer making that decision until the last possible
+ // moment
+ if (!mCanvasOwned) return true;
+
+ auto info = mCanvasOwned->imageInfo();
+
+ // If it's an unknown colortype then it's not a bitmap-backed canvas
+ if (info.colorType() == SkColorType::kUnknown_SkColorType) return true;
+
+ skcms_TransferFunction tfn;
+ info.colorSpace()->transferFn(&tfn);
+
+ auto transferType = skcms_TransferFunction_getType(&tfn);
+ switch (transferType) {
+ case skcms_TFType_HLGish:
+ case skcms_TFType_HLGinvish:
+ case skcms_TFType_PQish:
+ return true;
+ case skcms_TFType_Invalid:
+ case skcms_TFType_sRGBish:
+ return false;
+ }
+}
+
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
auto image = bitmap.makeImage();
- if (bitmap.hasGainmap()) {
+ if (useGainmapShader(bitmap)) {
Paint gainmapPaint = paint ? *paint : Paint();
sk_sp<SkShader> gainmapShader = uirenderer::MakeGainmapShader(
image, bitmap.gainmap()->bitmap->makeImage(), bitmap.gainmap()->info,
@@ -618,7 +648,7 @@
SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
- if (bitmap.hasGainmap()) {
+ if (useGainmapShader(bitmap)) {
Paint gainmapPaint = paint ? *paint : Paint();
sk_sp<SkShader> gainmapShader = uirenderer::MakeGainmapShader(
image, bitmap.gainmap()->bitmap->makeImage(), bitmap.gainmap()->info,
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index b785989..9cb50ed 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -223,6 +223,8 @@
void drawPoints(const float* points, int count, const Paint& paint, SkCanvas::PointMode mode);
+ bool useGainmapShader(Bitmap& bitmap);
+
class Clip;
std::unique_ptr<SkCanvas> mCanvasOwned; // Might own a canvas we allocated.
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp
index 69418b0..7298906 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.cpp
+++ b/libs/hwui/jni/YuvToJpegEncoder.cpp
@@ -5,6 +5,7 @@
#include "SkStream.h"
#include "YuvToJpegEncoder.h"
#include <ui/PixelFormat.h>
+#include <utils/Errors.h>
#include <hardware/hardware.h>
#include "graphics_jni_helpers.h"
@@ -298,7 +299,7 @@
}
///////////////////////////////////////////////////////////////////////////////
-using namespace android::ultrahdr;
+using namespace ultrahdr;
ultrahdr_color_gamut P010Yuv420ToJpegREncoder::findColorGamut(JNIEnv* env, int aDataSpace) {
switch (aDataSpace & ADataSpace::STANDARD_MASK) {
diff --git a/libs/hwui/jni/YuvToJpegEncoder.h b/libs/hwui/jni/YuvToJpegEncoder.h
index 8ef7805..0e711ef 100644
--- a/libs/hwui/jni/YuvToJpegEncoder.h
+++ b/libs/hwui/jni/YuvToJpegEncoder.h
@@ -103,7 +103,7 @@
* @param aDataSpace data space defined in data_space.h.
* @return color gamut for JPEG/R.
*/
- static android::ultrahdr::ultrahdr_color_gamut findColorGamut(JNIEnv* env, int aDataSpace);
+ static ultrahdr::ultrahdr_color_gamut findColorGamut(JNIEnv* env, int aDataSpace);
/** Map data space (defined in DataSpace.java and data_space.h) to the transfer function
* used in JPEG/R
@@ -112,8 +112,8 @@
* @param aDataSpace data space defined in data_space.h.
* @return color gamut for JPEG/R.
*/
- static android::ultrahdr::ultrahdr_transfer_function findHdrTransferFunction(
- JNIEnv* env, int aDataSpace);
+ static ultrahdr::ultrahdr_transfer_function findHdrTransferFunction(JNIEnv* env,
+ int aDataSpace);
};
#endif // _ANDROID_GRAPHICS_YUV_TO_JPEG_ENCODER_H_
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 8c7b9a4..2a218a2 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -243,6 +243,13 @@
return SET_AND_DIRTY(mutateLayerProperties().setImageFilter, imageFilter, RenderNode::GENERIC);
}
+static jboolean android_view_RenderNode_setBackdropRenderEffect(
+ CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr, jlong renderEffectPtr) {
+ SkImageFilter* imageFilter = reinterpret_cast<SkImageFilter*>(renderEffectPtr);
+ return SET_AND_DIRTY(mutateLayerProperties().setBackdropImageFilter, imageFilter,
+ RenderNode::GENERIC);
+}
+
static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
bool hasOverlappingRendering) {
return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering,
@@ -792,6 +799,8 @@
{"nSetAlpha", "(JF)Z", (void*)android_view_RenderNode_setAlpha},
{"nSetRenderEffect", "(JJ)Z", (void*)android_view_RenderNode_setRenderEffect},
+ {"nSetBackdropRenderEffect", "(JJ)Z",
+ (void*)android_view_RenderNode_setBackdropRenderEffect},
{"nSetHasOverlappingRendering", "(JZ)Z",
(void*)android_view_RenderNode_setHasOverlappingRendering},
{"nSetUsageHint", "(JI)V", (void*)android_view_RenderNode_setUsageHint},
diff --git a/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp b/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp
new file mode 100644
index 0000000..ffad699
--- /dev/null
+++ b/libs/hwui/pipeline/skia/BackdropFilterDrawable.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "BackdropFilterDrawable.h"
+
+#include <SkImage.h>
+#include <SkSurface.h>
+
+#include "RenderNode.h"
+#include "RenderNodeDrawable.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+BackdropFilterDrawable::~BackdropFilterDrawable() {}
+
+bool BackdropFilterDrawable::prepareToDraw(SkCanvas* canvas, const RenderProperties& properties,
+ int backdropImageWidth, int backdropImageHeight) {
+ // the drawing bounds for blurred content.
+ mDstBounds.setWH(properties.getWidth(), properties.getHeight());
+
+ float alphaMultiplier = 1.0f;
+ RenderNodeDrawable::setViewProperties(properties, canvas, &alphaMultiplier, true);
+
+ // get proper subset for previous content.
+ canvas->getTotalMatrix().mapRect(&mImageSubset, mDstBounds);
+ SkRect imageSubset(mImageSubset);
+ // ensure the subset is inside bounds of previous content.
+ if (!mImageSubset.intersect(SkRect::MakeWH(backdropImageWidth, backdropImageHeight))) {
+ return false;
+ }
+
+ // correct the drawing bounds if subset was changed.
+ if (mImageSubset != imageSubset) {
+ SkMatrix inverse;
+ if (canvas->getTotalMatrix().invert(&inverse)) {
+ inverse.mapRect(&mDstBounds, mImageSubset);
+ }
+ }
+
+ // follow the alpha from the target RenderNode.
+ mPaint.setAlpha(properties.layerProperties().alpha() * alphaMultiplier);
+ return true;
+}
+
+void BackdropFilterDrawable::onDraw(SkCanvas* canvas) {
+ const RenderProperties& properties = mTargetRenderNode->properties();
+ auto* backdropFilter = properties.layerProperties().getBackdropImageFilter();
+ auto* surface = canvas->getSurface();
+ if (!backdropFilter || !surface) {
+ return;
+ }
+
+ auto backdropImage = surface->makeImageSnapshot();
+ // sync necessary properties from target RenderNode.
+ if (!prepareToDraw(canvas, properties, backdropImage->width(), backdropImage->height())) {
+ return;
+ }
+
+ auto imageSubset = mImageSubset.roundOut();
+ backdropImage =
+ backdropImage->makeWithFilter(canvas->recordingContext(), backdropFilter, imageSubset,
+ imageSubset, &mOutSubset, &mOutOffset);
+ canvas->drawImageRect(backdropImage, SkRect::Make(mOutSubset), mDstBounds,
+ SkSamplingOptions(SkFilterMode::kLinear), &mPaint,
+ SkCanvas::kStrict_SrcRectConstraint);
+}
+
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/BackdropFilterDrawable.h b/libs/hwui/pipeline/skia/BackdropFilterDrawable.h
new file mode 100644
index 0000000..9e35837
--- /dev/null
+++ b/libs/hwui/pipeline/skia/BackdropFilterDrawable.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <SkCanvas.h>
+#include <SkDrawable.h>
+#include <SkPaint.h>
+
+namespace android {
+namespace uirenderer {
+
+class RenderNode;
+class RenderProperties;
+
+namespace skiapipeline {
+
+/**
+ * This drawable captures it's backdrop content and render it with a
+ * image filter.
+ */
+class BackdropFilterDrawable : public SkDrawable {
+public:
+ BackdropFilterDrawable(RenderNode* renderNode, SkCanvas* canvas)
+ : mTargetRenderNode(renderNode), mBounds(canvas->getLocalClipBounds()) {}
+
+ ~BackdropFilterDrawable();
+
+private:
+ RenderNode* mTargetRenderNode;
+ SkPaint mPaint;
+
+ SkRect mDstBounds;
+ SkRect mImageSubset;
+ SkIRect mOutSubset;
+ SkIPoint mOutOffset;
+
+ /**
+ * Check all necessary properties before actual drawing.
+ * Return true if ready to draw.
+ */
+ bool prepareToDraw(SkCanvas* canvas, const RenderProperties& properties, int backdropImageWidth,
+ int backdropImageHeight);
+
+protected:
+ void onDraw(SkCanvas* canvas) override;
+
+ virtual SkRect onGetBounds() override { return mBounds; }
+ const SkRect mBounds;
+};
+
+} // namespace skiapipeline
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index da4f66d..9d72c23 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -362,7 +362,7 @@
}
void RenderNodeDrawable::setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
- float* alphaMultiplier) {
+ float* alphaMultiplier, bool ignoreLayer) {
if (properties.getLeft() != 0 || properties.getTop() != 0) {
canvas->translate(properties.getLeft(), properties.getTop());
}
@@ -378,7 +378,8 @@
canvas->concat(*properties.getTransformMatrix());
}
}
- if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale) {
+ if (Properties::getStretchEffectBehavior() == StretchEffectBehavior::UniformScale &&
+ !ignoreLayer) {
const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
if (!stretch.isEmpty()) {
canvas->concat(
@@ -388,10 +389,10 @@
const bool isLayer = properties.effectiveLayerType() != LayerType::None;
int clipFlags = properties.getClippingFlags();
if (properties.getAlpha() < 1) {
- if (isLayer) {
+ if (isLayer && !ignoreLayer) {
clipFlags &= ~CLIP_TO_BOUNDS; // bounds clipping done by layer
}
- if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering())) {
+ if (CC_LIKELY(isLayer || !properties.getHasOverlappingRendering()) || ignoreLayer) {
*alphaMultiplier = properties.getAlpha();
} else {
// savelayer needed to create an offscreen buffer
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index c7582e7..818ac45 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -120,7 +120,7 @@
* Applies the rendering properties of a view onto a SkCanvas.
*/
static void setViewProperties(const RenderProperties& properties, SkCanvas* canvas,
- float* alphaMultiplier);
+ float* alphaMultiplier, bool ignoreLayer = false);
/**
* Stores transform on the canvas at time of recording and is used for
@@ -149,6 +149,11 @@
* display list that is searched for any render nodes with getProjectBackwards==true
*/
SkiaDisplayList* mProjectedDisplayList = nullptr;
+
+ /**
+ * Allow BackdropFilterDrawable to apply same render properties onto SkCanvas.
+ */
+ friend class BackdropFilterDrawable;
};
} // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 3ca7eeb..58c14c1 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -37,6 +37,7 @@
#include "NinePatchUtils.h"
#include "RenderNode.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include "pipeline/skia/BackdropFilterDrawable.h"
#ifdef __ANDROID__ // Layoutlib does not support GL, Vulcan etc.
#include "pipeline/skia/GLFunctorDrawable.h"
#include "pipeline/skia/VkFunctorDrawable.h"
@@ -168,6 +169,14 @@
// Put Vulkan WebViews with non-rectangular clips in a HW layer
renderNode->mutateStagingProperties().setClipMayBeComplex(mRecorder.isClipMayBeComplex());
}
+
+ // draw backdrop filter drawable if needed.
+ if (renderNode->stagingProperties().layerProperties().getBackdropImageFilter()) {
+ auto* backdropFilterDrawable =
+ mDisplayList->allocateDrawable<BackdropFilterDrawable>(renderNode, asSkCanvas());
+ drawDrawable(backdropFilterDrawable);
+ }
+
drawDrawable(&renderNodeDrawable);
// use staging property, since recording on UI thread
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 504dfaa..cedfaed 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -919,6 +919,7 @@
// buildLayer() will leave the tree in an unknown state, so we must stop drawing
stopDrawing();
+ ScopedActiveContext activeContext(this);
TreeInfo info(TreeInfo::MODE_FULL, *this);
info.damageAccumulator = &mDamageAccumulator;
info.layerUpdateQueue = &mLayerUpdateQueue;
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 596bd37..f67042b 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -20,14 +20,17 @@
#include <SkBlendMode.h>
#include <SkClipStack.h>
#include <SkSurface_Base.h>
+#include <include/effects/SkImageFilters.h>
#include <string.h>
+
#include "AnimationContext.h"
#include "DamageAccumulator.h"
#include "FatalTestCanvas.h"
#include "IContextFactory.h"
-#include "hwui/Paint.h"
#include "RecordingCanvas.h"
#include "SkiaCanvas.h"
+#include "hwui/Paint.h"
+#include "pipeline/skia/BackdropFilterDrawable.h"
#include "pipeline/skia/SkiaDisplayList.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
#include "pipeline/skia/SkiaPipeline.h"
@@ -1210,3 +1213,77 @@
canvas.drawDrawable(&drawable);
EXPECT_EQ(2, canvas.mDrawCounter);
}
+
+// Verify drawing logics for BackdropFilterDrawable
+RENDERTHREAD_TEST(BackdropFilterDrawable, drawing) {
+ static const int CANVAS_WIDTH = 100;
+ static const int CANVAS_HEIGHT = 200;
+ class SimpleTestCanvas : public TestCanvasBase {
+ public:
+ SkRect mDstBounds;
+ SimpleTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
+ void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
+ // did nothing.
+ }
+
+ // called when BackdropFilterDrawable is drawn.
+ void onDrawImageRect2(const SkImage*, const SkRect& src, const SkRect& dst,
+ const SkSamplingOptions&, const SkPaint*,
+ SrcRectConstraint) override {
+ mDrawCounter++;
+ mDstBounds = dst;
+ }
+ };
+ class SimpleLayer : public SkSurface_Base {
+ public:
+ SimpleLayer()
+ : SkSurface_Base(SkImageInfo::MakeN32Premul(CANVAS_WIDTH, CANVAS_HEIGHT), nullptr) {
+ }
+ virtual sk_sp<SkImage> onNewImageSnapshot(const SkIRect* bounds) override {
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(CANVAS_WIDTH, CANVAS_HEIGHT);
+ bitmap.setImmutable();
+ return SkImage::MakeFromBitmap(bitmap);
+ }
+ SkCanvas* onNewCanvas() override { return new SimpleTestCanvas(); }
+ sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
+ bool onCopyOnWrite(ContentChangeMode) override { return true; }
+ void onWritePixels(const SkPixmap&, int x, int y) {}
+ };
+
+ auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ Paint());
+ });
+
+ sk_sp<SkSurface> surface(new SimpleLayer());
+ auto* canvas = reinterpret_cast<SimpleTestCanvas*>(surface->getCanvas());
+ RenderNodeDrawable drawable(node.get(), canvas, true);
+ BackdropFilterDrawable backdropDrawable(node.get(), canvas);
+ canvas->drawDrawable(&drawable);
+ canvas->drawDrawable(&backdropDrawable);
+ // no backdrop filter, skip drawing.
+ EXPECT_EQ(0, canvas->mDrawCounter);
+
+ sk_sp<SkImageFilter> filter(SkImageFilters::Blur(3, 3, nullptr));
+ node->animatorProperties().mutateLayerProperties().setBackdropImageFilter(filter.get());
+ canvas->drawDrawable(&drawable);
+ canvas->drawDrawable(&backdropDrawable);
+ // backdrop filter is set, ok to draw.
+ EXPECT_EQ(1, canvas->mDrawCounter);
+ EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), canvas->mDstBounds);
+
+ canvas->translate(30, 30);
+ canvas->drawDrawable(&drawable);
+ canvas->drawDrawable(&backdropDrawable);
+ // the drawable is still visible, ok to draw.
+ EXPECT_EQ(2, canvas->mDrawCounter);
+ EXPECT_EQ(SkRect::MakeLTRB(0, 0, CANVAS_WIDTH - 30, CANVAS_HEIGHT - 30), canvas->mDstBounds);
+
+ canvas->translate(CANVAS_WIDTH, CANVAS_HEIGHT);
+ canvas->drawDrawable(&drawable);
+ canvas->drawDrawable(&backdropDrawable);
+ // the drawable is invisible, skip drawing.
+ EXPECT_EQ(2, canvas->mDrawCounter);
+}
diff --git a/media/Android.bp b/media/Android.bp
index f69dd3c..3493408 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -23,6 +23,10 @@
name: "soundtrigger_middleware-aidl",
unstable: true,
local_include_dir: "aidl",
+ defaults: [
+ "latest_android_media_audio_common_types_import_interface",
+ "latest_android_media_soundtrigger_types_import_interface",
+ ],
backend: {
java: {
sdk_version: "module_current",
@@ -32,8 +36,6 @@
"aidl/android/media/soundtrigger_middleware/*.aidl",
],
imports: [
- "android.media.audio.common.types-V2",
- "android.media.soundtrigger.types-V1",
"media_permission-aidl",
],
}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 7faa13c..447d3bb 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1176,6 +1176,7 @@
case AudioFormat.ENCODING_PCM_FLOAT:
case AudioFormat.ENCODING_PCM_16BIT:
case AudioFormat.ENCODING_PCM_8BIT:
+ case AudioFormat.ENCODING_E_AC3_JOC:
mAudioFormat = audioFormat;
break;
default:
@@ -1188,20 +1189,12 @@
// Convenience method for the contructor's audio buffer size check.
- // preconditions:
- // mChannelCount is valid
- // mAudioFormat is AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT,
- // or AudioFormat.ENCODING_PCM_FLOAT
// postcondition:
// mNativeBufferSizeInBytes is valid (multiple of frame size, positive)
private void audioBuffSizeCheck(int audioBufferSize) throws IllegalArgumentException {
- // NB: this section is only valid with PCM data.
- // To update when supporting compressed formats
- int frameSizeInBytes = mChannelCount
- * (AudioFormat.getBytesPerSample(mAudioFormat));
- if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) {
+ if ((audioBufferSize % getFormat().getFrameSizeInBytes() != 0) || (audioBufferSize < 1)) {
throw new IllegalArgumentException("Invalid audio buffer size " + audioBufferSize
- + " (frame size " + frameSizeInBytes + ")");
+ + " (frame size " + getFormat().getFrameSizeInBytes() + ")");
}
mNativeBufferSizeInBytes = audioBufferSize;
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 0a0a626..ab7c27f 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -272,7 +272,7 @@
Log.d(TAG, "Trying to get AIDL service");
IMediaCasService serviceAidl =
IMediaCasService.Stub.asInterface(
- ServiceManager.getService(
+ ServiceManager.waitForDeclaredService(
IMediaCasService.DESCRIPTOR + "/default"));
if (serviceAidl != null) {
return serviceAidl;
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 29e8716..cda919f 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -442,6 +442,7 @@
* but it must be released if your activity or service is being destroyed.
*/
public void release() {
+ setCallback(null);
try {
mBinder.destroySession();
} catch (RemoteException e) {
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index ed1072c..94fce79 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -81,6 +81,7 @@
"libhidlallocatorutils",
"libhidlbase",
"libsonivox",
+ "server_configurable_flags",
"android.hardware.cas@1.0",
"android.hardware.cas.native@1.0",
"android.hardware.drm@1.3",
@@ -99,6 +100,7 @@
static_libs: [
"libgrallocusage",
"libmedia_midiiowrapper",
+ "android.media.playback.flags-aconfig-cc",
],
include_dirs: [
@@ -120,11 +122,6 @@
"-Wunused",
"-Wunreachable-code",
],
-
- // Workaround Clang LTO crash.
- lto: {
- never: true,
- },
}
cc_library_shared {
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 1458758..2a10fa7 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -35,7 +35,9 @@
#include "android_media_MediaDataSource.h"
#include "android_media_Streams.h"
#include "android_util_Binder.h"
+#include <com_android_media_playback_flags.h>
+namespace playback_flags = com::android::media::playback::flags;
using namespace android;
struct fields_t {
@@ -374,9 +376,12 @@
jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
return NULL;
}
- // For getFrameAtTime family of calls, default to ANDROID_BITMAP_FORMAT_RGB_565
- // to keep the behavior consistent with older releases
- AndroidBitmapFormat colorFormat = getColorFormat(env, params, ANDROID_BITMAP_FORMAT_RGB_565);
+
+ AndroidBitmapFormat defaultColorFormat =
+ playback_flags::mediametadataretriever_default_rgba8888()
+ ? ANDROID_BITMAP_FORMAT_RGBA_8888
+ : ANDROID_BITMAP_FORMAT_RGB_565;
+ AndroidBitmapFormat colorFormat = getColorFormat(env, params, defaultColorFormat);
// Call native method to retrieve a video frame
VideoFrame *videoFrame = NULL;
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 8b5b726..cf5059c 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -44,9 +44,4 @@
"-Wunreachable-code",
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
-
- // Workaround Clang LTO crash.
- lto: {
- never: true,
- },
}
diff --git a/media/jni/playback_flags.aconfig b/media/jni/playback_flags.aconfig
new file mode 100644
index 0000000..2bb0ec5
--- /dev/null
+++ b/media/jni/playback_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.media.playback.flags"
+
+flag {
+ name: "mediametadataretriever_default_rgba8888"
+ namespace: "media_solutions"
+ description: "Change MediaMetadataRetriever to use RGBA8888 for bitmap handling by default."
+ bug: "298965955"
+}
diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp
index bdd7afe..7a329bc 100644
--- a/media/tests/MediaFrameworkTest/Android.bp
+++ b/media/tests/MediaFrameworkTest/Android.bp
@@ -20,6 +20,8 @@
"androidx.test.ext.junit",
"androidx.test.rules",
"android-ex-camera2",
+ "android.media.playback.flags-aconfig-java",
+ "flag-junit",
"testables",
"testng",
"truth",
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
index f70d2d1..e3d3897 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
@@ -16,19 +16,27 @@
package com.android.mediaframeworktest.unit;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import androidx.test.runner.AndroidJUnit4;
+import com.android.media.playback.flags.Flags;
import com.android.mediaframeworktest.MediaNames;
import com.android.mediaframeworktest.MediaProfileReader;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,6 +48,9 @@
private static final String TAG = "MediaMetadataRetrieverTest";
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
// Test album art extraction.
@MediumTest
@Test
@@ -284,6 +295,34 @@
assertTrue(!hasFailed);
}
+ /** Test the thumbnail is generated when the default is set to RGBA8888 */
+ @MediumTest
+ // TODO(b/305160754) Remove the following annotation and use SetFlagsRule.enableFlags
+ @RequiresFlagsEnabled(Flags.FLAG_MEDIAMETADATARETRIEVER_DEFAULT_RGBA8888)
+ @Test
+ public void testGetFrameAtTimeWithRGBA8888Flag_Set() throws IOException {
+ try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+ retriever.setDataSource(MediaNames.TEST_PATH_1);
+ Bitmap bitmap = retriever.getFrameAtTime(-1);
+ assertNotNull(bitmap);
+ assertEquals(Bitmap.Config.ARGB_8888, bitmap.getConfig());
+ }
+ }
+
+ /** Test the thumbnail is generated when the default is not set to RGBA8888 */
+ @MediumTest
+ // TODO(b/305160754) Remove the following annotation and use SetFlagsRule.disableFlags
+ @RequiresFlagsDisabled(Flags.FLAG_MEDIAMETADATARETRIEVER_DEFAULT_RGBA8888)
+ @Test
+ public void testGetFrameAtTimeWithRGBA8888Flag_Unset() throws IOException {
+ try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+ retriever.setDataSource(MediaNames.TEST_PATH_1);
+ Bitmap bitmap = retriever.getFrameAtTime(-1);
+ assertNotNull(bitmap);
+ assertEquals(Bitmap.Config.RGB_565, bitmap.getConfig());
+ }
+ }
+
// TODO:
// Encode and test for the correct mix of metadata elements on a per-file basis?
// We should be able to compare the actual returned metadata with the expected metadata
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index cb74cfc..fbdfe3e 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -54,6 +54,8 @@
?application/ttml+xml ttml dfxp
?application/vnd.android.ota ota
?application/vnd.apple.mpegurl m3u8
+?application/vnd.apple.pkpass pkpass
+?application/vnd.apple.pkpasses pkpasses
?application/vnd.ms-pki.stl stl
?application/vnd.ms-powerpoint pot
?application/vnd.ms-wpl wpl
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index fe3132e..ceab164 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -21,23 +21,21 @@
#include <android/font.h>
#include <android/font_matcher.h>
#include <android/system_fonts.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <hwui/MinikinSkia.h>
+#include <libxml/parser.h>
+#include <log/log.h>
+#include <minikin/FontCollection.h>
+#include <minikin/LocaleList.h>
+#include <minikin/SystemFonts.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <memory>
#include <string>
#include <vector>
-#include <errno.h>
-#include <fcntl.h>
-#include <libxml/tree.h>
-#include <log/log.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <hwui/MinikinSkia.h>
-#include <minikin/FontCollection.h>
-#include <minikin/LocaleList.h>
-#include <minikin/SystemFonts.h>
-
struct XmlCharDeleter {
void operator()(xmlChar* b) { xmlFree(b); }
};
diff --git a/nfc/Android.bp b/nfc/Android.bp
new file mode 100644
index 0000000..bf9f47c
--- /dev/null
+++ b/nfc/Android.bp
@@ -0,0 +1,51 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "framework-nfc-non-updatable-sources",
+ path: "java",
+ srcs: [],
+}
+
+filegroup {
+ name: "framework-nfc-updatable-sources",
+ path: "java",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ exclude_srcs: [
+ ":framework-nfc-non-updatable-sources",
+ ],
+}
+
+java_sdk_library {
+ name: "framework-nfc",
+ libs: [
+ "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+ ],
+ srcs: [
+ ":framework-nfc-updatable-sources",
+ ],
+ defaults: ["framework-non-updatable-unbundled-defaults"],
+ permitted_packages: [
+ "android.nfc",
+ "com.android.nfc",
+ ],
+ hidden_api_packages: [
+ "com.android.nfc",
+ ],
+ aidl: {
+ include_dirs: [
+ // TODO (b/303286040): Remove these when we change to |framework-module-defaults|
+ "frameworks/base/nfc/java",
+ "frameworks/base/core/java",
+ ],
+ },
+}
diff --git a/nfc/OWNERS b/nfc/OWNERS
new file mode 100644
index 0000000..35e9713
--- /dev/null
+++ b/nfc/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48448
+include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc/TEST_MAPPING b/nfc/TEST_MAPPING
new file mode 100644
index 0000000..5b5ea37
--- /dev/null
+++ b/nfc/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "NfcManagerTests"
+ },
+ {
+ "name": "CtsNfcTestCases"
+ }
+ ]
+}
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/nfc/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/module-lib-current.txt b/nfc/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/nfc/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/module-lib-removed.txt b/nfc/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/nfc/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/removed.txt b/nfc/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/nfc/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/nfc/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/system-removed.txt b/nfc/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/nfc/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/test-current.txt b/nfc/api/test-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/nfc/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/api/test-removed.txt b/nfc/api/test-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/nfc/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/nfc/java/android/nfc/Placeholder.java b/nfc/java/android/nfc/Placeholder.java
new file mode 100644
index 0000000..3509644
--- /dev/null
+++ b/nfc/java/android/nfc/Placeholder.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+/**
+ * Placeholder class so new framework-nfc module isn't empty, will be removed once module is
+ * populated.
+ *
+ * @hide
+ *
+ */
+public class Placeholder {
+}
diff --git a/packages/CrashRecovery/OWNERS b/packages/CrashRecovery/OWNERS
new file mode 100644
index 0000000..daa0211
--- /dev/null
+++ b/packages/CrashRecovery/OWNERS
@@ -0,0 +1,3 @@
+ancr@google.com
+harshitmahajan@google.com
+robertogil@google.com
diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp
new file mode 100644
index 0000000..b2af315
--- /dev/null
+++ b/packages/CrashRecovery/framework/Android.bp
@@ -0,0 +1,9 @@
+filegroup {
+ name: "framework-crashrecovery-sources",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ path: "java",
+ visibility: ["//frameworks/base:__subpackages__"],
+}
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
similarity index 100%
rename from core/java/android/service/watchdog/ExplicitHealthCheckService.java
rename to packages/CrashRecovery/framework/java/android/service/watchdog/ExplicitHealthCheckService.java
diff --git a/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl b/packages/CrashRecovery/framework/java/android/service/watchdog/IExplicitHealthCheckService.aidl
similarity index 96%
rename from core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
rename to packages/CrashRecovery/framework/java/android/service/watchdog/IExplicitHealthCheckService.aidl
index 78c0328..9096509 100644
--- a/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
+++ b/packages/CrashRecovery/framework/java/android/service/watchdog/IExplicitHealthCheckService.aidl
@@ -21,6 +21,7 @@
/**
* @hide
*/
+@PermissionManuallyEnforced
oneway interface IExplicitHealthCheckService
{
void setCallback(in @nullable RemoteCallback callback);
diff --git a/core/java/android/service/watchdog/OWNERS b/packages/CrashRecovery/framework/java/android/service/watchdog/OWNERS
similarity index 100%
rename from core/java/android/service/watchdog/OWNERS
rename to packages/CrashRecovery/framework/java/android/service/watchdog/OWNERS
diff --git a/core/java/android/service/watchdog/PackageConfig.aidl b/packages/CrashRecovery/framework/java/android/service/watchdog/PackageConfig.aidl
similarity index 100%
rename from core/java/android/service/watchdog/PackageConfig.aidl
rename to packages/CrashRecovery/framework/java/android/service/watchdog/PackageConfig.aidl
diff --git a/packages/CrashRecovery/services/Android.bp b/packages/CrashRecovery/services/Android.bp
new file mode 100644
index 0000000..27ddff9
--- /dev/null
+++ b/packages/CrashRecovery/services/Android.bp
@@ -0,0 +1,9 @@
+filegroup {
+ name: "services-crashrecovery-sources",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.aidl",
+ ],
+ path: "java",
+ visibility: ["//frameworks/base:__subpackages__"],
+}
diff --git a/services/core/java/com/android/server/ExplicitHealthCheckController.java b/packages/CrashRecovery/services/java/com/android/server/ExplicitHealthCheckController.java
similarity index 100%
rename from services/core/java/com/android/server/ExplicitHealthCheckController.java
rename to packages/CrashRecovery/services/java/com/android/server/ExplicitHealthCheckController.java
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
similarity index 100%
rename from services/core/java/com/android/server/PackageWatchdog.java
rename to packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
diff --git a/services/core/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
similarity index 100%
rename from services/core/java/com/android/server/RescueParty.java
rename to packages/CrashRecovery/services/java/com/android/server/RescueParty.java
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
similarity index 100%
rename from services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
rename to packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
similarity index 100%
rename from services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
rename to packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 1088ace..4992ef1 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -290,7 +290,14 @@
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
- session.commit(pendingIntent.getIntentSender());
+ try {
+ session.commit(pendingIntent.getIntentSender());
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Cannot install package: ", e);
+ launchFailure(PackageInstaller.STATUS_FAILURE,
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+ return;
+ }
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index ed5654d..ec50323 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2432,9 +2432,7 @@
R.bool.def_auto_time_zone); // Sync timezone to NITZ
loadSetting(stmt, Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
- ("1".equals(SystemProperties.get("ro.boot.qemu"))
- || res.getBoolean(R.bool.def_stay_on_while_plugged_in))
- ? 1 : 0);
+ res.getBoolean(R.bool.def_stay_on_while_plugged_in) ? 1 : 0);
loadIntegerSetting(stmt, Settings.Global.WIFI_SLEEP_POLICY,
R.integer.def_wifi_sleep_policy);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 215a040..3299a37 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1149,8 +1149,6 @@
Slog.v(LOG_TAG, "getConfigSetting(" + name + ")");
}
- Settings.Config.enforceReadPermission(/*namespace=*/name.split("/")[0]);
-
// Get the value.
synchronized (mLock) {
return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_CONFIG,
@@ -1330,9 +1328,6 @@
Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
}
- Settings.Config.enforceReadPermission(
- prefix != null ? prefix.split("/")[0] : null);
-
synchronized (mLock) {
// Get the settings.
SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ee9883b..1edb89c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -588,6 +588,9 @@
<!-- Permission needed for CTS test - ConcurrencyTest#testP2pSetWfdInfo -->
<uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
+ <!-- Permission required for CTS test - CtsThreadNetworkTestCases -->
+ <uses-permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED"/>
+
<!-- Permission required for CTS tests to enable/disable rate limiting toasts. -->
<uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" />
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 34545cf..967a36b 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -27,6 +27,8 @@
chandruis@google.com
chrisgollner@google.com
cinek@google.com
+cocod@google.com
+darrellshi@google.com
dupin@google.com
ethibodeau@google.com
evanlaird@google.com
@@ -80,6 +82,7 @@
pinyaoting@google.com
pixel@google.com
pomini@google.com
+princedonkor@google.com
rahulbanerjee@google.com
roosa@google.com
saff@google.com
@@ -102,6 +105,7 @@
victortulias@google.com
winsonc@google.com
wleshner@google.com
+wxyz@google.com
xilei@google.com
xuqiu@google.com
yeinj@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 63b4288..e0ce3db 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -27,7 +27,7 @@
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
-import android.os.SystemProperties;
+import android.os.Build;
import android.provider.Settings;
import android.util.Log;
@@ -87,7 +87,7 @@
String clipSource = mClipboardManager.getPrimaryClipSource();
ClipData clipData = mClipboardManager.getPrimaryClip();
- if (shouldSuppressOverlay(clipData, clipSource, isEmulator())) {
+ if (shouldSuppressOverlay(clipData, clipSource, Build.IS_EMULATOR)) {
Log.i(TAG, "Clipboard overlay suppressed.");
return;
}
@@ -141,10 +141,6 @@
return true;
}
- private static boolean isEmulator() {
- return SystemProperties.getBoolean("ro.boot.qemu", false);
- }
-
private boolean isUserSetupComplete() {
return Settings.Secure.getInt(mContext.getContentResolver(),
SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 67e9664..27e6895 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -165,6 +165,9 @@
+ "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})"
+ "?)*";
+ // Not all JDKs support emoji patterns, including the one errorprone runs under, which
+ // makes it think that this is an invalid pattern.
+ @SuppressWarnings("InvalidPatternSyntax")
private static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX);
public static final String EMPTY_STRING = "";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 6875b52..ea140cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -24,6 +24,7 @@
import android.content.IntentFilter;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.icu.lang.UCharacter;
import android.icu.text.DateTimePatternGenerator;
import android.os.Bundle;
import android.os.Handler;
@@ -452,7 +453,7 @@
if (a >= 0) {
// Move a back so any whitespace before AM/PM is also in the alternate size.
final int b = a;
- while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
+ while (a > 0 && UCharacter.isUWhiteSpace(format.charAt(a - 1))) {
a--;
}
format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 941cd77..900805f 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -25,6 +25,7 @@
import android.content.IntentFilter;
import android.debug.IAdbManager;
import android.hardware.usb.UsbManager;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ServiceManager;
@@ -69,8 +70,7 @@
super.onCreate(icicle);
// Emulator does not support reseating the usb cable to reshow the dialog.
- boolean isEmulator = SystemProperties.get("ro.boot.qemu").equals("1");
- if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0 && !isEmulator) {
+ if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0 && !Build.IS_EMULATOR) {
mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java
index 1866a99..67c2caa 100644
--- a/rs/java/android/renderscript/ScriptC.java
+++ b/rs/java/android/renderscript/ScriptC.java
@@ -16,9 +16,12 @@
package android.renderscript;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.res.Resources;
+import android.util.Slog;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -35,6 +38,15 @@
private static final String TAG = "ScriptC";
/**
+ * In targetSdkVersion 35 and above, Renderscript's ScriptC stops being supported
+ * and an exception is thrown when the class is instantiated.
+ * In targetSdkVersion 34 and below, Renderscript's ScriptC still works.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = 35)
+ private static final long RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID = 297019750L;
+
+ /**
* Only intended for use by the generated derived classes.
*
* @param id
@@ -89,7 +101,19 @@
setID(id);
}
+ private static void throwExceptionIfSDKTooHigh() {
+ String message =
+ "ScriptC scripts are not supported when targeting an API Level >= 35. Please refer "
+ + "to https://developer.android.com/guide/topics/renderscript/migration-guide "
+ + "for proposed alternatives.";
+ Slog.w(TAG, message);
+ if (CompatChanges.isChangeEnabled(RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID)) {
+ throw new UnsupportedOperationException(message);
+ }
+ }
+
private static synchronized long internalCreate(RenderScript rs, Resources resources, int resourceID) {
+ throwExceptionIfSDKTooHigh();
byte[] pgm;
int pgmLength;
InputStream is = resources.openRawResource(resourceID);
@@ -126,6 +150,7 @@
private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) {
// Log.v(TAG, "Create script for resource = " + resName);
+ throwExceptionIfSDKTooHigh();
return rs.nScriptCCreate(resName, RenderScript.getCachePath(), bitcode, bitcode.length);
}
}
diff --git a/services/Android.bp b/services/Android.bp
index 3a0428e..e4fe581 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -43,7 +43,10 @@
name: "system_optimized_java_defaults",
module_type: "java_defaults",
config_namespace: "ANDROID",
- bool_variables: ["SYSTEM_OPTIMIZE_JAVA"],
+ bool_variables: [
+ "SYSTEM_OPTIMIZE_JAVA",
+ "FULL_SYSTEM_OPTIMIZE_JAVA",
+ ],
properties: ["optimize"],
}
@@ -55,9 +58,11 @@
enabled: true,
// TODO(b/210510433): Enable optimizations after improving
// retracing infra.
+ // See also FULL_SYSTEM_OPTIMIZE_JAVA.
optimize: false,
shrink: true,
ignore_warnings: false,
+ proguard_compatibility: false,
proguard_flags_files: [
"proguard.flags",
// Ensure classes referenced in the framework-res manifest
@@ -77,6 +82,12 @@
},
},
},
+ // Allow form factors to opt-in full system java optimization
+ FULL_SYSTEM_OPTIMIZE_JAVA: {
+ optimize: {
+ optimize: true,
+ },
+ },
},
}
diff --git a/services/companion/java/com/android/server/companion/virtual/OWNERS b/services/companion/java/com/android/server/companion/virtual/OWNERS
index 83143a4..4fe0592 100644
--- a/services/companion/java/com/android/server/companion/virtual/OWNERS
+++ b/services/companion/java/com/android/server/companion/virtual/OWNERS
@@ -1,6 +1,8 @@
+# Bug component: 1171888
+
set noparent
-ogunwale@google.com
-michaelwr@google.com
+marvinramin@google.com
vladokom@google.com
-marvinramin@google.com
\ No newline at end of file
+ogunwale@google.com
+michaelwr@google.com
\ No newline at end of file
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 22f8570..37976b5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -122,6 +122,9 @@
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
"java/com/android/server/policy/EventLogTags.logtags",
+
+ // Java/AIDL sources to be moved out to CrashRecovery module
+ ":services-crashrecovery-sources",
],
libs: [
@@ -130,7 +133,6 @@
"android.hardware.light-V2.0-java",
"android.hardware.gnss-V2-java",
"android.hardware.vibrator-V2-java",
- "android.nfc.flags-aconfig-java",
"app-compat-annotations",
"framework-tethering.stubs.module_lib",
"service-art.stubs.system_server",
@@ -153,7 +155,7 @@
"android.hardware.boot-V1.0-java", // HIDL
"android.hardware.boot-V1.1-java", // HIDL
"android.hardware.boot-V1.2-java", // HIDL
- "android.hardware.boot-V1-java", // AIDL
+ "android.hardware.boot-V1-java", // AIDL
"android.hardware.broadcastradio-V2.0-java", // HIDL
"android.hardware.broadcastradio-V1-java", // AIDL
"android.hardware.health-V1.0-java", // HIDL
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 572e9c2..926d7a4 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -62,6 +62,7 @@
import java.nio.file.attribute.PosixFilePermissions;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -328,9 +329,11 @@
* @param tombstone path to the tombstone
* @param proto whether the tombstone is stored as proto
* @param processName the name of the process corresponding to the tombstone
+ * @param tmpFileLock the lock for reading/writing tmp files
*/
public static void addTombstoneToDropBox(
- Context ctx, File tombstone, boolean proto, String processName) {
+ Context ctx, File tombstone, boolean proto, String processName,
+ ReentrantLock tmpFileLock) {
final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
if (db == null) {
Slog.e(TAG, "Can't log tombstone: DropBoxManager not available");
@@ -351,39 +354,11 @@
// due to rate limiting. Do this by enclosing the proto tombsstone in a
// container proto that has the dropped entry count and the proto tombstone as
// bytes (to avoid the complexity of reading and writing nested protos).
-
- // Read the proto tombstone file as bytes.
- final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath());
-
- final File tombstoneProtoWithHeaders = File.createTempFile(
- tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR);
- Files.setPosixFilePermissions(
- tombstoneProtoWithHeaders.toPath(),
- PosixFilePermissions.fromString("rw-rw----"));
-
- // Write the new proto container proto with headers.
- ParcelFileDescriptor pfd;
+ tmpFileLock.lock();
try {
- pfd = ParcelFileDescriptor.open(tombstoneProtoWithHeaders, MODE_READ_WRITE);
-
- ProtoOutputStream protoStream = new ProtoOutputStream(
- pfd.getFileDescriptor());
- protoStream.write(TombstoneWithHeadersProto.TOMBSTONE, tombstoneBytes);
- protoStream.write(
- TombstoneWithHeadersProto.DROPPED_COUNT,
- rateLimitResult.droppedCountSinceRateLimitActivated());
- protoStream.flush();
-
- // Add the proto to dropbox.
- db.addFile(TAG_TOMBSTONE_PROTO_WITH_HEADERS, tombstoneProtoWithHeaders, 0);
- } catch (FileNotFoundException ex) {
- Slog.e(TAG, "failed to open for write: " + tombstoneProtoWithHeaders, ex);
- throw ex;
+ addAugmentedProtoToDropbox(tombstone, db, rateLimitResult);
} finally {
- // Remove the temporary file.
- if (tombstoneProtoWithHeaders != null) {
- tombstoneProtoWithHeaders.delete();
- }
+ tmpFileLock.unlock();
}
}
} else {
@@ -399,6 +374,44 @@
writeTimestamps(timestamps);
}
+ private static void addAugmentedProtoToDropbox(
+ File tombstone, DropBoxManager db,
+ DropboxRateLimiter.RateLimitResult rateLimitResult) throws IOException {
+ // Read the proto tombstone file as bytes.
+ final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath());
+
+ final File tombstoneProtoWithHeaders = File.createTempFile(
+ tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR);
+ Files.setPosixFilePermissions(
+ tombstoneProtoWithHeaders.toPath(),
+ PosixFilePermissions.fromString("rw-rw----"));
+
+ // Write the new proto container proto with headers.
+ try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+ tombstoneProtoWithHeaders, MODE_READ_WRITE)) {
+ ProtoOutputStream protoStream =
+ new ProtoOutputStream(pfd.getFileDescriptor());
+ protoStream.write(TombstoneWithHeadersProto.TOMBSTONE, tombstoneBytes);
+ protoStream.write(
+ TombstoneWithHeadersProto.DROPPED_COUNT,
+ rateLimitResult.droppedCountSinceRateLimitActivated());
+ protoStream.flush();
+
+ // Add the proto to dropbox.
+ db.addFile(TAG_TOMBSTONE_PROTO_WITH_HEADERS, tombstoneProtoWithHeaders, 0);
+ } catch (FileNotFoundException ex) {
+ Slog.e(TAG, "failed to open for write: " + tombstoneProtoWithHeaders, ex);
+ throw ex;
+ } catch (IOException ex) {
+ Slog.e(TAG, "IO exception during write: " + tombstoneProtoWithHeaders, ex);
+ } finally {
+ // Remove the temporary file and unlock the lock.
+ if (tombstoneProtoWithHeaders != null) {
+ tombstoneProtoWithHeaders.delete();
+ }
+ }
+ }
+
private static void addLastkToDropBox(
DropBoxManager db, HashMap<String, Long> timestamps,
String headers, String footers, String filename, int maxSize,
diff --git a/services/core/java/com/android/server/BrickReceiver.java b/services/core/java/com/android/server/BrickReceiver.java
deleted file mode 100644
index cff3805..0000000
--- a/services/core/java/com/android/server/BrickReceiver.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
-import android.os.SystemService;
-import android.util.Slog;
-
-public class BrickReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Slog.w("BrickReceiver", "!!! BRICKING DEVICE !!!");
- SystemService.start("brick");
- }
-}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index b67e627..e92f043 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -179,6 +179,8 @@
3130 pm_snapshot_stats (build_count|1|1),(reuse_count|1|1),(big_builds|1|1),(short_lived|1|1),(max_build_time|1|3),(cumm_build_time|2|3)
# Snapshot rebuild instance
3131 pm_snapshot_rebuild (build_time|1|3),(lifetime|1|3)
+# Caller information to clear application data
+3132 pm_clear_app_data_caller (pid|1),(uid|1),(package|3)
# ---------------------------
# InputMethodManagerService.java
@@ -218,6 +220,14 @@
35000 auto_brightness_adj (old_lux|5),(old_brightness|5),(new_lux|5),(new_brightness|5)
# ---------------------------
+# Installer.java
+# ---------------------------
+# Caller Information to clear application data
+39000 installer_clear_app_data_caller (pid|1),(uid|1),(package|3),(flags|1)
+# Call stack to clear application data
+39001 installer_clear_app_data_call_stack (method|3),(class|3),(file|3),(line|1)
+
+# ---------------------------
# ConnectivityService.java
# ---------------------------
# Connectivity state changed
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 987507f..3aa275c 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
-# BootReceiver
-per-file BootReceiver.java = gaillard@google.com
+# BootReceiver / Watchdog
+per-file BootReceiver.java,Watchdog.java = gaillard@google.com
# Connectivity / Networking
per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java,VpnManagerService.java = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 0d423d8..2ba3a1d 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -33,7 +33,6 @@
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.IVpnManager;
-import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkStack;
import android.net.UnderlyingNetworkInfo;
@@ -437,16 +436,9 @@
throw new UnsupportedOperationException("Legacy VPN is deprecated");
}
int user = UserHandle.getUserId(mDeps.getCallingUid());
- // Note that if the caller is not system (uid >= Process.FIRST_APPLICATION_UID),
- // the code might not work well since getActiveNetwork might return null if the uid is
- // blocked by NetworkPolicyManagerService.
- final LinkProperties egress = mCm.getLinkProperties(mCm.getActiveNetwork());
- if (egress == null) {
- throw new IllegalStateException("Missing active network connection");
- }
synchronized (mVpns) {
throwIfLockdownEnabled();
- mVpns.get(user).startLegacyVpn(profile, null /* underlying */, egress);
+ mVpns.get(user).startLegacyVpn(profile);
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 62651dd..4bfcb87 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -120,6 +120,7 @@
"/system/bin/mediaserver",
"/system/bin/netd",
"/system/bin/sdcard",
+ "/system/bin/servicemanager",
"/system/bin/surfaceflinger",
"/system/bin/vold",
"media.extractor", // system/bin/mediaextractor
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 6794f75..3280afdf 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -1797,8 +1797,13 @@
mFingerprints);
try {
- dump.write("user_keys", AdbDebuggingManagerProto.USER_KEYS,
- FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
+ File userKeys = new File("/data/misc/adb/adb_keys");
+ if (userKeys.exists()) {
+ dump.write("user_keys", AdbDebuggingManagerProto.USER_KEYS,
+ FileUtils.readTextFile(userKeys, 0, null));
+ } else {
+ Slog.i(TAG, "No user keys on this device");
+ }
} catch (IOException e) {
Slog.i(TAG, "Cannot read user keys", e);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e74371e..6c840a5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1690,19 +1690,23 @@
private static final int INDEX_NATIVE_PSS = 0;
private static final int INDEX_NATIVE_SWAP_PSS = 1;
private static final int INDEX_NATIVE_RSS = 2;
- private static final int INDEX_DALVIK_PSS = 3;
- private static final int INDEX_DALVIK_SWAP_PSS = 4;
- private static final int INDEX_DALVIK_RSS = 5;
- private static final int INDEX_OTHER_PSS = 6;
- private static final int INDEX_OTHER_SWAP_PSS = 7;
- private static final int INDEX_OTHER_RSS = 8;
- private static final int INDEX_TOTAL_PSS = 9;
- private static final int INDEX_TOTAL_SWAP_PSS = 10;
- private static final int INDEX_TOTAL_RSS = 11;
- private static final int INDEX_TOTAL_NATIVE_PSS = 12;
- private static final int INDEX_TOTAL_MEMTRACK_GRAPHICS = 13;
- private static final int INDEX_TOTAL_MEMTRACK_GL = 14;
- private static final int INDEX_LAST = 15;
+ private static final int INDEX_NATIVE_PRIVATE_DIRTY = 3;
+ private static final int INDEX_DALVIK_PSS = 4;
+ private static final int INDEX_DALVIK_SWAP_PSS = 5;
+ private static final int INDEX_DALVIK_RSS = 6;
+ private static final int INDEX_DALVIK_PRIVATE_DIRTY = 7;
+ private static final int INDEX_OTHER_PSS = 8;
+ private static final int INDEX_OTHER_SWAP_PSS = 9;
+ private static final int INDEX_OTHER_RSS = 10;
+ private static final int INDEX_OTHER_PRIVATE_DIRTY = 11;
+ private static final int INDEX_TOTAL_PSS = 12;
+ private static final int INDEX_TOTAL_SWAP_PSS = 13;
+ private static final int INDEX_TOTAL_RSS = 14;
+ private static final int INDEX_TOTAL_PRIVATE_DIRTY = 15;
+ private static final int INDEX_TOTAL_NATIVE_PSS = 16;
+ private static final int INDEX_TOTAL_MEMTRACK_GRAPHICS = 17;
+ private static final int INDEX_TOTAL_MEMTRACK_GL = 18;
+ private static final int INDEX_LAST = 19;
/**
* Used to notify activity lifecycle events.
@@ -3468,6 +3472,7 @@
enforceNotIsolatedCaller("clearApplicationUserData");
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
+ EventLog.writeEvent(EventLogTags.AM_CLEAR_APP_DATA_CALLER, pid, uid, packageName);
final int resolvedUserId = mUserController.handleIncomingUser(pid, uid, userId, false,
ALLOW_FULL_ONLY, "clearApplicationUserData", null);
@@ -11354,13 +11359,14 @@
final long pss;
final long swapPss;
final long mRss;
+ final long mPrivateDirty;
final int id; // pid
final int userId;
final boolean hasActivities;
ArrayList<MemItem> subitems;
- MemItem(String label, String shortLabel, long pss, long swapPss, long rss, int id,
- @UserIdInt int userId,
+ MemItem(String label, String shortLabel, long pss, long swapPss, long rss,
+ long privateDirty, int id, @UserIdInt int userId,
boolean hasActivities) {
this.isProc = true;
this.label = label;
@@ -11368,18 +11374,21 @@
this.pss = pss;
this.swapPss = swapPss;
this.mRss = rss;
+ this.mPrivateDirty = privateDirty;
this.id = id;
this.userId = userId;
this.hasActivities = hasActivities;
}
- MemItem(String label, String shortLabel, long pss, long swapPss, long rss, int id) {
+ MemItem(String label, String shortLabel, long pss, long swapPss, long rss,
+ long privateDirty, int id) {
this.isProc = false;
this.label = label;
this.shortLabel = shortLabel;
this.pss = pss;
this.swapPss = swapPss;
this.mRss = rss;
+ this.mPrivateDirty = privateDirty;
this.id = id;
this.userId = UserHandle.USER_SYSTEM;
this.hasActivities = false;
@@ -11404,7 +11413,7 @@
static final void dumpMemItems(PrintWriter pw, String prefix, String tag,
ArrayList<MemItem> items, boolean sort, boolean isCompact, boolean dumpPss,
- boolean dumpSwapPss) {
+ boolean dumpSwapPss, boolean dumpPrivateDirty) {
if (sort && !isCompact) {
sortMemItems(items, dumpPss);
}
@@ -11412,14 +11421,18 @@
for (int i=0; i<items.size(); i++) {
MemItem mi = items.get(i);
if (!isCompact) {
- if (dumpPss && dumpSwapPss) {
- pw.printf("%s%s: %-60s (%s in swap)\n", prefix, stringifyKBSize(mi.pss),
- mi.label, stringifyKBSize(mi.swapPss));
- } else {
- pw.printf("%s%s: %s%s\n", prefix, stringifyKBSize(dumpPss ? mi.pss : mi.mRss),
+ pw.printf("%s%s: %s%s\n", prefix, stringifyKBSize(dumpPss ? mi.pss : mi.mRss),
mi.label,
mi.userId != UserHandle.USER_SYSTEM ? " (user " + mi.userId + ")" : "");
+ if (dumpPss && dumpSwapPss) {
+ pw.printf("(%s in swap%s", stringifyKBSize(mi.swapPss),
+ dumpPrivateDirty ? ", " : ")");
}
+ if (dumpPrivateDirty) {
+ pw.printf("%s%s private dirty)", dumpSwapPss ? "" : "(",
+ stringifyKBSize(mi.mPrivateDirty));
+ }
+ pw.printf("\n");
} else if (mi.isProc) {
pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel);
pw.print(","); pw.print(mi.id); pw.print(",");
@@ -11433,7 +11446,7 @@
}
if (mi.subitems != null) {
dumpMemItems(pw, prefix + " ", mi.shortLabel, mi.subitems,
- true, isCompact, dumpPss, dumpSwapPss);
+ true, isCompact, dumpPss, dumpSwapPss, dumpPrivateDirty);
}
}
}
@@ -11601,6 +11614,7 @@
boolean isCheckinRequest;
boolean dumpSwapPss;
boolean dumpProto;
+ boolean mDumpPrivateDirty;
}
@NeverCompile // Avoid size overhead of debugging code.
@@ -11619,6 +11633,7 @@
opts.isCheckinRequest = false;
opts.dumpSwapPss = false;
opts.dumpProto = asProto;
+ opts.mDumpPrivateDirty = false;
int opti = 0;
while (opti < args.length) {
@@ -11641,6 +11656,8 @@
opts.dumpSummaryOnly = true;
} else if ("-S".equals(opt)) {
opts.dumpSwapPss = true;
+ } else if ("-p".equals(opt)) {
+ opts.mDumpPrivateDirty = true;
} else if ("--unreachable".equals(opt)) {
opts.dumpUnreachable = true;
} else if ("--oom".equals(opt)) {
@@ -11661,6 +11678,7 @@
pw.println(" -c: dump in a compact machine-parseable representation.");
pw.println(" -s: dump only summary of application memory usage.");
pw.println(" -S: dump also SwapPss.");
+ pw.println(" -p: dump also private dirty memory usage.");
pw.println(" --oom: only show processes organized by oom adj.");
pw.println(" --local: only collect details locally, don't call process.");
pw.println(" --package: interpret process arg as package, dumping all");
@@ -11779,14 +11797,18 @@
EmptyArray.LONG;
long[] dalvikSubitemRss = opts.dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
EmptyArray.LONG;
+ long[] dalvikSubitemPrivateDirty = opts.dumpDalvik
+ ? new long[Debug.MemoryInfo.NUM_DVK_STATS] : EmptyArray.LONG;
long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
long[] miscSwapPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
long[] miscRss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
+ long[] miscPrivateDirty = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
long[] memtrackTmp = new long[4];
long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
long oomSwapPss[] = new long[DUMP_MEM_OOM_LABEL.length];
long[] oomRss = new long[DUMP_MEM_OOM_LABEL.length];
+ long[] oomPrivateDirty = new long[DUMP_MEM_OOM_LABEL.length];
ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
new ArrayList[DUMP_MEM_OOM_LABEL.length];
@@ -11882,6 +11904,7 @@
final long myTotalUss = mi.getTotalUss();
final long myTotalRss = mi.getTotalRss();
final long myTotalSwapPss = mi.getTotalSwappedOutPss();
+ final long myTotalPrivateDirty = mi.getTotalPrivateDirty();
synchronized (mProcLock) {
if (r.getThread() != null && oomAdj == r.mState.getSetAdjWithServices()) {
@@ -11895,29 +11918,36 @@
ss[INDEX_TOTAL_PSS] += myTotalPss;
ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
ss[INDEX_TOTAL_RSS] += myTotalRss;
+ ss[INDEX_TOTAL_PRIVATE_DIRTY] += myTotalPrivateDirty;
ss[INDEX_TOTAL_MEMTRACK_GRAPHICS] += memtrackGraphics;
ss[INDEX_TOTAL_MEMTRACK_GL] += memtrackGl;
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
(hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
- myTotalSwapPss, myTotalRss, pid, r.userId, hasActivities);
+ myTotalSwapPss, myTotalRss, myTotalPrivateDirty,
+ pid, r.userId, hasActivities);
procMems.add(pssItem);
procMemsMap.put(pid, pssItem);
ss[INDEX_NATIVE_PSS] += mi.nativePss;
ss[INDEX_NATIVE_SWAP_PSS] += mi.nativeSwappedOutPss;
ss[INDEX_NATIVE_RSS] += mi.nativeRss;
+ ss[INDEX_NATIVE_PRIVATE_DIRTY] += mi.nativePrivateDirty;
ss[INDEX_DALVIK_PSS] += mi.dalvikPss;
ss[INDEX_DALVIK_SWAP_PSS] += mi.dalvikSwappedOutPss;
ss[INDEX_DALVIK_RSS] += mi.dalvikRss;
+ ss[INDEX_DALVIK_PRIVATE_DIRTY] += mi.dalvikPrivateDirty;
for (int j=0; j<dalvikSubitemPss.length; j++) {
dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemSwapPss[j] +=
mi.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+ dalvikSubitemPrivateDirty[j] +=
+ mi.getOtherPrivateDirty(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemRss[j] += mi.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
}
ss[INDEX_OTHER_PSS] += mi.otherPss;
ss[INDEX_OTHER_RSS] += mi.otherRss;
ss[INDEX_OTHER_SWAP_PSS] += mi.otherSwappedOutPss;
+ ss[INDEX_OTHER_PRIVATE_DIRTY] += mi.otherPrivateDirty;
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
long mem = mi.getOtherPss(j);
miscPss[j] += mem;
@@ -11925,6 +11955,9 @@
mem = mi.getOtherSwappedOutPss(j);
miscSwapPss[j] += mem;
ss[INDEX_OTHER_SWAP_PSS] -= mem;
+ mem = mi.getOtherPrivateDirty(j);
+ miscPrivateDirty[j] += mem;
+ ss[INDEX_OTHER_PRIVATE_DIRTY] -= mem;
mem = mi.getOtherRss(j);
miscRss[j] += mem;
ss[INDEX_OTHER_RSS] -= mem;
@@ -11941,6 +11974,7 @@
&& oomAdj < DUMP_MEM_OOM_ADJ[oomIndex + 1])) {
oomPss[oomIndex] += myTotalPss;
oomSwapPss[oomIndex] += myTotalSwapPss;
+ oomPrivateDirty[oomIndex] += myTotalPrivateDirty;
if (oomProcs[oomIndex] == null) {
oomProcs[oomIndex] = new ArrayList<MemItem>();
}
@@ -11987,6 +12021,7 @@
final long myTotalPss = info.getTotalPss();
final long myTotalSwapPss = info.getTotalSwappedOutPss();
final long myTotalRss = info.getTotalRss();
+ final long myTotalPrivateDirty = info.getTotalPrivateDirty();
ss[INDEX_TOTAL_PSS] += myTotalPss;
ss[INDEX_TOTAL_SWAP_PSS] += myTotalSwapPss;
ss[INDEX_TOTAL_RSS] += myTotalRss;
@@ -11996,15 +12031,17 @@
MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss,
- st.pid, UserHandle.getUserId(st.uid), false);
+ myTotalPrivateDirty, st.pid, UserHandle.getUserId(st.uid), false);
procMems.add(pssItem);
ss[INDEX_NATIVE_PSS] += info.nativePss;
ss[INDEX_NATIVE_SWAP_PSS] += info.nativeSwappedOutPss;
ss[INDEX_NATIVE_RSS] += info.nativeRss;
+ ss[INDEX_NATIVE_PRIVATE_DIRTY] += info.nativePrivateDirty;
ss[INDEX_DALVIK_PSS] += info.dalvikPss;
ss[INDEX_DALVIK_SWAP_PSS] += info.dalvikSwappedOutPss;
ss[INDEX_DALVIK_RSS] += info.dalvikRss;
+ ss[INDEX_DALVIK_PRIVATE_DIRTY] += info.dalvikPrivateDirty;
for (int j = 0; j < dalvikSubitemPss.length; j++) {
dalvikSubitemPss[j] += info.getOtherPss(
Debug.MemoryInfo.NUM_OTHER_STATS + j);
@@ -12012,10 +12049,13 @@
info.getOtherSwappedOutPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
dalvikSubitemRss[j] += info.getOtherRss(Debug.MemoryInfo.NUM_OTHER_STATS
+ j);
+ dalvikSubitemPrivateDirty[j] +=
+ info.getOtherPrivateDirty(Debug.MemoryInfo.NUM_OTHER_STATS + j);
}
ss[INDEX_OTHER_PSS] += info.otherPss;
ss[INDEX_OTHER_SWAP_PSS] += info.otherSwappedOutPss;
ss[INDEX_OTHER_RSS] += info.otherRss;
+ ss[INDEX_OTHER_PRIVATE_DIRTY] += info.otherPrivateDirty;
for (int j = 0; j < Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
long mem = info.getOtherPss(j);
miscPss[j] += mem;
@@ -12026,6 +12066,9 @@
mem = info.getOtherRss(j);
miscRss[j] += mem;
ss[INDEX_OTHER_RSS] -= mem;
+ mem = info.getOtherPrivateDirty(j);
+ miscPrivateDirty[j] += mem;
+ ss[INDEX_OTHER_PRIVATE_DIRTY] -= mem;
}
oomPss[0] += myTotalPss;
oomSwapPss[0] += myTotalSwapPss;
@@ -12034,21 +12077,26 @@
}
oomProcs[0].add(pssItem);
oomRss[0] += myTotalRss;
+ oomPrivateDirty[0] += myTotalPrivateDirty;
}
});
ArrayList<MemItem> catMems = new ArrayList<MemItem>();
catMems.add(new MemItem("Native", "Native",
- ss[INDEX_NATIVE_PSS], ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], -1));
+ ss[INDEX_NATIVE_PSS], ss[INDEX_NATIVE_SWAP_PSS],
+ ss[INDEX_NATIVE_RSS], ss[INDEX_NATIVE_PRIVATE_DIRTY], -1));
final int dalvikId = -2;
catMems.add(new MemItem("Dalvik", "Dalvik", ss[INDEX_DALVIK_PSS],
- ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], dalvikId));
+ ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS],
+ ss[INDEX_DALVIK_PRIVATE_DIRTY], dalvikId));
catMems.add(new MemItem("Unknown", "Unknown", ss[INDEX_OTHER_PSS],
- ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], -3));
+ ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS],
+ ss[INDEX_OTHER_PRIVATE_DIRTY], -3));
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
String label = Debug.MemoryInfo.getOtherLabel(j);
- catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j], j));
+ catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j],
+ miscPrivateDirty[j], j));
}
if (dalvikSubitemPss.length > 0) {
// Add dalvik subitems.
@@ -12074,7 +12122,8 @@
final String name = Debug.MemoryInfo.getOtherLabel(
Debug.MemoryInfo.NUM_OTHER_STATS + j);
memItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j],
- dalvikSubitemSwapPss[j], dalvikSubitemRss[j], j));
+ dalvikSubitemSwapPss[j], dalvikSubitemRss[j],
+ dalvikSubitemPrivateDirty[j], j));
}
}
}
@@ -12085,7 +12134,7 @@
String label = opts.isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
: DUMP_MEM_OOM_LABEL[j];
MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], oomRss[j],
- DUMP_MEM_OOM_ADJ[j]);
+ oomPrivateDirty[j], DUMP_MEM_OOM_ADJ[j]);
item.subitems = oomProcs[j];
oomMems.add(item);
}
@@ -12096,33 +12145,34 @@
if (!brief && !opts.oomOnly && !opts.isCompact) {
pw.println();
pw.println("Total RSS by process:");
- dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, false, false);
+ dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, false, false, false);
pw.println();
}
if (!opts.isCompact) {
pw.println("Total RSS by OOM adjustment:");
}
- dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, false, false);
+ dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, false, false, false);
if (!brief && !opts.oomOnly) {
PrintWriter out = categoryPw != null ? categoryPw : pw;
if (!opts.isCompact) {
out.println();
out.println("Total RSS by category:");
}
- dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, false, false);
+ dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, false, false, false);
}
opts.dumpSwapPss = opts.dumpSwapPss && hasSwapPss && ss[INDEX_TOTAL_SWAP_PSS] != 0;
if (!brief && !opts.oomOnly && !opts.isCompact) {
pw.println();
pw.println("Total PSS by process:");
dumpMemItems(pw, " ", "proc", procMems, true, opts.isCompact, true,
- opts.dumpSwapPss);
+ opts.dumpSwapPss, opts.mDumpPrivateDirty);
pw.println();
}
if (!opts.isCompact) {
pw.println("Total PSS by OOM adjustment:");
}
- dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, true, opts.dumpSwapPss);
+ dumpMemItems(pw, " ", "oom", oomMems, false, opts.isCompact, true, opts.dumpSwapPss,
+ opts.mDumpPrivateDirty);
if (!brief && !opts.oomOnly) {
PrintWriter out = categoryPw != null ? categoryPw : pw;
if (!opts.isCompact) {
@@ -12130,7 +12180,7 @@
out.println("Total PSS by category:");
}
dumpMemItems(out, " ", "cat", catMems, true, opts.isCompact, true,
- opts.dumpSwapPss);
+ opts.dumpSwapPss, opts.mDumpPrivateDirty);
}
if (!opts.isCompact) {
pw.println();
@@ -12532,7 +12582,7 @@
ss[INDEX_TOTAL_RSS] += myTotalRss;
MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
(hasActivities ? " / activities)" : ")"), r.processName, myTotalPss,
- myTotalSwapPss, myTotalRss, pid, r.userId, hasActivities);
+ myTotalSwapPss, myTotalRss, 0, pid, r.userId, hasActivities);
procMems.add(pssItem);
procMemsMap.put(pid, pssItem);
@@ -12619,7 +12669,7 @@
ss[INDEX_TOTAL_NATIVE_PSS] += myTotalPss;
MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
- st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss,
+ st.name, myTotalPss, info.getSummaryTotalSwapPss(), myTotalRss, 0,
st.pid, UserHandle.getUserId(st.uid), false);
procMems.add(pssItem);
@@ -12664,15 +12714,16 @@
ArrayList<MemItem> catMems = new ArrayList<MemItem>();
catMems.add(new MemItem("Native", "Native", ss[INDEX_NATIVE_PSS],
- ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], -1));
+ ss[INDEX_NATIVE_SWAP_PSS], ss[INDEX_NATIVE_RSS], 0, -1));
final int dalvikId = -2;
catMems.add(new MemItem("Dalvik", "Dalvik", ss[INDEX_DALVIK_PSS],
- ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], dalvikId));
+ ss[INDEX_DALVIK_SWAP_PSS], ss[INDEX_DALVIK_RSS], 0, dalvikId));
catMems.add(new MemItem("Unknown", "Unknown", ss[INDEX_OTHER_PSS],
- ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], -3));
+ ss[INDEX_OTHER_SWAP_PSS], ss[INDEX_OTHER_RSS], 0, -3));
for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
String label = Debug.MemoryInfo.getOtherLabel(j);
- catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], miscRss[j], j));
+ catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j],
+ miscRss[j], 0, j));
}
if (dalvikSubitemPss.length > 0) {
// Add dalvik subitems.
@@ -12698,7 +12749,7 @@
final String name = Debug.MemoryInfo.getOtherLabel(
Debug.MemoryInfo.NUM_OTHER_STATS + j);
memItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j],
- dalvikSubitemSwapPss[j], dalvikSubitemRss[j], j));
+ dalvikSubitemSwapPss[j], dalvikSubitemRss[j], 0, j));
}
}
}
@@ -12708,7 +12759,7 @@
if (oomPss[j] != 0) {
String label = opts.isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
: DUMP_MEM_OOM_LABEL[j];
- MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], oomRss[j],
+ MemItem item = new MemItem(label, label, oomPss[j], oomSwapPss[j], oomRss[j], 0,
DUMP_MEM_OOM_ADJ[j]);
item.subitems = oomProcs[j];
oomMems.add(item);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 2e45da3..469582d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1174,7 +1174,7 @@
synchronized (mInternal.mOomAdjuster.mCachedAppOptimizer.mFreezerLock) {
app.mOptRecord.setFreezeSticky(isSticky);
mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppInternalLSP(app, 0,
- false);
+ true);
}
}
}
@@ -4122,7 +4122,7 @@
pw.println(" -D: enable debugging");
pw.println(" --suspend: debugged app suspend threads at startup (only with -D)");
pw.println(" -N: enable native debugging");
- pw.println(" -W: wait for launch to complete");
+ pw.println(" -W: wait for launch to complete (initial display)");
pw.println(" --start-profiler <FILE>: start profiler and send results to <FILE>");
pw.println(" --sampling INTERVAL: use sample profiling with INTERVAL microseconds");
pw.println(" between samples (use with --start-profiler)");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index dc6f858..7dac241 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.Manifest.permission.BATTERY_STATS;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.DEVICE_POWER;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.POWER_SAVER;
@@ -103,6 +102,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ParseUtils;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
@@ -426,7 +426,12 @@
ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
try {
- nms.registerObserver(mActivityChangeObserver);
+ if (!SdkLevel.isAtLeastV()) {
+ // On V+ devices, ConnectivityService calls BatteryStats API to update
+ // RadioPowerState change. So BatteryStatsService registers the callback only on
+ // pre V devices.
+ nms.registerObserver(mActivityChangeObserver);
+ }
cm.registerDefaultNetworkCallback(mNetworkCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Could not register INetworkManagement event observer " + e);
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 0fcec6f..c26fd5d 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -258,13 +258,6 @@
@Nullable
public BroadcastRecord enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record,
int recordIndex, @NonNull BroadcastConsumer deferredStatesApplyConsumer) {
- // When updateDeferredStates() has already applied a deferred state to
- // all pending items, apply to this new broadcast too
- if (mLastDeferredStates && record.deferUntilActive
- && (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) {
- deferredStatesApplyConsumer.accept(record, recordIndex);
- }
-
// Ignore FLAG_RECEIVER_REPLACE_PENDING if the sender specified the policy using the
// BroadcastOptions delivery group APIs.
if (record.isReplacePending()
@@ -287,6 +280,13 @@
// with implicit responsiveness expectations.
getQueueForBroadcast(record).addLast(newBroadcastArgs);
onBroadcastEnqueued(record, recordIndex);
+
+ // When updateDeferredStates() has already applied a deferred state to
+ // all pending items, apply to this new broadcast too
+ if (mLastDeferredStates && shouldBeDeferred()
+ && (record.getDeliveryState(recordIndex) == BroadcastRecord.DELIVERY_PENDING)) {
+ deferredStatesApplyConsumer.accept(record, recordIndex);
+ }
return null;
}
@@ -1144,9 +1144,6 @@
} else if (mProcessPersistent) {
mRunnableAt = runnableAt + constants.DELAY_PERSISTENT_PROC_MILLIS;
mRunnableAtReason = REASON_PERSISTENT;
- } else if (UserHandle.isCore(uid)) {
- mRunnableAt = runnableAt;
- mRunnableAtReason = REASON_CORE_UID;
} else if (mCountOrdered > 0) {
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_CONTAINS_ORDERED;
@@ -1193,6 +1190,9 @@
// is already cached, they'll be deferred on the line above
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
+ } else if (UserHandle.isCore(uid)) {
+ mRunnableAt = runnableAt;
+ mRunnableAtReason = REASON_CORE_UID;
} else {
mRunnableAt = runnableAt + constants.DELAY_NORMAL_MILLIS;
mRunnableAtReason = REASON_NORMAL;
@@ -1221,32 +1221,45 @@
}
/**
- * Update {@link BroadcastRecord.DELIVERY_DEFERRED} states of all our
+ * Update {@link BroadcastRecord#DELIVERY_DEFERRED} states of all our
* pending broadcasts, when needed.
*/
void updateDeferredStates(@NonNull BroadcastConsumer applyConsumer,
@NonNull BroadcastConsumer clearConsumer) {
// When all we have pending is deferred broadcasts, and we're cached,
// then we want everything to be marked deferred
- final boolean wantDeferredStates = (mCountDeferred > 0)
- && (mCountDeferred == mCountEnqueued) && mProcessFreezable;
+ final boolean wantDeferredStates = shouldBeDeferred();
if (mLastDeferredStates != wantDeferredStates) {
mLastDeferredStates = wantDeferredStates;
if (wantDeferredStates) {
forEachMatchingBroadcast((r, i) -> {
- return r.deferUntilActive
- && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_PENDING);
+ return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_PENDING);
}, applyConsumer, false);
} else {
forEachMatchingBroadcast((r, i) -> {
- return r.deferUntilActive
- && (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
+ return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
}, clearConsumer, false);
}
}
}
+ void clearDeferredStates(@NonNull BroadcastConsumer clearConsumer) {
+ if (mLastDeferredStates) {
+ mLastDeferredStates = false;
+ forEachMatchingBroadcast((r, i) -> {
+ return (r.getDeliveryState(i) == BroadcastRecord.DELIVERY_DEFERRED);
+ }, clearConsumer, false);
+ }
+ }
+
+ @VisibleForTesting
+ boolean shouldBeDeferred() {
+ if (mRunnableAtInvalidated) updateRunnableAt();
+ return mRunnableAtReason == REASON_CACHED
+ || mRunnableAtReason == REASON_CACHED_INFINITE_DEFER;
+ }
+
/**
* Check overall health, confirming things are in a reasonable state and
* that we're not wedged.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index f420619..abec890 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -249,6 +249,7 @@
private static final int MSG_CHECK_HEALTH = 5;
private static final int MSG_CHECK_PENDING_COLD_START_VALIDITY = 6;
private static final int MSG_PROCESS_FREEZABLE_CHANGED = 7;
+ private static final int MSG_UID_STATE_CHANGED = 8;
private void enqueueUpdateRunningList() {
mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
@@ -295,6 +296,19 @@
}
return true;
}
+ case MSG_UID_STATE_CHANGED: {
+ final int uid = (int) msg.obj;
+ final int procState = msg.arg1;
+ synchronized (mService) {
+ if (procState == ActivityManager.PROCESS_STATE_TOP) {
+ mUidForeground.put(uid, true);
+ } else {
+ mUidForeground.delete(uid);
+ }
+ refreshProcessQueuesLocked(uid);
+ }
+ return true;
+ }
}
return false;
};
@@ -450,6 +464,10 @@
break;
}
+ // Clear the deferred state of broadcasts in this queue as we are just about to
+ // deliver broadcasts to this process.
+ queue.clearDeferredStates(mBroadcastConsumerDeferClear);
+
// We might not have heard about a newly running process yet, so
// consider refreshing if we think we're cold
updateWarmProcess(queue);
@@ -672,7 +690,7 @@
@Override
public void onProcessFreezableChangedLocked(@NonNull ProcessRecord app) {
mLocalHandler.removeMessages(MSG_PROCESS_FREEZABLE_CHANGED, app);
- mLocalHandler.sendMessage(mHandler.obtainMessage(MSG_PROCESS_FREEZABLE_CHANGED, app));
+ mLocalHandler.obtainMessage(MSG_PROCESS_FREEZABLE_CHANGED, app).sendToTarget();
}
@Override
@@ -1510,12 +1528,14 @@
r.resultExtras = null;
};
- private final BroadcastConsumer mBroadcastConsumerDeferApply = (r, i) -> {
+ @VisibleForTesting
+ final BroadcastConsumer mBroadcastConsumerDeferApply = (r, i) -> {
setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_DEFERRED,
"mBroadcastConsumerDeferApply");
};
- private final BroadcastConsumer mBroadcastConsumerDeferClear = (r, i) -> {
+ @VisibleForTesting
+ final BroadcastConsumer mBroadcastConsumerDeferClear = (r, i) -> {
setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_PENDING,
"mBroadcastConsumerDeferClear");
};
@@ -1601,14 +1621,9 @@
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq,
int capability) {
- synchronized (mService) {
- if (procState == ActivityManager.PROCESS_STATE_TOP) {
- mUidForeground.put(uid, true);
- } else {
- mUidForeground.delete(uid);
- }
- refreshProcessQueuesLocked(uid);
- }
+ mLocalHandler.removeMessages(MSG_UID_STATE_CHANGED, uid);
+ mLocalHandler.obtainMessage(MSG_UID_STATE_CHANGED, procState, 0, uid)
+ .sendToTarget();
}
}, ActivityManager.UID_OBSERVER_PROCSTATE,
ActivityManager.PROCESS_STATE_TOP, "android");
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 9e9db6a..2aed847 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -129,3 +129,6 @@
# Intent Sender redirect for UserHandle.USER_CURRENT
30110 am_intent_sender_redirect_user (userId|1|5)
+
+# Caller information to clear application data
+30120 am_clear_app_data_caller (pid|1),(uid|1),(package|3)
diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java
index 4380b42..507fd9e 100644
--- a/services/core/java/com/android/server/am/LmkdStatsReporter.java
+++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java
@@ -44,6 +44,7 @@
private static final int DIRECT_RECL_AND_THRASHING = 5;
private static final int LOW_MEM_AND_SWAP_UTIL = 6;
private static final int LOW_FILECACHE_AFTER_THRASHING = 7;
+ private static final int LOW_MEM = 8;
/**
* Processes the LMK_KILL_OCCURRED packet data
@@ -106,6 +107,8 @@
return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP_UTIL;
case LOW_FILECACHE_AFTER_THRASHING:
return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_FILECACHE_AFTER_THRASHING;
+ case LOW_MEM:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM;
default:
return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__UNKNOWN;
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index e4a5a3e..2358436 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -399,59 +399,6 @@
}
}
- public enum FrameRate {
- FPS_DEFAULT(0),
- FPS_30(30),
- FPS_36(36),
- FPS_40(40),
- FPS_45(45),
- FPS_48(48),
- FPS_60(60),
- FPS_72(72),
- FPS_90(90),
- FPS_120(120),
- FPS_144(144),
- FPS_INVALID(-1);
-
- public final int fps;
-
- FrameRate(int fps) {
- this.fps = fps;
- }
- }
-
- // Turn the raw string to the corresponding fps int.
- // Return 0 when disabling, -1 for invalid fps.
- static int getFpsInt(String raw) {
- // TODO(b/243448953): make sure this translates to proper values based on current display
- switch (raw) {
- case "30":
- return FrameRate.FPS_30.fps;
- case "36":
- return FrameRate.FPS_36.fps;
- case "40":
- return FrameRate.FPS_40.fps;
- case "45":
- return FrameRate.FPS_45.fps;
- case "48":
- return FrameRate.FPS_48.fps;
- case "60":
- return FrameRate.FPS_60.fps;
- case "72":
- return FrameRate.FPS_72.fps;
- case "90":
- return FrameRate.FPS_90.fps;
- case "120":
- return FrameRate.FPS_120.fps;
- case "144":
- return FrameRate.FPS_144.fps;
- case "disable":
- case "":
- return FrameRate.FPS_DEFAULT.fps;
- }
- return FrameRate.FPS_INVALID.fps;
- }
-
/**
* Called by games to communicate the current state to the platform.
*
@@ -699,7 +646,12 @@
}
public synchronized int getFps() {
- return GameManagerService.getFpsInt(mFps);
+ try {
+ final int fpsInt = Integer.parseInt(mFps);
+ return fpsInt;
+ } catch (NumberFormatException e) {
+ return 0;
+ }
}
synchronized String getFpsStr() {
@@ -739,7 +691,12 @@
}
android.app.GameModeConfiguration toPublicGameModeConfig() {
- int fpsOverride = getFpsInt(mFps);
+ int fpsOverride;
+ try {
+ fpsOverride = Integer.parseInt(mFps);
+ } catch (NumberFormatException e) {
+ fpsOverride = 0;
+ }
// TODO(b/243448953): match to proper value in case of display change?
fpsOverride = fpsOverride > 0 ? fpsOverride
: android.app.GameModeConfiguration.FPS_OVERRIDE_NONE;
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 00ff489..ab57c4f 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -241,8 +241,10 @@
case "--fps":
if (fpsStr == null) {
fpsStr = getNextArgRequired();
- if (fpsStr != null && GameManagerService.getFpsInt(fpsStr) == -1) {
- pw.println("Invalid frame rate '" + fpsStr + "'");
+ try {
+ Integer.parseInt(fpsStr);
+ } catch (NumberFormatException e) {
+ pw.println("Invalid frame rate: '" + fpsStr + "'");
return -1;
}
} else {
@@ -375,8 +377,8 @@
pw.println(" --downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65");
pw.println(" |0.7|0.75|0.8|0.85|0.9|disable]: Set app to run at the");
pw.println(" specified scaling ratio.");
- pw.println(" --fps [30|45|60|90|120|disable]: Set app to run at the specified fps,");
- pw.println(" if supported.");
+ pw.println(" --fps: Integer value to set app to run at the specified fps,");
+ pw.println(" if supported. 0 to disable.");
pw.println(" reset [--mode [2|3|performance|battery] --user <USER_ID>] <PACKAGE_NAME>");
pw.println(" Resets the game mode of the app to device configuration.");
pw.println(" This should only be used to reset any override to non custom game mode");
diff --git a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
index 4dfd9b0..f0dd23c 100644
--- a/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsCheckingServiceImpl.java
@@ -221,20 +221,24 @@
synchronized (mLock) {
SparseIntArray opModes = mUidModes.get(uid, null);
if (opModes == null) {
- if (mode != defaultMode) {
- opModes = new SparseIntArray();
- mUidModes.put(uid, opModes);
- opModes.put(op, mode);
- scheduleWriteLocked();
+ if (mode == defaultMode) {
+ return false;
}
+ opModes = new SparseIntArray();
+ mUidModes.put(uid, opModes);
+ opModes.put(op, mode);
+ scheduleWriteLocked();
} else {
- if (opModes.indexOfKey(op) >= 0 && opModes.get(op) == mode) {
+ if (opModes.indexOfKey(op) >= 0) {
+ if (opModes.get(op) == mode) {
+ return false;
+ }
+ } else if (mode == defaultMode) {
return false;
}
if (mode == defaultMode) {
opModes.delete(op);
if (opModes.size() <= 0) {
- opModes = null;
mUidModes.delete(uid);
}
} else {
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 969dd60..f465867 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -37,10 +37,9 @@
import android.media.ISpatializerHeadTrackingModeCallback;
import android.media.ISpatializerOutputCallback;
import android.media.MediaMetrics;
-import android.media.SpatializationLevel;
-import android.media.SpatializationMode;
import android.media.Spatializer;
-import android.media.SpatializerHeadTrackingMode;
+import android.media.audio.common.HeadTracking;
+import android.media.audio.common.Spatialization;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.text.TextUtils;
@@ -82,22 +81,22 @@
/*package*/ static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(14) {
{
- append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_WIRED_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
- append(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, SpatializationMode.SPATIALIZER_BINAURAL);
+ append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_WIRED_HEADSET, Spatialization.Mode.BINAURAL);
+ append(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, Spatialization.Mode.BINAURAL);
// assumption for A2DP: mostly headsets
- append(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, SpatializationMode.SPATIALIZER_BINAURAL);
- append(AudioDeviceInfo.TYPE_DOCK, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_USB_ACCESSORY, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_USB_DEVICE, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_USB_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
- append(AudioDeviceInfo.TYPE_LINE_ANALOG, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_LINE_DIGITAL, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_AUX_LINE, SpatializationMode.SPATIALIZER_TRANSAURAL);
- append(AudioDeviceInfo.TYPE_BLE_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL);
- append(AudioDeviceInfo.TYPE_BLE_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, Spatialization.Mode.BINAURAL);
+ append(AudioDeviceInfo.TYPE_DOCK, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_USB_ACCESSORY, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_USB_DEVICE, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_USB_HEADSET, Spatialization.Mode.BINAURAL);
+ append(AudioDeviceInfo.TYPE_LINE_ANALOG, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_LINE_DIGITAL, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_AUX_LINE, Spatialization.Mode.TRANSAURAL);
+ append(AudioDeviceInfo.TYPE_BLE_HEADSET, Spatialization.Mode.BINAURAL);
+ append(AudioDeviceInfo.TYPE_BLE_SPEAKER, Spatialization.Mode.TRANSAURAL);
// assumption that BLE broadcast would be mostly consumed on headsets
- append(AudioDeviceInfo.TYPE_BLE_BROADCAST, SpatializationMode.SPATIALIZER_BINAURAL);
+ append(AudioDeviceInfo.TYPE_BLE_BROADCAST, Spatialization.Mode.BINAURAL);
}
};
@@ -224,12 +223,12 @@
ArrayList<Integer> list = new ArrayList<>(0);
for (byte value : values) {
switch (value) {
- case SpatializerHeadTrackingMode.OTHER:
- case SpatializerHeadTrackingMode.DISABLED:
+ case HeadTracking.Mode.OTHER:
+ case HeadTracking.Mode.DISABLED:
// not expected here, skip
break;
- case SpatializerHeadTrackingMode.RELATIVE_WORLD:
- case SpatializerHeadTrackingMode.RELATIVE_SCREEN:
+ case HeadTracking.Mode.RELATIVE_WORLD:
+ case HeadTracking.Mode.RELATIVE_SCREEN:
list.add(headTrackingModeTypeToSpatializerInt(value));
break;
default:
@@ -252,10 +251,10 @@
byte[] spatModes = spat.getSupportedModes();
for (byte mode : spatModes) {
switch (mode) {
- case SpatializationMode.SPATIALIZER_BINAURAL:
+ case Spatialization.Mode.BINAURAL:
mBinauralSupported = true;
break;
- case SpatializationMode.SPATIALIZER_TRANSAURAL:
+ case Spatialization.Mode.TRANSAURAL:
mTransauralSupported = true;
break;
default:
@@ -272,8 +271,8 @@
// initialize list of compatible devices
for (int i = 0; i < SPAT_MODE_FOR_DEVICE_TYPE.size(); i++) {
int mode = SPAT_MODE_FOR_DEVICE_TYPE.valueAt(i);
- if ((mode == (int) SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
- || (mode == (int) SpatializationMode.SPATIALIZER_TRANSAURAL
+ if ((mode == (int) Spatialization.Mode.BINAURAL && mBinauralSupported)
+ || (mode == (int) Spatialization.Mode.TRANSAURAL
&& mTransauralSupported)) {
mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i));
}
@@ -576,9 +575,9 @@
int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(device.getDeviceType(),
Integer.MIN_VALUE);
- device.setSAEnabled(spatMode == SpatializationMode.SPATIALIZER_BINAURAL
+ device.setSAEnabled(spatMode == Spatialization.Mode.BINAURAL
? mBinauralEnabledDefault
- : spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL
+ : spatMode == Spatialization.Mode.TRANSAURAL
? mTransauralEnabledDefault
: false);
device.setHeadTrackerEnabled(mHeadTrackingEnabledDefault);
@@ -628,9 +627,9 @@
if (isBluetoothDevice(internalDeviceType)) return deviceType;
final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
- if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) {
+ if (spatMode == Spatialization.Mode.TRANSAURAL) {
return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
- } else if (spatMode == SpatializationMode.SPATIALIZER_BINAURAL) {
+ } else if (spatMode == Spatialization.Mode.BINAURAL) {
return AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
}
return AudioDeviceInfo.TYPE_UNKNOWN;
@@ -755,8 +754,8 @@
// not be included.
final byte modeForDevice = (byte) SPAT_MODE_FOR_DEVICE_TYPE.get(ada.getType(),
/*default when type not found*/ -1);
- if ((modeForDevice == SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported)
- || (modeForDevice == SpatializationMode.SPATIALIZER_TRANSAURAL
+ if ((modeForDevice == Spatialization.Mode.BINAURAL && mBinauralSupported)
+ || (modeForDevice == Spatialization.Mode.TRANSAURAL
&& mTransauralSupported)) {
return true;
}
@@ -966,6 +965,11 @@
if (mSpat == null) {
mSpatCallback = new SpatializerCallback();
mSpat = AudioSystem.getSpatializer(mSpatCallback);
+ if (mSpat == null) {
+ Log.e(TAG, "createSpat(): No Spatializer found");
+ postReset();
+ return;
+ }
try {
//TODO: register heatracking callback only when sensors are registered
if (mIsHeadTrackingSupported) {
@@ -1430,7 +1434,7 @@
}
synchronized void onInitSensors() {
- final boolean init = mFeatureEnabled && (mSpatLevel != SpatializationLevel.NONE);
+ final boolean init = mFeatureEnabled && (mSpatLevel != Spatialization.Level.NONE);
final String action = init ? "initializing" : "releasing";
if (mSpat == null) {
logloge("not " + action + " sensors, null spatializer");
@@ -1496,13 +1500,13 @@
// SDK <-> AIDL converters
private static int headTrackingModeTypeToSpatializerInt(byte mode) {
switch (mode) {
- case SpatializerHeadTrackingMode.OTHER:
+ case HeadTracking.Mode.OTHER:
return Spatializer.HEAD_TRACKING_MODE_OTHER;
- case SpatializerHeadTrackingMode.DISABLED:
+ case HeadTracking.Mode.DISABLED:
return Spatializer.HEAD_TRACKING_MODE_DISABLED;
- case SpatializerHeadTrackingMode.RELATIVE_WORLD:
+ case HeadTracking.Mode.RELATIVE_WORLD:
return Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
- case SpatializerHeadTrackingMode.RELATIVE_SCREEN:
+ case HeadTracking.Mode.RELATIVE_SCREEN:
return Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE;
default:
throw (new IllegalArgumentException("Unexpected head tracking mode:" + mode));
@@ -1512,13 +1516,13 @@
private static byte spatializerIntToHeadTrackingModeType(int sdkMode) {
switch (sdkMode) {
case Spatializer.HEAD_TRACKING_MODE_OTHER:
- return SpatializerHeadTrackingMode.OTHER;
+ return HeadTracking.Mode.OTHER;
case Spatializer.HEAD_TRACKING_MODE_DISABLED:
- return SpatializerHeadTrackingMode.DISABLED;
+ return HeadTracking.Mode.DISABLED;
case Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD:
- return SpatializerHeadTrackingMode.RELATIVE_WORLD;
+ return HeadTracking.Mode.RELATIVE_WORLD;
case Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE:
- return SpatializerHeadTrackingMode.RELATIVE_SCREEN;
+ return HeadTracking.Mode.RELATIVE_SCREEN;
default:
throw (new IllegalArgumentException("Unexpected head tracking mode:" + sdkMode));
}
@@ -1526,11 +1530,11 @@
private static int spatializationLevelToSpatializerInt(byte level) {
switch (level) {
- case SpatializationLevel.NONE:
+ case Spatialization.Level.NONE:
return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
- case SpatializationLevel.SPATIALIZER_MULTICHANNEL:
+ case Spatialization.Level.MULTICHANNEL:
return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL;
- case SpatializationLevel.SPATIALIZER_MCHAN_BED_PLUS_OBJECTS:
+ case Spatialization.Level.BED_PLUS_OBJECTS:
return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MCHAN_BED_PLUS_OBJECTS;
default:
throw (new IllegalArgumentException("Unexpected spatializer level:" + level));
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 4538cad..0629e637 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -39,6 +39,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IAuthService;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
@@ -333,6 +334,33 @@
}
@Override
+ public long getLastAuthenticationTime(int userId,
+ @Authenticators.Types int authenticators) throws RemoteException {
+ // Only allow internal clients to call getLastAuthenticationTime with a different
+ // userId.
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (userId != callingUserId) {
+ checkInternalPermission();
+ } else {
+ checkPermission();
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // We can't do this above because we need the READ_DEVICE_CONFIG permission, which
+ // the calling user may not possess.
+ if (!Flags.lastAuthenticationTime()) {
+ throw new UnsupportedOperationException();
+ }
+
+ return mBiometricService.getLastAuthenticationTime(userId, authenticators);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public boolean hasEnrolledBiometrics(int userId, String opPackageName)
throws RemoteException {
checkInternalPermission();
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index e8ffe4f..9c454ae 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static android.hardware.biometrics.BiometricManager.BIOMETRIC_NO_AUTHENTICATION;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE;
@@ -37,6 +38,7 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -51,6 +53,7 @@
import android.hardware.camera2.CameraManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -60,10 +63,16 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.security.Authorization;
+import android.security.GateKeeper;
import android.security.KeyStore;
+import android.security.authorization.IKeystoreAuthorization;
+import android.security.authorization.ResponseCode;
+import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Pair;
@@ -77,6 +86,7 @@
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.utils.Slogf;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -114,6 +124,10 @@
KeyStore mKeyStore;
@VisibleForTesting
ITrustManager mTrustManager;
+ @VisibleForTesting
+ IKeystoreAuthorization mKeystoreAuthorization;
+ @VisibleForTesting
+ IGateKeeperService mGateKeeper;
// Get and cache the available biometric authenticators and their associated info.
final ArrayList<BiometricSensor> mSensors = new ArrayList<>();
@@ -630,6 +644,64 @@
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override // Binder call
+ public long getLastAuthenticationTime(
+ int userId, @Authenticators.Types int authenticators) {
+ super.getLastAuthenticationTime_enforcePermission();
+
+ if (!Flags.lastAuthenticationTime()) {
+ throw new UnsupportedOperationException();
+ }
+
+ Slogf.d(TAG, "getLastAuthenticationTime(userId=%d, authenticators=0x%x)",
+ userId, authenticators);
+
+ final long secureUserId;
+ try {
+ secureUserId = mGateKeeper.getSecureUserId(userId);
+ } catch (RemoteException e) {
+ Slogf.w(TAG, "Failed to get secure user id for " + userId, e);
+ return BIOMETRIC_NO_AUTHENTICATION;
+ }
+
+ if (secureUserId == GateKeeper.INVALID_SECURE_USER_ID) {
+ Slogf.w(TAG, "No secure user id for " + userId);
+ return BIOMETRIC_NO_AUTHENTICATION;
+ }
+
+ ArrayList<Integer> hardwareAuthenticators = new ArrayList<>(2);
+
+ if ((authenticators & Authenticators.DEVICE_CREDENTIAL) != 0) {
+ hardwareAuthenticators.add(HardwareAuthenticatorType.PASSWORD);
+ }
+
+ if ((authenticators & Authenticators.BIOMETRIC_STRONG) != 0) {
+ hardwareAuthenticators.add(HardwareAuthenticatorType.FINGERPRINT);
+ }
+
+ if (hardwareAuthenticators.isEmpty()) {
+ throw new IllegalArgumentException("authenticators must not be empty");
+ }
+
+ int[] authTypesArray = hardwareAuthenticators.stream()
+ .mapToInt(Integer::intValue)
+ .toArray();
+ try {
+ return mKeystoreAuthorization.getLastAuthTime(secureUserId, authTypesArray);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error getting last auth time: " + e);
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ } catch (ServiceSpecificException e) {
+ // This is returned when the feature flag test fails in keystore2
+ if (e.errorCode == ResponseCode.PERMISSION_DENIED) {
+ throw new UnsupportedOperationException();
+ }
+
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ }
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public boolean hasEnrolledBiometrics(int userId, String opPackageName) {
@@ -951,6 +1023,14 @@
return ActivityManager.getService();
}
+ public IKeystoreAuthorization getKeystoreAuthorizationService() {
+ return Authorization.getService();
+ }
+
+ public IGateKeeperService getGateKeeperService() {
+ return GateKeeper.getService();
+ }
+
public ITrustManager getTrustManager() {
return ITrustManager.Stub.asInterface(ServiceManager.getService(Context.TRUST_SERVICE));
}
@@ -1064,6 +1144,8 @@
mBiometricContext = injector.getBiometricContext(context);
mUserManager = injector.getUserManager(context);
mBiometricCameraManager = injector.getBiometricCameraManager(context);
+ mKeystoreAuthorization = injector.getKeystoreAuthorizationService();
+ mGateKeeper = injector.getGateKeeperService();
try {
injector.getActivityManagerService().registerUserSwitchObserver(
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 3c885a1..3b18f0c 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -52,7 +52,6 @@
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.media.AudioManager;
-import android.nfc.INfcAdapter;
import android.nfc.NfcAdapter;
import android.nfc.NfcManager;
import android.os.Binder;
@@ -1252,45 +1251,19 @@
}
}
- // TODO(b/303286040): Remove the raw INfcAdapter usage once |ENABLE_NFC_MAINLINE_FLAG| is
- // rolled out.
- private static final String NFC_SERVICE_BINDER_NAME = "nfc";
- // Flags arguments to NFC adapter to enable/disable NFC
- public static final int DISABLE_POLLING_FLAGS = 0x1000;
- public static final int ENABLE_POLLING_FLAGS = 0x0000;
- private void setNfcReaderModeUsingINfcAdapter(boolean enablePolling) {
- IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
- if (nfcServiceBinder == null) {
+ private void notifyNfcService(boolean enablePolling) {
+ NfcManager nfcManager = mContext.getSystemService(NfcManager.class);
+ if (nfcManager == null) {
Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
return;
}
- INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder);
- int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
- if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags);
- try {
- nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null);
- } catch (RemoteException e) {
- Slog.w(TAG, "Could not notify NFC service, remote exception: " + e);
+ NfcAdapter nfcAdapter = nfcManager.getDefaultAdapter();
+ if (nfcAdapter == null) {
+ Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
+ return;
}
- }
-
- private void notifyNfcService(boolean enablePolling) {
- if (android.nfc.Flags.enableNfcMainline()) {
- NfcManager nfcManager = mContext.getSystemService(NfcManager.class);
- if (nfcManager == null) {
- Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
- return;
- }
- NfcAdapter nfcAdapter = nfcManager.getDefaultAdapter();
- if (nfcAdapter == null) {
- Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
- return;
- }
- if (DEBUG) Slog.v(TAG, "Setting NFC reader mode. enablePolling: " + enablePolling);
- nfcAdapter.setReaderMode(enablePolling);
- } else {
- setNfcReaderModeUsingINfcAdapter(enablePolling);
- }
+ if (DEBUG) Slog.v(TAG, "Setting NFC reader mode. enablePolling: " + enablePolling);
+ nfcAdapter.setReaderMode(enablePolling);
}
private static int[] toArray(Collection<Integer> c) {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 4b8b431..74310b5 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -54,6 +54,7 @@
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -65,7 +66,6 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -114,8 +114,6 @@
public class ClipboardService extends SystemService {
private static final String TAG = "ClipboardService";
- private static final boolean IS_EMULATOR =
- SystemProperties.getBoolean("ro.boot.qemu", false);
@VisibleForTesting
public static final long DEFAULT_CLIPBOARD_TIMEOUT_MILLIS = 3600000;
@@ -190,7 +188,7 @@
mAutofillInternal = LocalServices.getService(AutofillManagerInternal.class);
final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
mPermissionOwner = permOwner;
- if (IS_EMULATOR) {
+ if (Build.IS_EMULATOR) {
mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
synchronized (mLock) {
setPrimaryClipInternalLocked(getClipboardLocked(0, DEVICE_ID_DEFAULT), clip,
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 53fbe8f..c517058 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -22,7 +22,6 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_AUTO;
@@ -45,12 +44,10 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -59,7 +56,6 @@
import android.content.pm.UserInfo;
import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityManager;
-import android.net.DnsResolver;
import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.Ikev2VpnProfile;
@@ -70,8 +66,6 @@
import android.net.IpSecTransform;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
@@ -112,8 +106,6 @@
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -124,7 +116,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemService;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -152,7 +143,6 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
-import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BinderUtils;
import com.android.net.module.util.LinkPropertiesUtils;
import com.android.net.module.util.NetdUtils;
@@ -165,11 +155,8 @@
import libcore.io.IoUtils;
-import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -195,14 +182,11 @@
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* @hide
@@ -457,10 +441,6 @@
// The user id of initiating VPN.
private final int mUserId;
- interface RetryScheduler {
- void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException;
- }
-
private static class CarrierConfigInfo {
public final String mccMnc;
public final int keepaliveDelaySec;
@@ -489,26 +469,6 @@
return Binder.getCallingUid() == Process.SYSTEM_UID;
}
- public void startService(final String serviceName) {
- SystemService.start(serviceName);
- }
-
- public void stopService(final String serviceName) {
- SystemService.stop(serviceName);
- }
-
- public boolean isServiceRunning(final String serviceName) {
- return SystemService.isRunning(serviceName);
- }
-
- public boolean isServiceStopped(final String serviceName) {
- return SystemService.isStopped(serviceName);
- }
-
- public File getStateFile() {
- return new File("/data/misc/vpn/state");
- }
-
public DeviceIdleInternal getDeviceIdleInternal() {
return LocalServices.getService(DeviceIdleInternal.class);
}
@@ -517,104 +477,6 @@
return VpnConfig.getIntentForStatusPanel(context);
}
- public void sendArgumentsToDaemon(
- final String daemon, final LocalSocket socket, final String[] arguments,
- final RetryScheduler retryScheduler) throws IOException, InterruptedException {
- final LocalSocketAddress address = new LocalSocketAddress(
- daemon, LocalSocketAddress.Namespace.RESERVED);
-
- // Wait for the socket to connect.
- while (true) {
- try {
- socket.connect(address);
- break;
- } catch (Exception e) {
- // ignore
- }
- retryScheduler.checkInterruptAndDelay(true /* sleepLonger */);
- }
- socket.setSoTimeout(500);
-
- final OutputStream out = socket.getOutputStream();
- for (String argument : arguments) {
- byte[] bytes = argument.getBytes(StandardCharsets.UTF_8);
- if (bytes.length >= 0xFFFF) {
- throw new IllegalArgumentException("Argument is too large");
- }
- out.write(bytes.length >> 8);
- out.write(bytes.length);
- out.write(bytes);
- retryScheduler.checkInterruptAndDelay(false /* sleepLonger */);
- }
- out.write(0xFF);
- out.write(0xFF);
-
- // Wait for End-of-File.
- final InputStream in = socket.getInputStream();
- while (true) {
- try {
- if (in.read() == -1) {
- break;
- }
- } catch (Exception e) {
- // ignore
- }
- retryScheduler.checkInterruptAndDelay(true /* sleepLonger */);
- }
- }
-
- @NonNull
- public InetAddress resolve(final String endpoint)
- throws ExecutionException, InterruptedException {
- try {
- return InetAddresses.parseNumericAddress(endpoint);
- } catch (IllegalArgumentException e) {
- // Endpoint is not numeric : fall through and resolve
- }
-
- final CancellationSignal cancellationSignal = new CancellationSignal();
- try {
- final DnsResolver resolver = DnsResolver.getInstance();
- final CompletableFuture<InetAddress> result = new CompletableFuture();
- final DnsResolver.Callback<List<InetAddress>> cb =
- new DnsResolver.Callback<List<InetAddress>>() {
- @Override
- public void onAnswer(@NonNull final List<InetAddress> answer,
- final int rcode) {
- if (answer.size() > 0) {
- result.complete(answer.get(0));
- } else {
- result.completeExceptionally(
- new UnknownHostException(endpoint));
- }
- }
-
- @Override
- public void onError(@Nullable final DnsResolver.DnsException error) {
- // Unfortunately UnknownHostException doesn't accept a cause, so
- // print a message here instead. Only show the summary, not the
- // full stack trace.
- Log.e(TAG, "Async dns resolver error : " + error);
- result.completeExceptionally(new UnknownHostException(endpoint));
- }
- };
- resolver.query(null /* network, null for default */, endpoint,
- DnsResolver.FLAG_EMPTY, r -> r.run(), cancellationSignal, cb);
- return result.get();
- } catch (final ExecutionException e) {
- Log.e(TAG, "Cannot resolve VPN endpoint : " + endpoint + ".", e);
- throw e;
- } catch (final InterruptedException e) {
- Log.e(TAG, "Legacy VPN was interrupted while resolving the endpoint", e);
- cancellationSignal.cancel();
- throw e;
- }
- }
-
- public boolean isInterfacePresent(final Vpn vpn, final String iface) {
- return vpn.jniCheck(iface) != 0;
- }
-
/**
* @see ParcelFileDescriptor#adoptFd(int)
*/
@@ -1063,8 +925,6 @@
// Store mPackage since it might be reset or might be replaced with the other VPN app.
final String oldPackage = mPackage;
final boolean isPackageChanged = !Objects.equals(packageName, oldPackage);
- // TODO: Remove "SdkLevel.isAtLeastT()" check once VpnManagerService is decoupled from
- // ConnectivityServiceTest.
// Only notify VPN apps that were already always-on, and only if the always-on provider
// changed, or the lockdown mode changed.
final boolean shouldNotifyOldPkg = isVpnApp(oldPackage) && mAlwaysOn
@@ -1078,12 +938,6 @@
saveAlwaysOnPackage();
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
- if (!SdkLevel.isAtLeastT()) {
- return true;
- }
-
if (shouldNotifyOldPkg) {
// If both of shouldNotifyOldPkg & isPackageChanged are true, that means the
// always-on of old package is disabled or the old package is replaced with the new
@@ -1984,9 +1838,7 @@
for (String app : packageNames) {
int uid = getAppUid(mContext, app, userId);
if (uid != -1) uids.add(uid);
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
- if (Process.isApplicationUid(uid) && SdkLevel.isAtLeastT()) {
+ if (Process.isApplicationUid(uid)) {
uids.add(Process.toSdkSandboxUid(uid));
}
}
@@ -2297,15 +2149,6 @@
private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() {
@Override
- public void interfaceStatusChanged(String interfaze, boolean up) {
- synchronized (Vpn.this) {
- if (!up && mVpnRunner != null && mVpnRunner instanceof LegacyVpnRunner) {
- ((LegacyVpnRunner) mVpnRunner).exitIfOuterInterfaceIs(interfaze);
- }
- }
- }
-
- @Override
public void interfaceRemoved(String interfaze) {
synchronized (Vpn.this) {
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
@@ -2556,17 +2399,6 @@
private native boolean jniAddAddress(String interfaze, String address, int prefixLen);
private native boolean jniDelAddress(String interfaze, String address, int prefixLen);
- private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
- for (RouteInfo route : prop.getAllRoutes()) {
- // Currently legacy VPN only works on IPv4.
- if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
- return route;
- }
- }
-
- throw new IllegalStateException("Unable to find IPv4 default gateway");
- }
-
private void enforceNotRestrictedUser() {
final long token = Binder.clearCallingIdentity();
try {
@@ -2585,15 +2417,14 @@
* secondary thread to perform connection work, returning quickly.
*
* Should only be called to respond to Binder requests as this enforces caller permission. Use
- * {@link #startLegacyVpnPrivileged(VpnProfile, Network, LinkProperties)} to skip the
+ * {@link #startLegacyVpnPrivileged(VpnProfile)} to skip the
* permission check only when the caller is trusted (or the call is initiated by the system).
*/
- public void startLegacyVpn(VpnProfile profile, @Nullable Network underlying,
- LinkProperties egress) {
+ public void startLegacyVpn(VpnProfile profile) {
enforceControlPermission();
final long token = Binder.clearCallingIdentity();
try {
- startLegacyVpnPrivileged(profile, underlying, egress);
+ startLegacyVpnPrivileged(profile);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2652,23 +2483,19 @@
}
/**
- * Like {@link #startLegacyVpn(VpnProfile, Network, LinkProperties)}, but does not
- * check permissions under the assumption that the caller is the system.
+ * Like {@link #startLegacyVpn(VpnProfile)}, but does not check permissions under
+ * the assumption that the caller is the system.
*
* Callers are responsible for checking permissions if needed.
*/
- public void startLegacyVpnPrivileged(VpnProfile profile,
- @Nullable Network underlying, @NonNull LinkProperties egress) {
+ public void startLegacyVpnPrivileged(VpnProfile profileToStart) {
+ final VpnProfile profile = profileToStart.clone();
UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
new UserHandle(mUserId))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
- final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
- final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
- final String iface = ipv4DefaultRoute.getInterface();
-
// Load certificates.
String privateKey = "";
String userCert = "";
@@ -2700,8 +2527,6 @@
throw new IllegalStateException("Cannot load credentials");
}
- // Prepare arguments for racoon.
- String[] racoon = null;
switch (profile.type) {
case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
// Secret key is still just the alias (not the actual private key). The private key
@@ -2731,109 +2556,9 @@
// profile.
startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN);
return;
- case VpnProfile.TYPE_L2TP_IPSEC_PSK:
- racoon = new String[] {
- iface, profile.server, "udppsk", profile.ipsecIdentifier,
- profile.ipsecSecret, "1701",
- };
- break;
- case VpnProfile.TYPE_L2TP_IPSEC_RSA:
- racoon = new String[] {
- iface, profile.server, "udprsa", makeKeystoreEngineGrantString(privateKey),
- userCert, caCert, serverCert, "1701",
- };
- break;
- case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
- racoon = new String[] {
- iface, profile.server, "xauthpsk", profile.ipsecIdentifier,
- profile.ipsecSecret, profile.username, profile.password, "", gateway,
- };
- break;
- case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
- racoon = new String[] {
- iface, profile.server, "xauthrsa", makeKeystoreEngineGrantString(privateKey),
- userCert, caCert, serverCert, profile.username, profile.password, "", gateway,
- };
- break;
- case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
- racoon = new String[] {
- iface, profile.server, "hybridrsa",
- caCert, serverCert, profile.username, profile.password, "", gateway,
- };
- break;
}
- // Prepare arguments for mtpd. MTU/MRU calculated conservatively. Only IPv4 supported
- // because LegacyVpn.
- // 1500 - 60 (Carrier-internal IPv6 + UDP + GTP) - 10 (PPP) - 16 (L2TP) - 8 (UDP)
- // - 77 (IPsec w/ SHA-2 512, 256b trunc-len, AES-CBC) - 8 (UDP encap) - 20 (IPv4)
- // - 28 (464xlat)
- String[] mtpd = null;
- switch (profile.type) {
- case VpnProfile.TYPE_PPTP:
- mtpd = new String[] {
- iface, "pptp", profile.server, "1723",
- "name", profile.username, "password", profile.password,
- "linkname", "vpn", "refuse-eap", "nodefaultroute",
- "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
- (profile.mppe ? "+mppe" : "nomppe"),
- };
- if (profile.mppe) {
- // Disallow PAP authentication when MPPE is requested, as MPPE cannot work
- // with PAP anyway, and users may not expect PAP (plain text) to be used when
- // MPPE was requested.
- mtpd = Arrays.copyOf(mtpd, mtpd.length + 1);
- mtpd[mtpd.length - 1] = "-pap";
- }
- break;
- case VpnProfile.TYPE_L2TP_IPSEC_PSK:
- case VpnProfile.TYPE_L2TP_IPSEC_RSA:
- mtpd = new String[] {
- iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
- "name", profile.username, "password", profile.password,
- "linkname", "vpn", "refuse-eap", "nodefaultroute",
- "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
- };
- break;
- }
-
- VpnConfig config = new VpnConfig();
- config.legacy = true;
- config.user = profile.key;
- config.interfaze = iface;
- config.session = profile.name;
- config.isMetered = false;
- config.proxyInfo = profile.proxy;
- if (underlying != null) {
- config.underlyingNetworks = new Network[] { underlying };
- }
-
- config.addLegacyRoutes(profile.routes);
- if (!profile.dnsServers.isEmpty()) {
- config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
- }
- if (!profile.searchDomains.isEmpty()) {
- config.searchDomains = Arrays.asList(profile.searchDomains.split(" +"));
- }
- startLegacyVpn(config, racoon, mtpd, profile);
- }
-
- private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd,
- VpnProfile profile) {
- stopVpnRunnerPrivileged();
-
- // Prepare for the new request.
- prepareInternal(VpnConfig.LEGACY_VPN);
- updateState(DetailedState.CONNECTING, "startLegacyVpn");
-
- // Start a new LegacyVpnRunner and we are done!
- mVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile);
- startLegacyVpnRunner();
- }
-
- @VisibleForTesting
- protected void startLegacyVpnRunner() {
- mVpnRunner.start();
+ throw new UnsupportedOperationException("Legacy VPN is deprecated");
}
/**
@@ -2851,17 +2576,7 @@
return;
}
- final boolean isLegacyVpn = mVpnRunner instanceof LegacyVpnRunner;
mVpnRunner.exit();
-
- // LegacyVpn uses daemons that must be shut down before new ones are brought up.
- // The same limitation does not apply to Platform VPNs.
- if (isLegacyVpn) {
- synchronized (LegacyVpnRunner.TAG) {
- // wait for old thread to completely finish before spinning up
- // new instance, otherwise state updates can be out of order.
- }
- }
}
/**
@@ -3537,6 +3252,13 @@
* given network to start a new IKE session.
*/
private void startOrMigrateIkeSession(@Nullable Network underlyingNetwork) {
+ synchronized (Vpn.this) {
+ // Ignore stale runner.
+ if (mVpnRunner != this) return;
+ setVpnNetworkPreference(mSessionKey,
+ createUserAndRestrictedProfilesRanges(mUserId,
+ mConfig.allowedApplications, mConfig.disallowedApplications));
+ }
if (underlyingNetwork == null) {
// For null underlyingNetwork case, there will not be a NetworkAgent available so
// no underlying network update is necessary here. Note that updating
@@ -4057,6 +3779,7 @@
updateState(DetailedState.FAILED, exception.getMessage());
}
+ clearVpnNetworkPreference(mSessionKey);
disconnectVpnRunner();
}
@@ -4143,9 +3866,7 @@
// Ignore stale runner.
if (mVpnRunner != this) return;
- // TODO(b/230548427): Remove SDK check once VPN related stuff are
- // decoupled from ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT() && category != null && isVpnApp(mPackage)) {
+ if (category != null && isVpnApp(mPackage)) {
sendEventToVpnManagerApp(category, errorClass, errorCode,
getPackage(), mSessionKey, makeVpnProfileStateLocked(),
mActiveNetwork,
@@ -4193,6 +3914,13 @@
}
resetIkeState();
+ if (errorCode != VpnManager.ERROR_CODE_NETWORK_LOST
+ // Clear the VPN network preference when the retry delay is higher than 5s.
+ // mRetryCount was increased when scheduleRetryNewIkeSession() is called,
+ // therefore use mRetryCount - 1 here.
+ && mDeps.getNextRetryDelayMs(mRetryCount - 1) > 5_000L) {
+ clearVpnNetworkPreference(mSessionKey);
+ }
}
/**
@@ -4239,13 +3967,17 @@
mCarrierConfigManager.unregisterCarrierConfigChangeListener(
mCarrierConfigChangeListener);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
- clearVpnNetworkPreference(mSessionKey);
mExecutor.shutdown();
}
@Override
public void exitVpnRunner() {
+ // mSessionKey won't be changed since the Ikev2VpnRunner is created, so it's ok to use
+ // it outside the mExecutor. And clearing the VPN network preference here can prevent
+ // the case that the VPN network preference isn't cleared when Ikev2VpnRunner became
+ // stale.
+ clearVpnNetworkPreference(mSessionKey);
try {
mExecutor.execute(() -> {
disconnectVpnRunner();
@@ -4256,343 +3988,6 @@
}
}
- /**
- * Bringing up a VPN connection takes time, and that is all this thread
- * does. Here we have plenty of time. The only thing we need to take
- * care of is responding to interruptions as soon as possible. Otherwise
- * requests will pile up. This could be done in a Handler as a state
- * machine, but it is much easier to read in the current form.
- */
- private class LegacyVpnRunner extends VpnRunner {
- private static final String TAG = "LegacyVpnRunner";
-
- private final String[] mDaemons;
- private final String[][] mArguments;
- private final LocalSocket[] mSockets;
- private final String mOuterInterface;
- private final AtomicInteger mOuterConnection =
- new AtomicInteger(ConnectivityManager.TYPE_NONE);
- private final VpnProfile mProfile;
-
- private long mBringupStartTime = -1;
-
- /**
- * Watch for the outer connection (passing in the constructor) going away.
- */
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!mEnableTeardown) return;
-
- if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
- ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
- NetworkInfo info = (NetworkInfo)intent.getExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
- if (info != null && !info.isConnectedOrConnecting()) {
- try {
- mObserver.interfaceStatusChanged(mOuterInterface, false);
- } catch (RemoteException e) {}
- }
- }
- }
- }
- };
-
- // GuardedBy("Vpn.this") (annotation can't be applied to constructor)
- LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) {
- super(TAG);
- if (racoon == null && mtpd == null) {
- throw new IllegalArgumentException(
- "Arguments to racoon and mtpd must not both be null");
- }
- mConfig = config;
- mDaemons = new String[] {"racoon", "mtpd"};
- // TODO: clear arguments from memory once launched
- mArguments = new String[][] {racoon, mtpd};
- mSockets = new LocalSocket[mDaemons.length];
-
- // This is the interface which VPN is running on,
- // mConfig.interfaze will change to point to OUR
- // internal interface soon. TODO - add inner/outer to mconfig
- // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
- // we will leave the VPN up. We should check that it's still there/connected after
- // registering
- mOuterInterface = mConfig.interfaze;
-
- mProfile = profile;
-
- if (!TextUtils.isEmpty(mOuterInterface)) {
- for (Network network : mConnectivityManager.getAllNetworks()) {
- final LinkProperties lp = mConnectivityManager.getLinkProperties(network);
- if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) {
- final NetworkInfo netInfo = mConnectivityManager.getNetworkInfo(network);
- if (netInfo != null) {
- mOuterConnection.set(netInfo.getType());
- break;
- }
- }
- }
- }
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- mContext.registerReceiver(mBroadcastReceiver, filter);
- }
-
- /**
- * Checks if the parameter matches the underlying interface
- *
- * <p>If the underlying interface is torn down, the LegacyVpnRunner also should be. It has
- * no ability to migrate between interfaces (or Networks).
- */
- public void exitIfOuterInterfaceIs(String interfaze) {
- if (interfaze.equals(mOuterInterface)) {
- Log.i(TAG, "Legacy VPN is going down with " + interfaze);
- exitVpnRunner();
- }
- }
-
- /** Tears down this LegacyVpn connection */
- @Override
- public void exitVpnRunner() {
- // We assume that everything is reset after stopping the daemons.
- interrupt();
-
- // Always disconnect. This may be called again in cleanupVpnStateLocked() if
- // exitVpnRunner() was called from exit(), but it will be a no-op.
- agentDisconnect();
- try {
- mContext.unregisterReceiver(mBroadcastReceiver);
- } catch (IllegalArgumentException e) {}
- }
-
- @Override
- public void run() {
- // Wait for the previous thread since it has been interrupted.
- Log.v(TAG, "Waiting");
- synchronized (TAG) {
- Log.v(TAG, "Executing");
- try {
- bringup();
- waitForDaemonsToStop();
- interrupted(); // Clear interrupt flag if execute called exit.
- } catch (InterruptedException e) {
- } finally {
- for (LocalSocket socket : mSockets) {
- IoUtils.closeQuietly(socket);
- }
- // This sleep is necessary for racoon to successfully complete sending delete
- // message to server.
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- }
- for (String daemon : mDaemons) {
- mDeps.stopService(daemon);
- }
- }
- agentDisconnect();
- }
- }
-
- private void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException {
- long now = SystemClock.elapsedRealtime();
- if (now - mBringupStartTime <= 60000) {
- Thread.sleep(sleepLonger ? 200 : 1);
- } else {
- updateState(DetailedState.FAILED, "checkpoint");
- throw new IllegalStateException("VPN bringup took too long");
- }
- }
-
- private void checkAndFixupArguments(@NonNull final InetAddress endpointAddress) {
- final String endpointAddressString = endpointAddress.getHostAddress();
- // Perform some safety checks before inserting the address in place.
- // Position 0 in mDaemons and mArguments must be racoon, and position 1 must be mtpd.
- if (!"racoon".equals(mDaemons[0]) || !"mtpd".equals(mDaemons[1])) {
- throw new IllegalStateException("Unexpected daemons order");
- }
-
- // Respectively, the positions at which racoon and mtpd take the server address
- // argument are 1 and 2. Not all types of VPN require both daemons however, and
- // in that case the corresponding argument array is null.
- if (mArguments[0] != null) {
- if (!mProfile.server.equals(mArguments[0][1])) {
- throw new IllegalStateException("Invalid server argument for racoon");
- }
- mArguments[0][1] = endpointAddressString;
- }
-
- if (mArguments[1] != null) {
- if (!mProfile.server.equals(mArguments[1][2])) {
- throw new IllegalStateException("Invalid server argument for mtpd");
- }
- mArguments[1][2] = endpointAddressString;
- }
- }
-
- private void bringup() {
- // Catch all exceptions so we can clean up a few things.
- try {
- // resolve never returns null. If it does because of some bug, it will be
- // caught by the catch() block below and cleanup gracefully.
- final InetAddress endpointAddress = mDeps.resolve(mProfile.server);
-
- // Big hack : dynamically replace the address of the server in the arguments
- // with the resolved address.
- checkAndFixupArguments(endpointAddress);
-
- // Initialize the timer.
- mBringupStartTime = SystemClock.elapsedRealtime();
-
- // Wait for the daemons to stop.
- for (String daemon : mDaemons) {
- while (!mDeps.isServiceStopped(daemon)) {
- checkInterruptAndDelay(true);
- }
- }
-
- // Clear the previous state.
- final File state = mDeps.getStateFile();
- state.delete();
- if (state.exists()) {
- throw new IllegalStateException("Cannot delete the state");
- }
- new File("/data/misc/vpn/abort").delete();
-
- updateState(DetailedState.CONNECTING, "execute");
-
- // Start the daemon with arguments.
- for (int i = 0; i < mDaemons.length; ++i) {
- String[] arguments = mArguments[i];
- if (arguments == null) {
- continue;
- }
-
- // Start the daemon.
- String daemon = mDaemons[i];
- mDeps.startService(daemon);
-
- // Wait for the daemon to start.
- while (!mDeps.isServiceRunning(daemon)) {
- checkInterruptAndDelay(true);
- }
-
- // Create the control socket.
- mSockets[i] = new LocalSocket();
-
- // Wait for the socket to connect and send over the arguments.
- mDeps.sendArgumentsToDaemon(daemon, mSockets[i], arguments,
- this::checkInterruptAndDelay);
- }
-
- // Wait for the daemons to create the new state.
- while (!state.exists()) {
- // Check if a running daemon is dead.
- for (int i = 0; i < mDaemons.length; ++i) {
- String daemon = mDaemons[i];
- if (mArguments[i] != null && !mDeps.isServiceRunning(daemon)) {
- throw new IllegalStateException(daemon + " is dead");
- }
- }
- checkInterruptAndDelay(true);
- }
-
- // Now we are connected. Read and parse the new state.
- String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1);
- if (parameters.length != 7) {
- throw new IllegalStateException("Cannot parse the state: '"
- + String.join("', '", parameters) + "'");
- }
-
- // Set the interface and the addresses in the config.
- synchronized (Vpn.this) {
- mConfig.interfaze = parameters[0].trim();
-
- mConfig.addLegacyAddresses(parameters[1]);
- // Set the routes if they are not set in the config.
- if (mConfig.routes == null || mConfig.routes.isEmpty()) {
- mConfig.addLegacyRoutes(parameters[2]);
- }
-
- // Set the DNS servers if they are not set in the config.
- if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) {
- String dnsServers = parameters[3].trim();
- if (!dnsServers.isEmpty()) {
- mConfig.dnsServers = Arrays.asList(dnsServers.split(" "));
- }
- }
-
- // Set the search domains if they are not set in the config.
- if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) {
- String searchDomains = parameters[4].trim();
- if (!searchDomains.isEmpty()) {
- mConfig.searchDomains = Arrays.asList(searchDomains.split(" "));
- }
- }
-
- // Add a throw route for the VPN server endpoint, if one was specified.
- if (endpointAddress instanceof Inet4Address) {
- mConfig.routes.add(new RouteInfo(
- new IpPrefix(endpointAddress, 32), null /*gateway*/,
- null /*iface*/, RTN_THROW));
- } else if (endpointAddress instanceof Inet6Address) {
- mConfig.routes.add(new RouteInfo(
- new IpPrefix(endpointAddress, 128), null /*gateway*/,
- null /*iface*/, RTN_THROW));
- } else {
- Log.e(TAG, "Unknown IP address family for VPN endpoint: "
- + endpointAddress);
- }
-
- // Here is the last step and it must be done synchronously.
- // Set the start time
- mConfig.startTime = SystemClock.elapsedRealtime();
-
- // Check if the thread was interrupted while we were waiting on the lock.
- checkInterruptAndDelay(false);
-
- // Check if the interface is gone while we are waiting.
- if (!mDeps.isInterfacePresent(Vpn.this, mConfig.interfaze)) {
- throw new IllegalStateException(mConfig.interfaze + " is gone");
- }
-
- // Now INetworkManagementEventObserver is watching our back.
- mInterface = mConfig.interfaze;
- prepareStatusIntent();
-
- agentConnect();
-
- Log.i(TAG, "Connected!");
- }
- } catch (Exception e) {
- Log.i(TAG, "Aborting", e);
- updateState(DetailedState.FAILED, e.getMessage());
- exitVpnRunner();
- }
- }
-
- /**
- * Check all daemons every two seconds. Return when one of them is stopped.
- * The caller will move to the disconnected state when this function returns,
- * which can happen if a daemon failed or if the VPN was torn down.
- */
- private void waitForDaemonsToStop() throws InterruptedException {
- if (!mNetworkInfo.isConnected()) {
- return;
- }
- while (true) {
- Thread.sleep(2000);
- for (int i = 0; i < mDaemons.length; i++) {
- if (mArguments[i] != null && mDeps.isServiceStopped(mDaemons[i])) {
- return;
- }
- }
- }
- }
- }
-
private void verifyCallingUidAndPackage(String packageName) {
mDeps.verifyCallingUidAndPackage(mContext, packageName, mUserId);
}
@@ -4839,11 +4234,9 @@
// Build intent first because the sessionKey will be reset after performing
// VpnRunner.exit(). Also, cache mOwnerUID even if ownerUID will not be changed in
// VpnRunner.exit() to prevent design being changed in the future.
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
final int ownerUid = mOwnerUID;
Intent intent = null;
- if (SdkLevel.isAtLeastT() && isVpnApp(mPackage)) {
+ if (isVpnApp(mPackage)) {
intent = buildVpnManagerEventIntent(
VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
-1 /* errorClass */, -1 /* errorCode*/, mPackage,
@@ -4884,12 +4277,8 @@
// The underlying network, NetworkCapabilities and LinkProperties are not
// necessary to send to VPN app since the purpose of this event is to notify
// VPN app that VPN is deactivated by the user.
- // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from
- // ConnectivityServiceTest.
- if (SdkLevel.isAtLeastT()) {
- mEventChanges.log("[VMEvent] " + packageName + " stopped");
- sendEventToVpnManagerApp(intent, packageName);
- }
+ mEventChanges.log("[VMEvent] " + packageName + " stopped");
+ sendEventToVpnManagerApp(intent, packageName);
}
private boolean storeAppExclusionList(@NonNull String packageName,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 7d0d5a7..ec6b5ea 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -559,7 +559,9 @@
HdmiDeviceInfo cecDeviceInfo = mService.getHdmiCecNetwork().getCecDeviceInfo(address);
// If no non-default display name is available for the device, request the devices OSD name.
- if (cecDeviceInfo != null && cecDeviceInfo.getDisplayName().equals(
+ // On TV devices, the OSD name is queried in NewDeviceAction instead.
+ if (!mService.isTvDevice() && cecDeviceInfo != null
+ && cecDeviceInfo.getDisplayName().equals(
HdmiUtils.getDefaultDeviceName(address))) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildGiveOsdNameCommand(
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index 7045e65..d994849 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -899,6 +899,9 @@
* port id.
*/
int portIdToPath(int portId) {
+ if (portId == Constants.CEC_SWITCH_HOME) {
+ return getPhysicalAddress();
+ }
HdmiPortInfo portInfo = getPortInfo(portId);
if (portInfo == null) {
Slog.e(TAG, "Cannot find the port info: " + portId);
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 6147c10..f3532e5 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -182,7 +182,7 @@
.setVendorId(mVendorId)
.setDisplayName(mDisplayName)
.build();
- localDevice().mService.getHdmiCecNetwork().addCecDevice(deviceInfo);
+ localDevice().mService.getHdmiCecNetwork().updateCecDevice(deviceInfo);
// Consume CEC messages we already got for this newly found device.
tv().processDelayedMessages(mDeviceLogicalAddress);
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
index 6e5eb56..aa638aa 100644
--- a/services/core/java/com/android/server/inputmethod/OWNERS
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -3,6 +3,8 @@
roosa@google.com
yukawa@google.com
tarandeep@google.com
+fstern@google.com
+cosminbaies@google.com
ogunwale@google.com #{LAST_RESORT_SUGGESTION}
jjaggi@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index fa95a34..c08b6d2 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -243,6 +243,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 boolean FIX_UNLOCKED_DEVICE_REQUIRED_KEYS =
+ android.security.Flags.fixUnlockedDeviceRequiredKeysV2();
// Duration that LockSettingsService will store the gatekeeper password for. This allows
// multiple biometric enrollments without prompting the user to enter their password via
@@ -853,9 +857,11 @@
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
- // Notify keystore that a new user was added.
- final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
- AndroidKeyStoreMaintenance.onUserAdded(userHandle);
+ if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ // Notify keystore that a new user was added.
+ final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ AndroidKeyStoreMaintenance.onUserAdded(userHandle);
+ }
} else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
mStorage.prefetchUser(userHandle);
@@ -1019,24 +1025,53 @@
}
mEarlyCreatedUsers = null; // no longer needed
- // Also do a one-time migration of all users to SP-based credentials with the CE key
- // encrypted by the SP. This is needed for the system user on the first boot of a
- // device, as the system user is special and never goes through the user creation flow
- // that other users do. It is also needed for existing users on a device upgraded from
- // Android 13 or earlier, where users with no LSKF didn't necessarily have an SP, and if
- // they did have an SP then their CE key wasn't encrypted by it.
+ // Do a one-time migration for any unsecured users: create the user's synthetic password
+ // if not already done, encrypt the user's CE key with the synthetic password if not
+ // already done, and create the user's Keystore super keys if not already done.
//
- // If this gets interrupted (e.g. by the device powering off), there shouldn't be a
- // problem since this will run again on the next boot, and setCeStorageProtection() is
- // okay with the CE key being already protected by the given secret.
- if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
- for (UserInfo user : mUserManager.getAliveUsers()) {
- removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
- synchronized (mSpManager) {
- migrateUserToSpWithBoundCeKeyLocked(user.id);
+ // This is needed for the following cases:
+ //
+ // - Finalizing the creation of the system user on the first boot of a device, as the
+ // system user is special and doesn't go through the normal user creation flow.
+ //
+ // - Upgrading from Android 13 or earlier, where unsecured users didn't necessarily have
+ // a synthetic password, and if they did have a synthetic password their CE key wasn't
+ // encrypted by it. Also, unsecured users didn't have Keystore super keys.
+ //
+ // - Upgrading from Android 14, where unsecured users didn't have Keystore super keys.
+ //
+ // The end result is that all users, regardless of whether they are secured or not, have
+ // a synthetic password with all keys initialized and protected by it.
+ //
+ // Note: if this migration gets interrupted (e.g. by the device powering off), there
+ // shouldn't be a problem since this will run again on the next boot, and
+ // setCeStorageProtection() and initKeystoreSuperKeys(..., true) are idempotent.
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ if (!getBoolean(MIGRATED_SP_FULL, false, 0)) {
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
+ synchronized (mSpManager) {
+ migrateUserToSpWithBoundKeysLocked(user.id);
+ }
}
+ setBoolean(MIGRATED_SP_FULL, true, 0);
}
- setString(MIGRATED_SP_CE_ONLY, "true", 0);
+ } else {
+ if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
+ synchronized (mSpManager) {
+ migrateUserToSpWithBoundCeKeyLocked(user.id);
+ }
+ }
+ setString(MIGRATED_SP_CE_ONLY, "true", 0);
+ }
+
+ if (getBoolean(MIGRATED_SP_FULL, false, 0)) {
+ // The FIX_UNLOCKED_DEVICE_REQUIRED_KEYS flag was enabled but then got disabled.
+ // Ensure the full migration runs again the next time the flag is enabled...
+ setBoolean(MIGRATED_SP_FULL, false, 0);
+ }
}
mThirdPartyAppsStarted = true;
@@ -1067,6 +1102,37 @@
}
}
+ @GuardedBy("mSpManager")
+ private void migrateUserToSpWithBoundKeysLocked(@UserIdInt int userId) {
+ if (isUserSecure(userId)) {
+ Slogf.d(TAG, "User %d is secured; no migration needed", userId);
+ return;
+ }
+ long protectorId = getCurrentLskfBasedProtectorId(userId);
+ if (protectorId == SyntheticPasswordManager.NULL_PROTECTOR_ID) {
+ Slogf.i(TAG, "Migrating unsecured user %d to SP-based credential", userId);
+ initializeSyntheticPassword(userId);
+ return;
+ }
+ Slogf.i(TAG, "Existing unsecured user %d has a synthetic password", userId);
+ AuthenticationResult result = mSpManager.unlockLskfBasedProtector(
+ getGateKeeperService(), protectorId, LockscreenCredential.createNone(), userId,
+ null);
+ SyntheticPassword sp = result.syntheticPassword;
+ if (sp == null) {
+ Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
+ return;
+ }
+ // While setCeStorageProtection() is idempotent, it does log some error messages when called
+ // again. Skip it if we know it was already handled by an earlier upgrade to Android 14.
+ if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
+ 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);
+ }
+
/**
* Returns the lowest password quality that still presents the same UI for entering it.
*
@@ -1348,8 +1414,22 @@
AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
}
+ @VisibleForTesting /** Note: this method is overridden in unit tests */
+ void initKeystoreSuperKeys(@UserIdInt int userId, SyntheticPassword sp, boolean allowExisting) {
+ final byte[] password = sp.deriveKeyStorePassword();
+ try {
+ int res = AndroidKeyStoreMaintenance.initUserSuperKeys(userId, password, allowExisting);
+ if (res != 0) {
+ throw new IllegalStateException("Failed to initialize Keystore super keys for user "
+ + userId);
+ }
+ } finally {
+ Arrays.fill(password, (byte) 0);
+ }
+ }
+
private void unlockKeystore(int userId, SyntheticPassword sp) {
- Authorization.onLockScreenEvent(false, userId, sp.deriveKeyStorePassword(), null);
+ Authorization.onDeviceUnlocked(userId, sp.deriveKeyStorePassword());
}
@VisibleForTesting /** Note: this method is overridden in unit tests */
@@ -2071,6 +2151,9 @@
return;
}
onSyntheticPasswordUnlocked(userId, result.syntheticPassword);
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ unlockKeystore(userId, result.syntheticPassword);
+ }
unlockCeStorage(userId, result.syntheticPassword);
}
}
@@ -2350,6 +2433,16 @@
}
private void createNewUser(@UserIdInt int userId, int userSerialNumber) {
+
+ // Delete all Keystore keys for userId, just in case any were left around from a removed
+ // user with the same userId. This should be unnecessary, but we've been doing this for a
+ // long time, so for now we keep doing it just in case it's ever important. Don't wait
+ // until initKeystoreSuperKeys() to do this; that can be delayed if the user is being
+ // created during early boot, and maybe something will use Keystore before then.
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ AndroidKeyStoreMaintenance.onUserAdded(userId);
+ }
+
synchronized (mUserCreationAndRemovalLock) {
// During early boot, don't actually create the synthetic password yet, but rather
// automatically delay it to later. We do this because protecting the synthetic
@@ -2756,7 +2849,7 @@
/**
* Creates the synthetic password (SP) for the given user, protects it with an empty LSKF, and
- * protects the user's CE key with a key derived from the SP.
+ * protects the user's CE storage key and Keystore super keys with keys derived from the SP.
*
* <p>This is called just once in the lifetime of the user: at user creation time (possibly
* delayed until the time when Weaver is guaranteed to be available), or when upgrading from
@@ -2775,6 +2868,9 @@
LockscreenCredential.createNone(), sp, userId);
setCurrentLskfBasedProtectorId(protectorId, userId);
setCeStorageProtection(userId, sp);
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ initKeystoreSuperKeys(userId, sp, /* allowExisting= */ false);
+ }
onSyntheticPasswordCreated(userId, sp);
Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
return sp;
@@ -2867,11 +2963,10 @@
/**
* Changes the user's LSKF by creating an LSKF-based protector that uses the new LSKF (which may
* be empty) and replacing the old LSKF-based protector with it. The SP itself is not changed.
- *
- * Also maintains the invariants described in {@link SyntheticPasswordManager} by
- * setting/clearing the protection (by the SP) on the user's auth-bound Keystore keys when the
- * LSKF is added/removed, respectively. If an LSKF is being added, then the Gatekeeper auth
- * token is also refreshed.
+ * <p>
+ * Also maintains the invariants described in {@link SyntheticPasswordManager} by enrolling /
+ * deleting the synthetic password into Gatekeeper as the LSKF is set / cleared, and asking
+ * Keystore to delete the user's auth-bound keys when the LSKF is cleared.
*/
@GuardedBy("mSpManager")
private long setLockCredentialWithSpLocked(LockscreenCredential credential,
@@ -2890,7 +2985,9 @@
if (!mSpManager.hasSidForUser(userId)) {
mSpManager.newSidForUser(getGateKeeperService(), sp, userId);
mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
- setKeystorePassword(sp.deriveKeyStorePassword(), userId);
+ if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ setKeystorePassword(sp.deriveKeyStorePassword(), userId);
+ }
}
} else {
// Cache all profile password if they use unified work challenge. This will later be
@@ -2901,7 +2998,11 @@
gateKeeperClearSecureUserId(userId);
unlockCeStorage(userId, sp);
unlockKeystore(userId, sp);
- setKeystorePassword(null, userId);
+ if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
+ AndroidKeyStoreMaintenance.onUserLskfRemoved(userId);
+ } else {
+ setKeystorePassword(null, userId);
+ }
removeBiometricsForUser(userId);
}
setCurrentLskfBasedProtectorId(newProtectorId, userId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 4bac872..17f2fcc 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -155,43 +155,45 @@
try (final PrintWriter pw = getOutPrintWriter();) {
pw.println("lockSettings service commands:");
pw.println("");
- pw.println("NOTE: when lock screen is set, all commands require the --old <CREDENTIAL>"
- + " argument.");
+ pw.println("NOTE: when a secure lock screen is set, most commands require the");
+ pw.println("--old <CREDENTIAL> option.");
pw.println("");
pw.println(" help");
pw.println(" Prints this help text.");
pw.println("");
- pw.println(" get-disabled [--old <CREDENTIAL>] [--user USER_ID]");
- pw.println(" Checks whether lock screen is disabled.");
+ pw.println(" get-disabled [--user USER_ID]");
+ pw.println(" Prints true if the lock screen is completely disabled, i.e. set to None.");
+ pw.println(" Otherwise prints false.");
pw.println("");
- pw.println(" set-disabled [--old <CREDENTIAL>] [--user USER_ID] <true|false>");
- pw.println(" When true, disables lock screen.");
+ pw.println(" set-disabled [--user USER_ID] <true|false>");
+ pw.println(" Sets whether the lock screen is disabled. If the lock screen is secure, this");
+ pw.println(" has no immediate effect. I.e. this can only change between Swipe and None.");
pw.println("");
pw.println(" set-pattern [--old <CREDENTIAL>] [--user USER_ID] <PATTERN>");
- pw.println(" Sets the lock screen as pattern, using the given PATTERN to unlock.");
+ pw.println(" Sets a secure lock screen that uses the given PATTERN. PATTERN is a series");
+ pw.println(" of digits 1-9 that identify the cells of the pattern.");
pw.println("");
pw.println(" set-pin [--old <CREDENTIAL>] [--user USER_ID] <PIN>");
- pw.println(" Sets the lock screen as PIN, using the given PIN to unlock.");
+ pw.println(" Sets a secure lock screen that uses the given PIN.");
pw.println("");
pw.println(" set-password [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
- pw.println(" Sets the lock screen as password, using the given PASSWORD to unlock.");
+ pw.println(" Sets a secure lock screen that uses the given PASSWORD.");
pw.println("");
pw.println(" clear [--old <CREDENTIAL>] [--user USER_ID]");
- pw.println(" Clears the lock credentials.");
+ pw.println(" Clears the lock credential.");
pw.println("");
pw.println(" verify [--old <CREDENTIAL>] [--user USER_ID]");
- pw.println(" Verifies the lock credentials.");
+ pw.println(" Verifies the lock credential.");
pw.println("");
pw.println(" remove-cache [--user USER_ID]");
pw.println(" Removes cached unified challenge for the managed profile.");
pw.println("");
pw.println(" set-resume-on-reboot-provider-package <package_name>");
- pw.println(" Sets the package name for server based resume on reboot service "
- + "provider.");
+ pw.println(" Sets the package name for server based resume on reboot service provider.");
pw.println("");
pw.println(" require-strong-auth [--user USER_ID] <reason>");
- pw.println(" Requires the strong authentication. The current supported reasons: "
- + "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN.");
+ pw.println(" Requires strong authentication. The current supported reasons:");
+ pw.println(" STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN.");
pw.println("");
}
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 8e9c21f..cc205d4 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -90,10 +90,15 @@
*
* - The user's credential-encrypted storage is always protected by the SP.
*
- * - The user's auth-bound Keystore keys are protected by the SP, but only while an LSKF is set.
- * This works by setting the user's Keystore and Gatekeeper passwords to SP-derived secrets, but
- * only while an LSKF is set. When the LSKF is removed, these passwords are cleared,
- * invalidating the user's auth-bound keys.
+ * - The user's Keystore superencryption keys are always protected by the SP. These in turn
+ * protect the Keystore keys that require user authentication, an unlocked device, or both.
+ *
+ * - A secret derived from the synthetic password is enrolled in Gatekeeper for the user, but only
+ * while the user has a (nonempty) LSKF. This enrollment has an associated ID called the Secure
+ * user ID or SID. This use of Gatekeeper, which is separate from the use of GateKeeper that may
+ * be used in the LSKF-based protector, makes it so that unlocking the synthetic password
+ * generates a HardwareAuthToken (but only when the user has LSKF). That HardwareAuthToken can
+ * be provided to KeyMint to authorize the use of the user's authentication-bound Keystore keys.
*
* Files stored on disk for each user:
* For the SP itself, stored under NULL_PROTECTOR_ID:
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 1b7d1ba..9a0b391 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -208,7 +208,7 @@
// network is the system default. So, if the VPN is up and underlying network
// (e.g., wifi) disconnects, CS will inform apps that the VPN's capabilities have
// changed to match the new default network (e.g., cell).
- mVpn.startLegacyVpnPrivileged(mProfile, network, egressProp);
+ mVpn.startLegacyVpnPrivileged(mProfile);
} catch (IllegalStateException e) {
mAcceptedEgressIface = null;
Log.e(TAG, "Failed to start VPN", e);
diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java
index a5a934f..681d1a0 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -327,10 +327,10 @@
/**
* Notify our observers of a change in the data activity state of the interface
*/
- private void notifyInterfaceClassActivity(int type, boolean isActive, long tsNanos,
+ private void notifyInterfaceClassActivity(int label, boolean isActive, long tsNanos,
int uid) {
invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
- type, isActive, tsNanos, uid));
+ label, isActive, tsNanos, uid));
}
// Sync the state of the given chain with the native daemon.
@@ -1059,17 +1059,25 @@
Log.w(TAG, "setDataSaverMode(): already " + mDataSaverMode);
return true;
}
- Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "bandwidthEnableDataSaver");
+ Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setDataSaverModeEnabled");
try {
- final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
- if (changed) {
+ if (SdkLevel.isAtLeastV()) {
+ // setDataSaverEnabled throws if it fails to set data saver.
+ mContext.getSystemService(ConnectivityManager.class)
+ .setDataSaverEnabled(enable);
mDataSaverMode = enable;
+ return true;
} else {
- Log.w(TAG, "setDataSaverMode(" + enable + "): netd command silently failed");
+ final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
+ if (changed) {
+ mDataSaverMode = enable;
+ } else {
+ Log.e(TAG, "setDataSaverMode(" + enable + "): failed to set iptables");
+ }
+ return changed;
}
- return changed;
- } catch (RemoteException e) {
- Log.w(TAG, "setDataSaverMode(" + enable + "): netd command failed", e);
+ } catch (RemoteException | IllegalStateException e) {
+ Log.e(TAG, "setDataSaverMode(" + enable + "): failed with exception", e);
return false;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
index e5616d0..ab0d0d2 100644
--- a/services/core/java/com/android/server/os/NativeTombstoneManager.java
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -61,6 +61,7 @@
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.locks.ReentrantLock;
/**
* A class to manage native tombstones.
@@ -74,6 +75,8 @@
private final Handler mHandler;
private final TombstoneWatcher mWatcher;
+ private final ReentrantLock mTmpFileLock = new ReentrantLock();
+
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -112,7 +115,12 @@
// Clean up temporary files if they made it this far (e.g. if system server crashes).
if (filename.endsWith(".tmp")) {
- path.delete();
+ mTmpFileLock.lock();
+ try {
+ path.delete();
+ } finally {
+ mTmpFileLock.unlock();
+ }
return;
}
@@ -128,7 +136,7 @@
if (parsedTombstone.isPresent()) {
processName = parsedTombstone.get().getProcessName();
}
- BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName);
+ BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName, mTmpFileLock);
}
private Optional<TombstoneFile> handleProtoTombstone(File path, boolean addToList) {
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index f95f7bc..0803bb6 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -122,7 +122,7 @@
StorageManagerInternal.class);
for (UserInfo user : umInternal.getUsers(false /*excludeDying*/)) {
final int flags;
- if (StorageManager.isUserKeyUnlocked(user.id)
+ if (StorageManager.isCeStorageUnlocked(user.id)
&& smInternal.isCeStoragePrepared(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
@@ -404,7 +404,7 @@
// First look for stale data that doesn't belong, and check if things
// have changed since we did our last restorecon
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
- if (StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId)) {
+ if (StorageManager.isFileEncrypted() && !StorageManager.isCeStorageUnlocked(userId)) {
throw new RuntimeException(
"Yikes, someone asked us to reconcile CE storage while " + userId
+ " was still locked; this would have caused massive data loss!");
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 78f1fa6..723a3ae6 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -3538,7 +3538,7 @@
@Override
public int getPackageStartability(boolean safeMode, @NonNull String packageName, int callingUid,
@UserIdInt int userId) {
- final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
+ final boolean ceStorageUnlocked = StorageManager.isCeStorageUnlocked(userId);
final PackageStateInternal ps = getPackageStateInternal(packageName);
if (ps == null || shouldFilterApplication(ps, callingUid, userId)
|| !ps.getUserStateOrDefault(userId).isInstalled()) {
@@ -3553,7 +3553,7 @@
return PackageManagerService.PACKAGE_STARTABILITY_FROZEN;
}
- if (!userKeyUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.getPkg())) {
+ if (!ceStorageUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.getPkg())) {
return PackageManagerService.PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED;
}
return PackageManagerService.PACKAGE_STARTABILITY_OK;
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index fcaaa90..c33af9b 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -160,7 +160,7 @@
pkg = snapshot.resolveInternalPackageName(pkg,
PackageManager.VERSION_CODE_HIGHEST);
- pw.println(mPermissionManager.checkPermission(perm, pkg, user));
+ pw.println(mPermissionManager.checkPermission(pkg, perm, user));
return;
} else if ("l".equals(cmd) || "libraries".equals(cmd)) {
dumpState.setDump(DumpState.DUMP_LIBS);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 8080e40..988a32f 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -486,20 +486,30 @@
pkgSetting.setLoadingProgress(1f);
}
+ // TODO: passes the package name as an argument in a message to the handler for V+
+ // so we don't need to rely on creating lambda objects so frequently.
+ if (UpdateOwnershipHelper.hasValidOwnershipDenyList(pkgSetting)) {
+ mPm.mHandler.post(() -> handleUpdateOwnerDenyList(pkgSetting));
+ }
+ return pkg;
+ }
+
+ private void handleUpdateOwnerDenyList(PackageSetting pkgSetting) {
ArraySet<String> listItems = mUpdateOwnershipHelper.readUpdateOwnerDenyList(pkgSetting);
if (listItems != null && !listItems.isEmpty()) {
- mUpdateOwnershipHelper.addToUpdateOwnerDenyList(pkgSetting.getPackageName(), listItems);
- for (String unownedPackage : listItems) {
- PackageSetting unownedSetting = mPm.mSettings.getPackageLPr(unownedPackage);
- SystemConfig config = SystemConfig.getInstance();
- if (unownedSetting != null
- && config.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) {
- unownedSetting.setUpdateOwnerPackage(null);
+ mUpdateOwnershipHelper.addToUpdateOwnerDenyList(pkgSetting.getPackageName(),
+ listItems);
+ SystemConfig config = SystemConfig.getInstance();
+ synchronized (mPm.mLock) {
+ for (String unownedPackage : listItems) {
+ PackageSetting unownedSetting = mPm.mSettings.getPackageLPr(unownedPackage);
+ if (unownedSetting != null
+ && config.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) {
+ unownedSetting.setUpdateOwnerPackage(null);
+ }
}
}
}
-
- return pkg;
}
/**
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 9ac983d..c401dab 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -24,6 +24,7 @@
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageStats;
+import android.os.Binder;
import android.os.Build;
import android.os.CreateAppDataArgs;
import android.os.CreateAppDataResult;
@@ -34,9 +35,11 @@
import android.os.ServiceManager;
import android.os.storage.CrateMetadata;
import android.text.format.DateUtils;
+import android.util.EventLog;
import android.util.Slog;
import com.android.internal.os.BackgroundThread;
+import com.android.server.EventLogTags;
import com.android.server.SystemService;
import dalvik.system.BlockGuard;
@@ -438,6 +441,26 @@
if (!checkBeforeRemote()) return;
try {
mInstalld.clearAppData(uuid, packageName, userId, flags, ceDataInode);
+
+ final StackTraceElement[] elements = Thread.currentThread().getStackTrace();
+ String className;
+ String methodName;
+ String fileName;
+ int lineNumber;
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ EventLog.writeEvent(EventLogTags.INSTALLER_CLEAR_APP_DATA_CALLER, pid, uid, packageName,
+ flags);
+ // Skip the first two elements since they are always the same, ie
+ // Thread#getStackTrace() and VMStack#getThreadStackTrace()
+ for (int i = 2; i < elements.length; i++) {
+ className = elements[i].getClassName();
+ methodName = elements[i].getMethodName();
+ fileName = elements[i].getFileName();
+ lineNumber = elements[i].getLineNumber();
+ EventLog.writeEvent(EventLogTags.INSTALLER_CLEAR_APP_DATA_CALL_STACK, methodName,
+ className, fileName, lineNumber);
+ }
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index fef53d1..4780ed1 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -198,7 +198,8 @@
// If we're moving app data around, we need all the users unlocked
if (moveCompleteApp) {
for (int userId : installedUserIds) {
- if (StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId)) {
+ if (StorageManager.isFileEncrypted()
+ && !StorageManager.isCeStorageUnlocked(userId)) {
freezer.close();
throw new PackageManagerException(MOVE_FAILED_LOCKED_USER,
"User " + userId + " must be unlocked");
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 6a2ddc8..ea082cf 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -159,6 +159,9 @@
if (pkgSetting.getPkg().isCoreApp()) {
throw new IllegalStateException("Found a core app that's not important");
}
+ // Use REASON_FIRST_BOOT to query "pm.dexopt.first-boot" for the compiler filter, but
+ // the reason itself won't make it into the actual compiler reason because it will be
+ // overridden in otapreopt.cpp.
mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.getPkg(), pkgSetting,
PackageManagerService.REASON_FIRST_BOOT));
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 20bd56d..a1c435a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3460,7 +3460,7 @@
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
final int flags;
- if (StorageManager.isUserKeyUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
+ if (StorageManager.isCeStorageUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(userId)) {
flags = StorageManager.FLAG_STORAGE_DE;
@@ -4729,6 +4729,9 @@
throw new SecurityException("Cannot clear data for a protected package: "
+ packageName);
}
+ final int callingPid = Binder.getCallingPid();
+ EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid,
+ packageName);
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
@@ -4861,6 +4864,9 @@
/* checkShell= */ false, "delete application cache files");
final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.ACCESS_INSTANT_APPS);
+ final int callingPid = Binder.getCallingPid();
+ EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid,
+ packageName);
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 8f8f437..8961923 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -183,7 +183,7 @@
StorageManagerInternal.class);
for (UserInfo user : mPm.mUserManager.getUsers(false /* includeDying */)) {
final int flags;
- if (StorageManager.isUserKeyUnlocked(user.id)
+ if (StorageManager.isCeStorageUnlocked(user.id)
&& smInternal.isCeStoragePrepared(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
diff --git a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
index 43752f3..adac68b 100644
--- a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
+++ b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
@@ -48,7 +48,7 @@
private final Object mLock = new Object();
- private static boolean hasValidOwnershipDenyList(PackageSetting pkgSetting) {
+ static boolean hasValidOwnershipDenyList(PackageSetting pkgSetting) {
AndroidPackage pkg = pkgSetting.getPkg();
// we're checking for uses-permission for these priv permissions instead of grant as we're
// only considering system apps to begin with, so presumed to be granted.
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index 6d58d34..8adb566 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -23,10 +23,10 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.RecoverySystem;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -35,6 +35,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.Slogf;
import java.io.File;
import java.io.IOException;
@@ -43,7 +44,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
/**
* Helper class for preparing and destroying user storage
@@ -65,31 +65,37 @@
/**
* Prepare storage areas for given user on all mounted devices.
*/
- void prepareUserData(int userId, int userSerial, int flags) {
+ void prepareUserData(UserInfo userInfo, int flags) {
synchronized (mInstallLock) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
/*
* Internal storage must be prepared before adoptable storage, since the user's volume
* keys are stored in their internal storage.
*/
- prepareUserDataLI(null /* internal storage */, userId, userSerial, flags, true);
+ prepareUserDataLI(null /* internal storage */, userInfo, flags, true);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
if (volumeUuid != null) {
- prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
+ prepareUserDataLI(volumeUuid, userInfo, flags, true);
}
}
}
}
- private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
+ private void prepareUserDataLI(String volumeUuid, UserInfo userInfo, int flags,
boolean allowRecover) {
- // Prepare storage and verify that serial numbers are consistent; if
- // there's a mismatch we need to destroy to avoid leaking data
+ final int userId = userInfo.id;
+ final int userSerial = userInfo.serialNumber;
final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ final boolean isNewUser = userInfo.lastLoggedInTime == 0;
+ Slogf.d(TAG, "Preparing user data; volumeUuid=%s, userId=%d, flags=0x%x, isNewUser=%s",
+ volumeUuid, userId, flags, isNewUser);
try {
+ // Prepare CE and/or DE storage.
storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
+ // Ensure that the data directories of a removed user with the same ID are not being
+ // reused. New users must get fresh data directories, to avoid leaking data.
if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
enforceSerialNumber(getDataUserDeDirectory(volumeUuid, userId), userSerial);
if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
@@ -103,9 +109,10 @@
}
}
+ // Prepare the app data directories.
mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
- // CE storage is available after they are prepared.
+ // If applicable, record that the system user's CE storage has been prepared.
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 &&
(userId == UserHandle.USER_SYSTEM)) {
String propertyName = "sys.user." + userId + ".ce_available";
@@ -113,20 +120,31 @@
SystemProperties.set(propertyName, "true");
}
} catch (Exception e) {
- logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
- + " because we failed to prepare: " + e);
- destroyUserDataLI(volumeUuid, userId, flags);
-
+ // Failed to prepare user data. For new users, specifically users that haven't ever
+ // been unlocked, destroy the user data, and try again (if not already retried). This
+ // might be effective at resolving some errors, such as stale directories from a reused
+ // user ID. Don't auto-destroy data for existing users, since issues with existing
+ // users might be fixable via an OTA without having to wipe the user's data.
+ if (isNewUser) {
+ logCriticalInfo(Log.ERROR, "Destroying user " + userId + " on volume " + volumeUuid
+ + " because we failed to prepare: " + e);
+ destroyUserDataLI(volumeUuid, userId, flags);
+ } else {
+ logCriticalInfo(Log.ERROR, "Failed to prepare user " + userId + " on volume "
+ + volumeUuid + ": " + e);
+ }
if (allowRecover) {
// Try one last time; if we fail again we're really in trouble
- prepareUserDataLI(volumeUuid, userId, userSerial,
- flags | StorageManager.FLAG_STORAGE_DE, false);
+ prepareUserDataLI(volumeUuid, userInfo, flags | StorageManager.FLAG_STORAGE_DE,
+ false);
} else {
+ // If internal storage of the system user fails to prepare on first boot, then
+ // things are *really* broken, so we might as well reboot to recovery right away.
try {
Log.wtf(TAG, "prepareUserData failed for user " + userId, e);
- if (userId == UserHandle.USER_SYSTEM) {
+ if (isNewUser && userId == UserHandle.USER_SYSTEM && volumeUuid == null) {
RecoverySystem.rebootPromptAndWipeUserData(mContext,
- "prepareUserData failed for system user");
+ "failed to prepare internal storage for system user");
}
} catch (IOException e2) {
throw new RuntimeException("error rebooting into recovery", e2);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e5c19f5..6814848 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1348,7 +1348,7 @@
}
final boolean needToShowConfirmCredential = !dontAskCredential
&& mLockPatternUtils.isSecure(userId)
- && (!hasUnifiedChallenge || !StorageManager.isUserKeyUnlocked(userId));
+ && (!hasUnifiedChallenge || !StorageManager.isCeStorageUnlocked(userId));
if (needToShowConfirmCredential) {
if (onlyIfCredentialNotRequired) {
return false;
@@ -4914,8 +4914,7 @@
// unlocked. We do this to ensure that CE storage isn't prepared before the CE key is
// saved to disk. This also matches what is done for user 0.
t.traceBegin("prepareUserData");
- mUserDataPreparer.prepareUserData(userId, userInfo.serialNumber,
- StorageManager.FLAG_STORAGE_DE);
+ mUserDataPreparer.prepareUserData(userInfo, StorageManager.FLAG_STORAGE_DE);
t.traceEnd();
t.traceBegin("LSS.createNewUser");
@@ -6199,12 +6198,11 @@
}
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("onBeforeStartUser-" + userId);
- final int userSerial = userInfo.serialNumber;
// Migrate only if build fingerprints mismatch
boolean migrateAppsData = !PackagePartitions.FINGERPRINT.equals(
userInfo.lastLoggedInFingerprint);
t.traceBegin("prepareUserData");
- mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
+ mUserDataPreparer.prepareUserData(userInfo, StorageManager.FLAG_STORAGE_DE);
t.traceEnd();
t.traceBegin("reconcileAppsData");
getPackageManagerInternal().reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE,
@@ -6230,14 +6228,13 @@
if (userInfo == null) {
return;
}
- final int userSerial = userInfo.serialNumber;
// Migrate only if build fingerprints mismatch
boolean migrateAppsData = !PackagePartitions.FINGERPRINT.equals(
userInfo.lastLoggedInFingerprint);
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("prepareUserData-" + userId);
- mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+ mUserDataPreparer.prepareUserData(userInfo, StorageManager.FLAG_STORAGE_CE);
t.traceEnd();
StorageManagerInternal smInternal = LocalServices.getService(StorageManagerInternal.class);
@@ -6992,9 +6989,9 @@
synchronized (mUserStates) {
state = mUserStates.get(userId, -1);
}
- // Special case, in the stopping/shutdown state user key can still be unlocked
+ // Special case: in the stopping/shutdown state, CE storage can still be unlocked.
if (state == UserState.STATE_STOPPING || state == UserState.STATE_SHUTDOWN) {
- return StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isCeStorageUnlocked(userId);
}
return (state == UserState.STATE_RUNNING_UNLOCKING)
|| (state == UserState.STATE_RUNNING_UNLOCKED);
@@ -7011,9 +7008,9 @@
synchronized (mUserStates) {
state = mUserStates.get(userId, -1);
}
- // Special case, in the stopping/shutdown state user key can still be unlocked
+ // Special case: in the stopping/shutdown state, CE storage can still be unlocked.
if (state == UserState.STATE_STOPPING || state == UserState.STATE_SHUTDOWN) {
- return StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isCeStorageUnlocked(userId);
}
return state == UserState.STATE_RUNNING_UNLOCKED;
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index d0c346a..57f4a5d 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -337,7 +337,8 @@
0, // deprecated, used to be durationIncludingSleepMs
0, // optimizedPackagesCount
0, // packagesDependingOnBootClasspathCount
- 0); // totalPackagesCount
+ 0, // totalPackagesCount
+ ArtStatsLog.BACKGROUND_DEXOPT_JOB_ENDED__PASS__PASS_UNKNOWN);
}
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 297ad73..c24d523 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1001,7 +1001,9 @@
}
synchronized (mLock) {
- mAttributions.put(source.getToken(), source);
+ // Change the token for the AttributionSource we're storing, so that we don't store
+ // a strong reference to the original token inside the map itself.
+ mAttributions.put(source.getToken(), source.withDefaultToken());
}
}
@@ -1009,7 +1011,7 @@
synchronized (mLock) {
final AttributionSource cachedSource = mAttributions.get(source.getToken());
if (cachedSource != null) {
- return cachedSource.equals(source);
+ return cachedSource.equalsExceptToken(source);
}
return false;
}
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 0e99e7e..bd9738e 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -1315,7 +1315,8 @@
}
private boolean isTaskStartedFromLauncher(String currPkg, TaskInfo taskInfo) {
- return currPkg.equals(taskInfo.baseActivity.getPackageName())
+ return taskInfo.baseActivity != null
+ && currPkg.equals(taskInfo.baseActivity.getPackageName())
&& isLauncherIntent(taskInfo.baseIntent);
}
diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index a0e91ad..94340ec 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -2,4 +2,6 @@
santoscordon@google.com
philipjunker@google.com
-per-file ThermalManagerService.java=wvw@google.com
+per-file ThermalManagerService.java=file:/THERMAL_OWNERS
+per-file LowPowerStandbyController.java=qingxun@google.com
+per-file LowPowerStandbyControllerInternal.java=qingxun@google.com
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index cf1bfc3..fbfe291 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -20,6 +20,7 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.power"},
+ {"exclude-filter": "com.android.server.power.BatteryStatsTests"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
@@ -38,7 +39,8 @@
{
"name": "FrameworksServicesTests",
"options": [
- {"include-filter": "com.android.server.power"}
+ {"include-filter": "com.android.server.power"},
+ {"exclude-filter": "com.android.server.power.BatteryStatsTests"}
]
}
]
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 27329e2..5dff9ef 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -8203,18 +8203,18 @@
@GuardedBy("mBsi")
private void ensureMultiStateCounters(long timestampMs) {
- if (mProcStateTimeMs != null) {
- return;
+ if (mProcStateTimeMs == null) {
+ mProcStateTimeMs =
+ new TimeInFreqMultiStateCounter(mBsi.mOnBatteryTimeBase,
+ PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
+ timestampMs);
}
-
- mProcStateTimeMs =
- new TimeInFreqMultiStateCounter(mBsi.mOnBatteryTimeBase,
- PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
- timestampMs);
- mProcStateScreenOffTimeMs =
- new TimeInFreqMultiStateCounter(mBsi.mOnBatteryScreenOffTimeBase,
- PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
- timestampMs);
+ if (mProcStateScreenOffTimeMs == null) {
+ mProcStateScreenOffTimeMs =
+ new TimeInFreqMultiStateCounter(mBsi.mOnBatteryScreenOffTimeBase,
+ PROC_STATE_TIME_COUNTER_STATE_COUNT, mBsi.getCpuFreqCount(),
+ timestampMs);
+ }
}
@GuardedBy("mBsi")
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 7b0fe9a..a01bac6 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -266,10 +266,10 @@
}
/**
- * @return {@code true} iff. {@code userId} is locked on an FBE device.
+ * @return {@code true} iff the credential-encrypted storage for {@code userId} is locked.
*/
@VisibleForTesting
public boolean isUserCredentialLocked(int userId) {
- return StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isFileEncrypted() && !StorageManager.isCeStorageUnlocked(userId);
}
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 9128974..cc892a0 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1281,12 +1281,19 @@
private void addBytesTransferByTagAndMeteredAtoms(@NonNull NetworkStatsExt statsExt,
@NonNull List<StatsEvent> pulledData) {
+ // Workaround for 5G NSA mode, see {@link NetworkStatsManager#NETWORK_TYPE_5G_NSA}.
+ // 5G NSA mode means the primary cell is LTE with a secondary connection to an
+ // NR cell. To mitigate risk, NetworkStats is currently storing this state as
+ // a fake RAT type rather than storing the boolean separately.
+ final boolean is5GNsa = statsExt.ratType == NetworkStatsManager.NETWORK_TYPE_5G_NSA;
+
for (NetworkStats.Entry entry : statsExt.stats) {
pulledData.add(FrameworkStatsLog.buildStatsEvent(
FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.getUid(),
entry.getMetered() == NetworkStats.METERED_YES, entry.getTag(),
entry.getRxBytes(), entry.getRxPackets(), entry.getTxBytes(),
- entry.getTxPackets()));
+ entry.getTxPackets(),
+ is5GNsa ? TelephonyManager.NETWORK_TYPE_LTE : statsExt.ratType));
}
}
@@ -1414,6 +1421,7 @@
final NetworkStats nonTaggedStats =
NetworkStatsUtils.fromPublicNetworkStats(queryNonTaggedStats);
+ queryNonTaggedStats.close();
if (!includeTags) return nonTaggedStats;
final android.app.usage.NetworkStats queryTaggedStats =
@@ -1422,6 +1430,7 @@
currentTimeInMillis);
final NetworkStats taggedStats =
NetworkStatsUtils.fromPublicNetworkStats(queryTaggedStats);
+ queryTaggedStats.close();
return nonTaggedStats.add(taggedStats);
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 635e11b..327f74f 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -829,7 +829,7 @@
}
final boolean trusted;
- if (android.security.Flags.fixUnlockedDeviceRequiredKeys()) {
+ if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()) {
trusted = getUserTrustStateInner(id) == TrustState.TRUSTED;
} else {
trusted = aggregateIsTrusted(id);
@@ -867,21 +867,19 @@
mDeviceLockedForUser.put(userId, locked);
}
if (changed) {
- dispatchDeviceLocked(userId, locked);
- Authorization.onLockScreenEvent(locked, userId, null,
- getBiometricSids(userId));
+ notifyTrustAgentsOfDeviceLockState(userId, locked);
+ notifyKeystoreOfDeviceLockState(userId, locked);
// Also update the user's profiles who have unified challenge, since they
// share the same unlocked state (see {@link #isDeviceLocked(int)})
for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) {
if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(profileHandle)) {
- Authorization.onLockScreenEvent(locked, profileHandle, null,
- getBiometricSids(profileHandle));
+ notifyKeystoreOfDeviceLockState(profileHandle, locked);
}
}
}
}
- private void dispatchDeviceLocked(int userId, boolean isLocked) {
+ private void notifyTrustAgentsOfDeviceLockState(int userId, boolean isLocked) {
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo agent = mActiveAgents.valueAt(i);
if (agent.userId == userId) {
@@ -894,6 +892,17 @@
}
}
+ private void notifyKeystoreOfDeviceLockState(int userId, boolean isLocked) {
+ if (isLocked) {
+ Authorization.onDeviceLocked(userId, getBiometricSids(userId));
+ } else {
+ // Notify Keystore that the device is now unlocked for the user. Note that for unlocks
+ // with LSKF, this is redundant with the call from LockSettingsService which provides
+ // the password. However, for unlocks with biometric or trust agent, this is required.
+ Authorization.onDeviceUnlocked(userId, /* password= */ null);
+ }
+ }
+
private void dispatchEscrowTokenActivatedLocked(long handle, int userId) {
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo agent = mActiveAgents.valueAt(i);
@@ -1427,10 +1436,10 @@
}
}
- private long[] getBiometricSids(int userId) {
+ private @NonNull long[] getBiometricSids(int userId) {
BiometricManager biometricManager = mContext.getSystemService(BiometricManager.class);
if (biometricManager == null) {
- return null;
+ return new long[0];
}
return biometricManager.getAuthenticatorIds(userId);
}
@@ -1647,8 +1656,22 @@
fout.printf(" User \"%s\" (id=%d, flags=%#x)",
user.name, user.id, user.flags);
if (!user.supportsSwitchToByUser()) {
- fout.println("(managed profile)");
- fout.println(" disabled because switching to this user is not possible.");
+ final boolean locked;
+ if (user.isProfile()) {
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)) {
+ fout.print(" (profile with separate challenge)");
+ locked = isDeviceLockedInner(user.id);
+ } else {
+ fout.print(" (profile with unified challenge)");
+ locked = isDeviceLockedInner(resolveProfileParent(user.id));
+ }
+ } else {
+ fout.println(" (user that cannot be switched to)");
+ locked = isDeviceLockedInner(user.id);
+ }
+ fout.println(": deviceLocked=" + dumpBool(locked));
+ fout.println(
+ " Trust agents disabled because switching to this user is not possible.");
return;
}
if (isCurrent) {
@@ -1715,8 +1738,7 @@
mDeviceLockedForUser.put(userId, locked);
}
- Authorization.onLockScreenEvent(locked, userId, null,
- getBiometricSids(userId));
+ notifyKeystoreOfDeviceLockState(userId, locked);
if (locked) {
try {
diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS
index be91611d..fbc0b56 100644
--- a/services/core/java/com/android/server/utils/OWNERS
+++ b/services/core/java/com/android/server/utils/OWNERS
@@ -10,3 +10,8 @@
per-file Watcher.java = shombert@google.com
per-file EventLogger.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS
per-file EventLogger.java = jmtrivi@google.com
+
+# Bug component : 158088 = per-file AnrTimer*.java
+per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS
+
+per-file flags.aconfig = file:/PERFORMANCE_OWNERS
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index d958222..9213d96 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.net.vcn.FeatureFlags;
+import android.net.vcn.FeatureFlagsImpl;
import android.os.Looper;
import java.util.Objects;
@@ -31,6 +33,7 @@
@NonNull private final Context mContext;
@NonNull private final Looper mLooper;
@NonNull private final VcnNetworkProvider mVcnNetworkProvider;
+ @NonNull private final FeatureFlags mFeatureFlags;
private final boolean mIsInTestMode;
public VcnContext(
@@ -42,6 +45,9 @@
mLooper = Objects.requireNonNull(looper, "Missing looper");
mVcnNetworkProvider = Objects.requireNonNull(vcnNetworkProvider, "Missing networkProvider");
mIsInTestMode = isInTestMode;
+
+ // Auto-generated class
+ mFeatureFlags = new FeatureFlagsImpl();
}
@NonNull
@@ -63,6 +69,11 @@
return mIsInTestMode;
}
+ @NonNull
+ public FeatureFlags getFeatureFlags() {
+ return mFeatureFlags;
+ }
+
/**
* Verifies that the caller is running on the VcnContext Thread.
*
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index d480ddb..54c97dd 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1222,6 +1222,14 @@
@VisibleForTesting(visibility = Visibility.PRIVATE)
void setSafeModeAlarm() {
+ final boolean isFlagSafeModeConfigEnabled = mVcnContext.getFeatureFlags().safeModeConfig();
+ logVdbg("isFlagSafeModeConfigEnabled " + isFlagSafeModeConfigEnabled);
+
+ if (isFlagSafeModeConfigEnabled && !mConnectionConfig.isSafeModeEnabled()) {
+ logVdbg("setSafeModeAlarm: safe mode disabled");
+ return;
+ }
+
logVdbg("Setting safe mode alarm; mCurrentToken: " + mCurrentToken);
// Only schedule a NEW alarm if none is already set.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 18a6254..82a954d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4214,6 +4214,9 @@
Slog.v(TAG_APP, "Keeping entry during removeHistory for activity " + this);
}
}
+ if (task != null && task.mKillProcessesOnDestroyed) {
+ mTaskSupervisor.removeTimeoutOfKillProcessesOnProcessDied(this, task);
+ }
// upgrade transition trigger to task if this is the last activity since it means we are
// closing the task.
final WindowContainer trigger = remove && task != null && task.getChildCount() == 1
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index b34ae19..ecdca93 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1891,7 +1891,7 @@
// DestroyActivityItem may be called first.
final ActivityRecord top = task.getTopMostActivity();
if (top != null && top.finishing && !top.mAppStopped && top.lastVisibleTime > 0
- && !task.mKillProcessesOnDestroyed) {
+ && !task.mKillProcessesOnDestroyed && top.hasProcess()) {
task.mKillProcessesOnDestroyed = true;
mHandler.sendMessageDelayed(
mHandler.obtainMessage(KILL_TASK_PROCESSES_TIMEOUT_MSG, task),
@@ -1901,8 +1901,26 @@
killTaskProcessesIfPossible(task);
}
+ void removeTimeoutOfKillProcessesOnProcessDied(@NonNull ActivityRecord r, @NonNull Task task) {
+ if (r.packageName.equals(task.getBasePackageName())) {
+ task.mKillProcessesOnDestroyed = false;
+ mHandler.removeMessages(KILL_TASK_PROCESSES_TIMEOUT_MSG, task);
+ }
+ }
+
void killTaskProcessesOnDestroyedIfNeeded(Task task) {
if (task == null || !task.mKillProcessesOnDestroyed) return;
+ final int[] numDestroyingActivities = new int[1];
+ task.forAllActivities(r -> {
+ if (r.finishing && r.lastVisibleTime > 0 && r.attachedToProcess()) {
+ numDestroyingActivities[0]++;
+ }
+ });
+ if (numDestroyingActivities[0] > 1) {
+ // Skip if there are still destroying activities. When the last activity reports
+ // destroyed, the number will be 1 to proceed the kill.
+ return;
+ }
mHandler.removeMessages(KILL_TASK_PROCESSES_TIMEOUT_MSG, task);
killTaskProcessesIfPossible(task);
}
@@ -2763,7 +2781,7 @@
} break;
case KILL_TASK_PROCESSES_TIMEOUT_MSG: {
final Task task = (Task) msg.obj;
- if (task.mKillProcessesOnDestroyed) {
+ if (task.mKillProcessesOnDestroyed && task.hasActivity()) {
Slog.i(TAG, "Destroy timeout of remove-task, attempt to kill " + task);
killTaskProcessesIfPossible(task);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7addaa2..1c8770b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4668,6 +4668,17 @@
scheduleAnimation();
mWmService.mH.post(() -> InputMethodManagerInternal.get().onImeParentChanged());
+ } else if (mImeControlTarget != null && mImeControlTarget == mImeLayeringTarget) {
+ // Even if the IME surface parent is not changed, the layer target belonging to the
+ // parent may have changes. Then attempt to reassign if the IME control target is
+ // possible to be the relative layer.
+ final SurfaceControl lastRelativeLayer = mImeWindowsContainer.getLastRelativeLayer();
+ if (lastRelativeLayer != mImeLayeringTarget.mSurfaceControl) {
+ assignRelativeLayerForIme(getSyncTransaction(), false /* forceUpdate */);
+ if (lastRelativeLayer != mImeWindowsContainer.getLastRelativeLayer()) {
+ scheduleAnimation();
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index f8c39d0..cd70447 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -18,5 +18,8 @@
yunfanc@google.com
wilsonshih@google.com
-per-file BackgroundActivityStartController.java = set noparent
-per-file BackgroundActivityStartController.java = brufino@google.com, topjohnwu@google.com, achim@google.com, ogunwale@google.com, louischang@google.com, lus@google.com
+# Files related to background activity launches
+per-file Background*Start* = set noparent
+per-file Background*Start* = file:/BAL_OWNERS
+per-file Background*Start* = ogunwale@google.com, louischang@google.com
+
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 9a0e47d..eba9bf6 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1637,8 +1637,8 @@
return false;
}
- if (!StorageManager.isUserKeyUnlocked(mCurrentUser)) {
- // Can't launch home on secondary display areas if device is still locked.
+ if (!StorageManager.isCeStorageUnlocked(mCurrentUser)) {
+ // Can't launch home on secondary display areas if CE storage is still locked.
return false;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3063d46..f2346e6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4626,7 +4626,7 @@
// Expanding pip into new rotation, so create a rotation leash
// until the display is rotated.
topActivity.getOrCreateFixedRotationLeash(
- topActivity.getSyncTransaction());
+ topActivity.getPendingTransaction());
}
lastParentBeforePip.moveToFront("movePinnedActivityToOriginalTask");
}
@@ -6135,6 +6135,7 @@
// Avoid resuming activities on secondary displays since we don't want bubble
// activities to be resumed while bubble is still collapsed.
// TODO(b/113840485): Having keyguard going away state for secondary displays.
+ && display != null
&& display.isDefaultDisplay) {
return false;
}
@@ -6510,11 +6511,11 @@
mActivityType = ACTIVITY_TYPE_STANDARD;
}
- if (mActivityType != ACTIVITY_TYPE_STANDARD
+ if (!DisplayContent.alwaysCreateRootTask(tda.getWindowingMode(), mActivityType)
&& mActivityType != ACTIVITY_TYPE_UNDEFINED) {
- // For now there can be only one root task of a particular non-standard activity
- // type on a display. So, get that ignoring whatever windowing mode it is
- // currently in.
+ // Only Recents or Standard activity types are allowed to have more than one
+ // root task on a display, this is independent of whatever windowing mode it
+ // is currently in.
Task rootTask = tda.getRootTask(WINDOWING_MODE_UNDEFINED, mActivityType);
if (rootTask != null) {
throw new IllegalArgumentException("Root task=" + rootTask + " of activityType="
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 0152666..4cab3a2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2635,6 +2635,10 @@
return mLastLayer;
}
+ SurfaceControl getLastRelativeLayer() {
+ return mLastRelativeToLayer;
+ }
+
protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
if (mSurfaceFreezer.hasLeash()) {
// When the freezer has created animation leash parent for the window, set the layer
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 06978a5..0a5b9eb 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3276,7 +3276,9 @@
// just kill it. And if it is a window of foreground activity, the activity can be
// restarted automatically if needed.
Slog.w(TAG, "Exception thrown during dispatchAppVisibility " + this, e);
- android.os.Process.killProcess(mSession.mPid);
+ if (android.os.Process.getUidForPid(mSession.mPid) == mSession.mUid) {
+ android.os.Process.killProcess(mSession.mPid);
+ }
}
}
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index bc29490..fd9d4a4 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -29,3 +29,6 @@
per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS
per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
+
+# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java
+per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 81a5472..a8e6f68 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -910,7 +910,7 @@
constants().readLogsDisabledMarkerName),
0777, idFromMetadata(metadata), {})) {
//{.metadata = {metadata.data(), (IncFsSize)metadata.size()}})) {
- LOG(ERROR) << "Failed to make marker file for storageId: " << storageId;
+ LOG(ERROR) << "Failed to make marker file for storageId: " << storageId << " err: " << -err;
return;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3610542..f1f5d83 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1451,8 +1451,6 @@
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
- boolean isEmulator = SystemProperties.get("ro.boot.qemu").equals("1");
-
boolean isWatch = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WATCH);
@@ -2261,7 +2259,7 @@
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
|| mPackageManager.hasSystemFeature(
PackageManager.FEATURE_USB_ACCESSORY)
- || isEmulator) {
+ || Build.IS_EMULATOR) {
// Manage USB host and device support
t.traceBegin("StartUsbService");
mSystemServiceManager.startService(USB_SERVICE_CLASS);
diff --git a/services/midi/OWNERS b/services/midi/OWNERS
index f4d51f9..683cae1 100644
--- a/services/midi/OWNERS
+++ b/services/midi/OWNERS
@@ -1 +1,3 @@
philburk@google.com
+robertwu@google.com
+elaurent@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/proguard.flags b/services/proguard.flags
index e11e613..88561b4 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -14,13 +14,20 @@
}
# APIs referenced by dependent JAR files and modules
--keep @interface android.annotation.SystemApi
+# TODO(b/300514883): Pull @SystemApi keep rules from system-api.pro.
+-keep interface android.annotation.SystemApi
-keep @android.annotation.SystemApi class * {
public protected *;
}
-keepclasseswithmembers class * {
@android.annotation.SystemApi *;
}
+# Also ensure nested classes are kept. This is overly conservative, but handles
+# cases where such classes aren't explicitly marked @SystemApi.
+-if @android.annotation.SystemApi class *
+-keep public class <1>$** {
+ public protected *;
+}
# Derivatives of SystemService and other services created via reflection
-keep,allowoptimization,allowaccessmodification class * extends com.android.server.SystemService {
@@ -38,15 +45,16 @@
public static void write(...);
}
-# Binder interfaces
--keep,allowoptimization,allowaccessmodification class * extends android.os.IInterface
--keep,allowoptimization,allowaccessmodification class * extends android.os.IHwInterface
-
# Various classes subclassed in or referenced via JNI in ethernet-service
-keep public class android.net.** { *; }
-keep,allowoptimization,allowaccessmodification class com.android.net.module.util.* { *; }
-keep,allowoptimization,allowaccessmodification public class com.android.server.net.IpConfigStore { *; }
-keep,allowoptimization,allowaccessmodification public class com.android.server.net.BaseNetworkObserver { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.display.feature.DisplayManagerFlags { *; }
+-keep,allowoptimization,allowaccessmodification class android.app.admin.flags.FeatureFlagsImpl { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.ThreadPriorityBooster { *; }
+-keep,allowaccessmodification class android.app.admin.flags.Flags { *; }
# Referenced via CarServiceHelperService in car-frameworks-service (avoid removing)
-keep public class com.android.server.utils.Slogf { *; }
@@ -99,9 +107,6 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.input.InputManagerService {
<methods>;
}
--keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl {
- <methods>;
-}
-keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbHostManager {
*** usbDeviceRemoved(...);
*** usbDeviceAdded(...);
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
index afbe352..e5be4d9 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
@@ -21,6 +21,8 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -56,6 +58,7 @@
private static final int TEST_USER_SERIAL = 1000;
private static final int TEST_USER_ID = 10;
+ private static final UserInfo TEST_USER = new UserInfo();
private TestUserDataPreparer mUserDataPreparer;
@@ -72,6 +75,8 @@
@Before
public void setup() {
+ TEST_USER.id = TEST_USER_ID;
+ TEST_USER.serialNumber = TEST_USER_SERIAL;
Context ctx = InstrumentationRegistry.getContext();
FileUtils.deleteContents(ctx.getCacheDir());
mInstallLock = new Object();
@@ -92,8 +97,7 @@
userDeDir.mkdirs();
File systemDeDir = mUserDataPreparer.getDataSystemDeDirectory(TEST_USER_ID);
systemDeDir.mkdirs();
- mUserDataPreparer
- .prepareUserData(TEST_USER_ID, TEST_USER_SERIAL, StorageManager.FLAG_STORAGE_DE);
+ mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_DE);
verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_DE));
verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID),
@@ -110,8 +114,7 @@
userCeDir.mkdirs();
File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID);
systemCeDir.mkdirs();
- mUserDataPreparer
- .prepareUserData(TEST_USER_ID, TEST_USER_SERIAL, StorageManager.FLAG_STORAGE_CE);
+ mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_CE));
verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID),
@@ -123,6 +126,28 @@
}
@Test
+ public void testPrepareUserData_forNewUser_destroysOnFailure() throws Exception {
+ TEST_USER.lastLoggedInTime = 0;
+ doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock)
+ .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(TEST_USER_SERIAL),
+ eq(StorageManager.FLAG_STORAGE_CE));
+ mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
+ verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID),
+ eq(StorageManager.FLAG_STORAGE_CE));
+ }
+
+ @Test
+ public void testPrepareUserData_forExistingUser_doesNotDestroyOnFailure() throws Exception {
+ TEST_USER.lastLoggedInTime = System.currentTimeMillis();
+ doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock)
+ .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(TEST_USER_SERIAL),
+ eq(StorageManager.FLAG_STORAGE_CE));
+ mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
+ verify(mStorageManagerMock, never()).destroyUserStorage(isNull(String.class),
+ eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE));
+ }
+
+ @Test
public void testDestroyUserData_De_DoesNotDestroyCe() throws Exception {
// Add file in CE storage
File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
new file mode 100644
index 0000000..a0beb17
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.annotation.NonNull;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.TestLooperManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.SparseArray;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.AlarmManagerInternal;
+import com.android.server.DropBoxManagerInternal;
+import com.android.server.LocalServices;
+import com.android.server.appop.AppOpsService;
+import com.android.server.wm.ActivityTaskManagerService;
+
+import org.junit.Rule;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public abstract class BaseBroadcastQueueTest {
+
+ static final int USER_GUEST = 11;
+
+ static final String PACKAGE_ANDROID = "android";
+ static final String PACKAGE_PHONE = "com.android.phone";
+ static final String PACKAGE_RED = "com.example.red";
+ static final String PACKAGE_GREEN = "com.example.green";
+ static final String PACKAGE_BLUE = "com.example.blue";
+ static final String PACKAGE_YELLOW = "com.example.yellow";
+ static final String PACKAGE_ORANGE = "com.example.orange";
+
+ static final String PROCESS_SYSTEM = "system";
+
+ static final String CLASS_RED = "com.example.red.Red";
+ static final String CLASS_GREEN = "com.example.green.Green";
+ static final String CLASS_BLUE = "com.example.blue.Blue";
+ static final String CLASS_YELLOW = "com.example.yellow.Yellow";
+ static final String CLASS_ORANGE = "com.example.orange.Orange";
+
+ static final BroadcastProcessQueue.BroadcastPredicate BROADCAST_PREDICATE_ANY =
+ (r, i) -> true;
+
+ @Rule
+ public final ApplicationExitInfoTest.ServiceThreadRule
+ mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
+
+ @Mock
+ AppOpsService mAppOpsService;
+ @Mock
+ PackageManagerInternal mPackageManagerInt;
+ @Mock
+ UsageStatsManagerInternal mUsageStatsManagerInt;
+ @Mock
+ DropBoxManagerInternal mDropBoxManagerInt;
+ @Mock
+ AlarmManagerInternal mAlarmManagerInt;
+ @Mock
+ ProcessList mProcessList;
+
+ Context mContext;
+ ActivityManagerService mAms;
+ BroadcastConstants mConstants;
+ BroadcastSkipPolicy mSkipPolicy;
+ HandlerThread mHandlerThread;
+ TestLooperManager mLooper;
+ AtomicInteger mNextPid;
+
+ /**
+ * Map from PID to registered registered runtime receivers.
+ */
+ SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
+
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mHandlerThread = new HandlerThread(getTag());
+ mHandlerThread.start();
+ // Pause all event processing until a test chooses to resume
+ mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+ .acquireLooperManager(mHandlerThread.getLooper()));
+ mNextPid = new AtomicInteger(100);
+
+ LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
+ LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+ LocalServices.removeServiceForTest(AlarmManagerInternal.class);
+ LocalServices.addService(AlarmManagerInternal.class, mAlarmManagerInt);
+ doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+ doNothing().when(mPackageManagerInt).setPackageStoppedState(any(), anyBoolean(), anyInt());
+ doAnswer((invocation) -> {
+ return getUidForPackage(invocation.getArgument(0));
+ }).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
+
+ final ActivityManagerService realAms = new ActivityManagerService(
+ new TestInjector(mContext), mServiceThreadRule.getThread());
+ realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+ realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+ realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
+ realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
+ realAms.mPackageManagerInt = mPackageManagerInt;
+ realAms.mUsageStatsService = mUsageStatsManagerInt;
+ realAms.mProcessesReady = true;
+ mAms = spy(realAms);
+
+ mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
+ doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
+ doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
+
+ mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
+ }
+
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ }
+
+ static int getUidForPackage(@NonNull String packageName) {
+ switch (packageName) {
+ case PACKAGE_ANDROID: return android.os.Process.SYSTEM_UID;
+ case PACKAGE_PHONE: return android.os.Process.PHONE_UID;
+ case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
+ case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
+ case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
+ case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4;
+ case PACKAGE_ORANGE: return android.os.Process.FIRST_APPLICATION_UID + 5;
+ default: throw new IllegalArgumentException();
+ }
+ }
+
+ static int getUidForPackage(@NonNull String packageName, int userId) {
+ return UserHandle.getUid(userId, getUidForPackage(packageName));
+ }
+
+ private class TestInjector extends ActivityManagerService.Injector {
+ TestInjector(Context context) {
+ super(context);
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
+ Handler handler) {
+ return mAppOpsService;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandlerThread.getThreadHandler();
+ }
+
+ @Override
+ public ProcessList getProcessList(ActivityManagerService service) {
+ return mProcessList;
+ }
+ }
+
+ abstract String getTag();
+
+ static ApplicationInfo makeApplicationInfo(String packageName) {
+ return makeApplicationInfo(packageName, packageName, UserHandle.USER_SYSTEM);
+ }
+
+ static ApplicationInfo makeApplicationInfo(String packageName, String processName, int userId) {
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = packageName;
+ ai.processName = processName;
+ ai.uid = getUidForPackage(packageName, userId);
+ return ai;
+ }
+
+ static ResolveInfo withPriority(ResolveInfo info, int priority) {
+ info.priority = priority;
+ return info;
+ }
+
+ static BroadcastFilter withPriority(BroadcastFilter filter, int priority) {
+ filter.setPriority(priority);
+ return filter;
+ }
+
+ static ResolveInfo makeManifestReceiver(String packageName, String name) {
+ return makeManifestReceiver(packageName, name, UserHandle.USER_SYSTEM);
+ }
+
+ static ResolveInfo makeManifestReceiver(String packageName, String name, int userId) {
+ return makeManifestReceiver(packageName, packageName, name, userId);
+ }
+
+ static ResolveInfo makeManifestReceiver(String packageName, String processName,
+ String name, int userId) {
+ final ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = new ActivityInfo();
+ ri.activityInfo.packageName = packageName;
+ ri.activityInfo.processName = processName;
+ ri.activityInfo.name = name;
+ ri.activityInfo.applicationInfo = makeApplicationInfo(packageName, processName, userId);
+ return ri;
+ }
+
+ BroadcastFilter makeRegisteredReceiver(ProcessRecord app) {
+ return makeRegisteredReceiver(app, 0);
+ }
+
+ BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
+ final ReceiverList receiverList = mRegisteredReceivers.get(app.getPid());
+ return makeRegisteredReceiver(receiverList, priority);
+ }
+
+ static BroadcastFilter makeRegisteredReceiver(ReceiverList receiverList, int priority) {
+ final IntentFilter filter = new IntentFilter();
+ filter.setPriority(priority);
+ final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
+ receiverList.app.info.packageName, null, null, null, receiverList.uid,
+ receiverList.userId, false, false, true);
+ receiverList.add(res);
+ return res;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index ff04728..2378416 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -31,17 +31,6 @@
import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_PRIORITIZED;
import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
-import static com.android.server.am.BroadcastQueueTest.CLASS_BLUE;
-import static com.android.server.am.BroadcastQueueTest.CLASS_GREEN;
-import static com.android.server.am.BroadcastQueueTest.CLASS_RED;
-import static com.android.server.am.BroadcastQueueTest.CLASS_YELLOW;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_BLUE;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_GREEN;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_RED;
-import static com.android.server.am.BroadcastQueueTest.PACKAGE_YELLOW;
-import static com.android.server.am.BroadcastQueueTest.getUidForPackage;
-import static com.android.server.am.BroadcastQueueTest.makeManifestReceiver;
-import static com.android.server.am.BroadcastQueueTest.withPriority;
import static com.android.server.am.BroadcastRecord.isReceiverEquals;
import static com.google.common.truth.Truth.assertThat;
@@ -74,17 +63,15 @@
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.BundleMerger;
import android.os.DropBoxManager;
-import android.os.HandlerThread;
import android.os.Process;
import android.os.SystemClock;
-import android.os.TestLooperManager;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -108,11 +95,12 @@
import java.util.Objects;
@SmallTest
-public final class BroadcastQueueModernImplTest {
+public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
+ private static final String TAG = "BroadcastQueueModernImplTest";
+
private static final int TEST_UID = android.os.Process.FIRST_APPLICATION_UID;
private static final int TEST_UID2 = android.os.Process.FIRST_APPLICATION_UID + 1;
- @Mock ActivityManagerService mAms;
@Mock ProcessRecord mProcess;
@Mock BroadcastProcessQueue mQueue1;
@@ -120,11 +108,6 @@
@Mock BroadcastProcessQueue mQueue3;
@Mock BroadcastProcessQueue mQueue4;
- HandlerThread mHandlerThread;
- TestLooperManager mLooper;
-
- BroadcastConstants mConstants;
- private BroadcastSkipPolicy mSkipPolicy;
BroadcastQueueModernImpl mImpl;
BroadcastProcessQueue mHead;
@@ -136,22 +119,12 @@
@Before
public void setUp() throws Exception {
- mHandlerThread = new HandlerThread(getClass().getSimpleName());
- mHandlerThread.start();
+ super.setUp();
- // Pause all event processing until a test chooses to resume
- mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
- .acquireLooperManager(mHandlerThread.getLooper()));
-
- mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
mConstants.DELAY_URGENT_MILLIS = -120_000;
mConstants.DELAY_NORMAL_MILLIS = 10_000;
mConstants.DELAY_CACHED_MILLIS = 120_000;
- mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
- doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
- doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
-
final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
public void addBroadcastToHistoryLocked(BroadcastRecord original) {
// Ignored
@@ -169,7 +142,12 @@
@After
public void tearDown() throws Exception {
- mHandlerThread.quit();
+ super.tearDown();
+ }
+
+ @Override
+ public String getTag() {
+ return TAG;
}
/**
@@ -225,11 +203,6 @@
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), false);
}
- private BroadcastRecord makeOrderedBroadcastRecord(Intent intent) {
- return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(),
- List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), true);
- }
-
private BroadcastRecord makeBroadcastRecord(Intent intent, List receivers) {
return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(), receivers, false);
}
@@ -246,8 +219,8 @@
private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options,
List receivers, IIntentReceiver resultTo, boolean ordered) {
- return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, 42, false, null,
- null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
+ return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, TEST_UID, false,
+ null, null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo,
Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM,
BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN);
}
@@ -259,12 +232,12 @@
private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
BroadcastRecord record, int recordIndex, long enqueueTime) {
- queue.enqueueOrReplaceBroadcast(record, recordIndex, (r, i) -> {
- throw new UnsupportedOperationException();
- });
record.enqueueTime = enqueueTime;
record.enqueueRealTime = enqueueTime;
record.enqueueClockTime = enqueueTime;
+ queue.enqueueOrReplaceBroadcast(record, recordIndex, (r, i) -> {
+ throw new UnsupportedOperationException();
+ });
}
@Test
@@ -419,6 +392,7 @@
assertFalse(queue.isRunnable());
assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
queue.getRunnableAtReason());
+ assertTrue(queue.shouldBeDeferred());
assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
}
@@ -445,6 +419,7 @@
assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
assertTrue(queue.isRunnable());
assertEquals(BroadcastProcessQueue.REASON_CACHED, queue.getRunnableAtReason());
+ assertTrue(queue.shouldBeDeferred());
assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
}
@@ -526,11 +501,13 @@
queue.invalidateRunnableAt();
assertThat(queue.getRunnableAt()).isGreaterThan(airplaneRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
mConstants.MAX_PENDING_BROADCASTS = 1;
queue.invalidateRunnableAt();
assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_MAX_PENDING, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -549,10 +526,12 @@
queue.setProcessAndUidState(mProcess, true, false);
assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_FOREGROUND, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -570,6 +549,7 @@
assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_TOP_PROCESS, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
doReturn(ActivityManager.PROCESS_STATE_SERVICE).when(mProcess).getSetProcState();
queue.setProcessAndUidState(mProcess, false, false);
@@ -580,6 +560,7 @@
List.of(makeMockRegisteredReceiver())), 0);
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -594,16 +575,19 @@
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
doReturn(true).when(mProcess).isPersistent();
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_PERSISTENT, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
doReturn(false).when(mProcess).isPersistent();
queue.setProcessAndUidState(mProcess, false, false);
assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
@Test
@@ -618,6 +602,31 @@
assertThat(queue.getRunnableAt()).isEqualTo(timeTickRecord.enqueueTime);
assertEquals(BroadcastProcessQueue.REASON_CORE_UID, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
+ }
+
+ @Test
+ public void testRunnableAt_freezableCoreUid() {
+ final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
+ "com.android.bluetooth", Process.BLUETOOTH_UID);
+
+ // Mark the process as freezable
+ queue.setProcessAndUidState(mProcess, false, true);
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastOptions options = BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, options,
+ List.of(makeMockRegisteredReceiver()), false);
+ enqueueOrReplaceBroadcast(queue, timeTickRecord, 0);
+
+ assertEquals(Long.MAX_VALUE, queue.getRunnableAt());
+ assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+ queue.getRunnableAtReason());
+ assertTrue(queue.shouldBeDeferred());
+
+ queue.setProcessAndUidState(mProcess, false, false);
+ assertThat(queue.getRunnableAt()).isEqualTo(timeTickRecord.enqueueTime);
+ assertEquals(BroadcastProcessQueue.REASON_CORE_UID, queue.getRunnableAtReason());
+ assertFalse(queue.shouldBeDeferred());
}
/**
@@ -1553,6 +1562,216 @@
verifyPendingRecords(redQueue, List.of(userPresent, timeTick));
}
+ @Test
+ public void testDeliveryDeferredForCached() throws Exception {
+ final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+ final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick,
+ List.of(makeRegisteredReceiver(greenProcess, 0)));
+
+ final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ final BroadcastOptions optionsBatteryChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged,
+ optionsBatteryChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 10),
+ makeRegisteredReceiver(redProcess, 0)),
+ false /* ordered */);
+
+ mImpl.enqueueBroadcastLocked(timeTickRecord);
+ mImpl.enqueueBroadcastLocked(batteryChangedRecord);
+
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ true /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ // Once the broadcasts to green process are deferred, broadcasts to red process
+ // shouldn't be blocked anymore.
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to green process should be deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 0)));
+ mImpl.enqueueBroadcastLocked(packageChangedRecord);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to the green process, including the newly enqueued one, should be
+ // deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ false /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ }
+
+ @Test
+ public void testDeliveryDeferredForCached_withInfiniteDeferred() throws Exception {
+ final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+ final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED));
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastOptions optionsTimeTick = BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, optionsTimeTick,
+ List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */);
+
+ final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ final BroadcastOptions optionsBatteryChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged,
+ optionsBatteryChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 10),
+ makeRegisteredReceiver(redProcess, 0)),
+ false /* ordered */);
+
+ mImpl.enqueueBroadcastLocked(timeTickRecord);
+ mImpl.enqueueBroadcastLocked(batteryChangedRecord);
+
+ final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED));
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ true /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+ greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ // Once the broadcasts to green process are deferred, broadcasts to red process
+ // shouldn't be blocked anymore.
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to green process should be deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ final BroadcastOptions optionsPackageChanged =
+ BroadcastOptions.makeWithDeferUntilActive(true);
+ final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged,
+ optionsPackageChanged,
+ List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */);
+ mImpl.enqueueBroadcastLocked(packageChangedRecord);
+
+ assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+ greenQueue.getRunnableAtReason());
+ assertTrue(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ // All broadcasts to the green process, including the newly enqueued one, should be
+ // deferred.
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+
+ // Simulate process state change
+ greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */,
+ false /* processFreezable */);
+ greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply,
+ mImpl.mBroadcastConsumerDeferClear);
+
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason());
+ assertFalse(greenQueue.shouldBeDeferred());
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason());
+ assertFalse(redQueue.shouldBeDeferred());
+
+ greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> {
+ assertEquals("Unexpected state for " + r,
+ BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i));
+ }, false /* andRemove */);
+ }
+
+ // TODO: Reuse BroadcastQueueTest.makeActiveProcessRecord()
+ private ProcessRecord makeProcessRecord(ApplicationInfo info) {
+ final ProcessRecord r = spy(new ProcessRecord(mAms, info, info.processName, info.uid));
+ r.setPid(mNextPid.incrementAndGet());
+ return r;
+ }
+
+ BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
+ final IIntentReceiver receiver = mock(IIntentReceiver.class);
+ final ReceiverList receiverList = new ReceiverList(mAms, app, app.getPid(), app.info.uid,
+ UserHandle.getUserId(app.info.uid), receiver);
+ return makeRegisteredReceiver(receiverList, priority);
+ }
+
private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
final Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
packageChangedIntent.putExtra(Intent.EXTRA_UID, uid);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 73eb237..e914726 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -62,58 +62,36 @@
import android.app.IApplicationThread;
import android.app.UidObserver;
import android.app.usage.UsageEvents.Event;
-import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
-import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeadObjectException;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.PowerExemptionManager;
import android.os.SystemClock;
-import android.os.TestLooperManager;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
-import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.AlarmManagerInternal;
-import com.android.server.DropBoxManagerInternal;
-import com.android.server.LocalServices;
-import com.android.server.am.ActivityManagerService.Injector;
-import com.android.server.appop.AppOpsService;
-import com.android.server.wm.ActivityTaskManagerService;
-
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
-import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.Writer;
@@ -125,7 +103,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.UnaryOperator;
@@ -135,13 +113,9 @@
@MediumTest
@RunWith(Parameterized.class)
@SuppressWarnings("GuardedBy")
-public class BroadcastQueueTest {
+public class BroadcastQueueTest extends BaseBroadcastQueueTest {
private static final String TAG = "BroadcastQueueTest";
- @Rule
- public final ApplicationExitInfoTest.ServiceThreadRule
- mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
-
private final Impl mImpl;
private enum Impl {
@@ -149,30 +123,8 @@
MODERN,
}
- private Context mContext;
- private HandlerThread mHandlerThread;
- private TestLooperManager mLooper;
- private AtomicInteger mNextPid;
-
- @Mock
- private AppOpsService mAppOpsService;
- @Mock
- private ProcessList mProcessList;
- @Mock
- private DropBoxManagerInternal mDropBoxManagerInt;
- @Mock
- private PackageManagerInternal mPackageManagerInt;
- @Mock
- private UsageStatsManagerInternal mUsageStatsManagerInt;
- @Mock
- private AlarmManagerInternal mAlarmManagerInt;
-
- private ActivityManagerService mAms;
private BroadcastQueue mQueue;
- BroadcastConstants mConstants;
- private BroadcastSkipPolicy mSkipPolicy;
private UidObserver mUidObserver;
- private UidObserver mUidCachedStateObserver;
/**
* Desired behavior of the next
@@ -182,11 +134,6 @@
ProcessStartBehavior.SUCCESS);
/**
- * Map from PID to registered registered runtime receivers.
- */
- private SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
-
- /**
* Collection of all active processes during current test run.
*/
private List<ProcessRecord> mActiveProcesses = new ArrayList<>();
@@ -207,41 +154,8 @@
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
+ super.setUp();
- mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
-
- // Pause all event processing until a test chooses to resume
- mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
- .acquireLooperManager(mHandlerThread.getLooper()));
-
- mNextPid = new AtomicInteger(100);
-
- LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
- LocalServices.addService(DropBoxManagerInternal.class, mDropBoxManagerInt);
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
- LocalServices.removeServiceForTest(AlarmManagerInternal.class);
- LocalServices.addService(AlarmManagerInternal.class, mAlarmManagerInt);
- doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
- doNothing().when(mPackageManagerInt).setPackageStoppedState(any(), anyBoolean(), anyInt());
- doAnswer((invocation) -> {
- return getUidForPackage(invocation.getArgument(0));
- }).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
-
- final ActivityManagerService realAms = new ActivityManagerService(
- new TestInjector(mContext), mServiceThreadRule.getThread());
- realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
- realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
- realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
- realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
- realAms.mPackageManagerInt = mPackageManagerInt;
- realAms.mUsageStatsService = mUsageStatsManagerInt;
- realAms.mProcessesReady = true;
- mAms = spy(realAms);
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting startProcessLocked() for "
+ Arrays.toString(invocation.getArguments()));
@@ -320,21 +234,11 @@
return null;
}).when(mAms).registerUidObserver(any(), anyInt(),
eq(ActivityManager.PROCESS_STATE_TOP), any());
- doAnswer((invocation) -> {
- mUidCachedStateObserver = invocation.getArgument(0);
- return null;
- }).when(mAms).registerUidObserver(any(), anyInt(),
- eq(ActivityManager.PROCESS_STATE_LAST_ACTIVITY), any());
- mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
- mConstants.TIMEOUT = 100;
+ mConstants.TIMEOUT = 200;
mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500;
- mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
- doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
- doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
-
final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
public void addBroadcastToHistoryLocked(BroadcastRecord original) {
// Ignored
@@ -357,7 +261,7 @@
@After
public void tearDown() throws Exception {
- mHandlerThread.quit();
+ super.tearDown();
// Verify that all processes have finished handling broadcasts
for (ProcessRecord app : mActiveProcesses) {
@@ -368,26 +272,9 @@
}
}
- private class TestInjector extends Injector {
- TestInjector(Context context) {
- super(context);
- }
-
- @Override
- public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
- Handler handler) {
- return mAppOpsService;
- }
-
- @Override
- public Handler getUiHandler(ActivityManagerService service) {
- return mHandlerThread.getThreadHandler();
- }
-
- @Override
- public ProcessList getProcessList(ActivityManagerService service) {
- return mProcessList;
- }
+ @Override
+ public String getTag() {
+ return TAG;
}
private enum ProcessStartBehavior {
@@ -533,62 +420,6 @@
return Pair.create(app.getPid(), intent.getAction());
}
- static ApplicationInfo makeApplicationInfo(String packageName) {
- return makeApplicationInfo(packageName, packageName, UserHandle.USER_SYSTEM);
- }
-
- static ApplicationInfo makeApplicationInfo(String packageName, String processName, int userId) {
- final ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = packageName;
- ai.processName = processName;
- ai.uid = getUidForPackage(packageName, userId);
- return ai;
- }
-
- static ResolveInfo withPriority(ResolveInfo info, int priority) {
- info.priority = priority;
- return info;
- }
-
- static BroadcastFilter withPriority(BroadcastFilter filter, int priority) {
- filter.setPriority(priority);
- return filter;
- }
-
- static ResolveInfo makeManifestReceiver(String packageName, String name) {
- return makeManifestReceiver(packageName, name, UserHandle.USER_SYSTEM);
- }
-
- static ResolveInfo makeManifestReceiver(String packageName, String name, int userId) {
- return makeManifestReceiver(packageName, packageName, name, userId);
- }
-
- static ResolveInfo makeManifestReceiver(String packageName, String processName, String name,
- int userId) {
- final ResolveInfo ri = new ResolveInfo();
- ri.activityInfo = new ActivityInfo();
- ri.activityInfo.packageName = packageName;
- ri.activityInfo.processName = processName;
- ri.activityInfo.name = name;
- ri.activityInfo.applicationInfo = makeApplicationInfo(packageName, processName, userId);
- return ri;
- }
-
- private BroadcastFilter makeRegisteredReceiver(ProcessRecord app) {
- return makeRegisteredReceiver(app, 0);
- }
-
- private BroadcastFilter makeRegisteredReceiver(ProcessRecord app, int priority) {
- final ReceiverList receiverList = mRegisteredReceivers.get(app.getPid());
- final IntentFilter filter = new IntentFilter();
- filter.setPriority(priority);
- final BroadcastFilter res = new BroadcastFilter(filter, receiverList,
- receiverList.app.info.packageName, null, null, null, receiverList.uid,
- receiverList.userId, false, false, true);
- receiverList.add(res);
- return res;
- }
-
private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
List<Object> receivers) {
return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(),
@@ -707,6 +538,9 @@
private void waitForIdle() throws Exception {
mLooper.release();
mQueue.waitForIdle(LOG_WRITER_INFO);
+ final CountDownLatch latch = new CountDownLatch(1);
+ mHandlerThread.getThreadHandler().post(latch::countDown);
+ latch.await();
mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
.acquireLooperManager(mHandlerThread.getLooper()));
}
@@ -772,41 +606,6 @@
eq(userId), anyInt(), anyInt(), any());
}
- static final int USER_GUEST = 11;
-
- static final String PACKAGE_ANDROID = "android";
- static final String PACKAGE_PHONE = "com.android.phone";
- static final String PACKAGE_RED = "com.example.red";
- static final String PACKAGE_GREEN = "com.example.green";
- static final String PACKAGE_BLUE = "com.example.blue";
- static final String PACKAGE_YELLOW = "com.example.yellow";
- static final String PACKAGE_ORANGE = "com.example.orange";
-
- static final String PROCESS_SYSTEM = "system";
-
- static final String CLASS_RED = "com.example.red.Red";
- static final String CLASS_GREEN = "com.example.green.Green";
- static final String CLASS_BLUE = "com.example.blue.Blue";
- static final String CLASS_YELLOW = "com.example.yellow.Yellow";
- static final String CLASS_ORANGE = "com.example.orange.Orange";
-
- static int getUidForPackage(@NonNull String packageName) {
- switch (packageName) {
- case PACKAGE_ANDROID: return android.os.Process.SYSTEM_UID;
- case PACKAGE_PHONE: return android.os.Process.PHONE_UID;
- case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
- case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
- case PACKAGE_BLUE: return android.os.Process.FIRST_APPLICATION_UID + 3;
- case PACKAGE_YELLOW: return android.os.Process.FIRST_APPLICATION_UID + 4;
- case PACKAGE_ORANGE: return android.os.Process.FIRST_APPLICATION_UID + 5;
- default: throw new IllegalArgumentException();
- }
- }
-
- static int getUidForPackage(@NonNull String packageName, int userId) {
- return UserHandle.getUid(userId, getUidForPackage(packageName));
- }
-
/**
* Baseline verification of common debugging infrastructure, mostly to make
* sure it doesn't crash.
@@ -2342,6 +2141,7 @@
mUidObserver.onUidStateChanged(receiverGreenApp.info.uid,
ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE);
+ waitForIdle();
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
@@ -2375,6 +2175,7 @@
mUidObserver.onUidStateChanged(receiverGreenApp.info.uid,
ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE);
+ waitForIdle();
final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index dc1c6d5..74d664f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -75,7 +75,7 @@
private static final String TEST_PACKAGE = "package1";
private static final String[] TEST_PACKAGES = new String[] { TEST_PACKAGE };
private static final String TEST_TRANSPORT = "transport";
- private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 1;
+ private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 100;
@Mock Context mContext;
@Mock IBackupManagerMonitor mBackupManagerMonitor;
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 7cd8819..d687915 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -320,11 +320,20 @@
@Test
public void testGetBootUser_Headless_ThrowsIfOnlySystemUserExists() throws Exception {
setSystemUserHeadless(true);
+ removeNonSystemUsers();
assertThrows(UserManager.CheckedUserOperationException.class,
() -> mUmi.getBootUser(/* waitUntilSet= */ false));
}
+ private void removeNonSystemUsers() {
+ for (UserInfo user : mUms.getUsers(true)) {
+ if (!user.getUserHandle().isSystem()) {
+ mUms.removeUserInfo(user.id);
+ }
+ }
+ }
+
private void mockCurrentUser(@UserIdInt int userId) {
mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/OWNERS b/services/tests/mockingservicestests/src/com/android/server/power/OWNERS
index fb62520..37396f3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/power/OWNERS
@@ -1,3 +1,3 @@
include /services/core/java/com/android/server/power/OWNERS
-per-file ThermalManagerServiceMockingTest.java=wvw@google.com,xwxw@google.com
+per-file ThermalManagerServiceMockingTest.java=file:/THERMAL_OWNERS
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index f770f8c..e656cf3 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -65,6 +65,7 @@
"ActivityContext",
"coretests-aidl",
"securebox",
+ "flag-junit",
],
libs: [
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index f88afe7..a78f2dc 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -41,6 +41,8 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -53,6 +55,7 @@
import android.os.Binder;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -84,6 +87,8 @@
@Rule
public MockitoRule mockitorule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private Context mContext;
@Mock
@@ -418,6 +423,37 @@
eq(callback));
}
+ @Test(expected = UnsupportedOperationException.class)
+ public void testGetLastAuthenticationTime_flaggedOff_throwsUnsupportedOperationException()
+ throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+ setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ mAuthService.mImpl.getLastAuthenticationTime(0,
+ BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void testGetLastAuthenticationTime_flaggedOn_callsBiometricService()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+ setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final int userId = 0;
+ final int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG;
+
+ mAuthService.mImpl.getLastAuthenticationTime(userId, authenticators);
+
+ waitForIdle();
+ verify(mBiometricService).getLastAuthenticationTime(eq(userId), eq(authenticators));
+ }
+
private static void setInternalAndTestBiometricPermissions(
Context context, boolean hasPermission) {
for (String p : List.of(TEST_BIOMETRIC, MANAGE_BIOMETRIC, USE_BIOMETRIC_INTERNAL)) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 6f4791a..14a567a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -18,7 +18,6 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators;
-import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTHENTICATED_PENDING_SYSUI;
@@ -61,6 +60,7 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -70,12 +70,17 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.keymaster.HardwareAuthenticatorType;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.security.GateKeeper;
import android.security.KeyStore;
+import android.security.authorization.IKeystoreAuthorization;
+import android.service.gatekeeper.IGateKeeperService;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowManager;
@@ -91,6 +96,7 @@
import com.android.server.biometrics.sensors.LockoutTracker;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor;
@@ -104,6 +110,9 @@
@SmallTest
public class BiometricServiceTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final String TEST_PACKAGE_NAME = "test_package";
private static final long TEST_REQUEST_ID = 44;
@@ -155,10 +164,16 @@
@Mock
private BiometricCameraManager mBiometricCameraManager;
+ @Mock
+ private IKeystoreAuthorization mKeystoreAuthService;
+
+ @Mock
+ private IGateKeeperService mGateKeeperService;
+
BiometricContextProvider mBiometricContextProvider;
@Before
- public void setUp() {
+ public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
resetReceivers();
@@ -196,6 +211,9 @@
mStatusBarService, null /* handler */,
mAuthSessionCoordinator);
when(mInjector.getBiometricContext(any())).thenReturn(mBiometricContextProvider);
+ when(mInjector.getKeystoreAuthorizationService()).thenReturn(mKeystoreAuthService);
+ when(mInjector.getGateKeeperService()).thenReturn(mGateKeeperService);
+ when(mGateKeeperService.getSecureUserId(anyInt())).thenReturn(42L);
final String[] config = {
"0:2:15", // ID0:Fingerprint:Strong
@@ -1612,6 +1630,44 @@
verifyNoMoreInteractions(callback);
}
+ @Test(expected = UnsupportedOperationException.class)
+ public void testGetLastAuthenticationTime_flagOff_throwsUnsupportedOperationException()
+ throws RemoteException {
+ mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.mImpl.getLastAuthenticationTime(0, Authenticators.BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void testGetLastAuthenticationTime_flagOn_callsKeystoreAuthorization()
+ throws RemoteException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+
+ final int[] hardwareAuthenticators = new int[] {
+ HardwareAuthenticatorType.PASSWORD,
+ HardwareAuthenticatorType.FINGERPRINT
+ };
+
+ final int userId = 0;
+ final long secureUserId = mGateKeeperService.getSecureUserId(userId);
+
+ assertNotEquals(GateKeeper.INVALID_SECURE_USER_ID, secureUserId);
+
+ final long expectedResult = 31337L;
+
+ when(mKeystoreAuthService.getLastAuthTime(eq(secureUserId), eq(hardwareAuthenticators)))
+ .thenReturn(expectedResult);
+
+ mBiometricService = new BiometricService(mContext, mInjector);
+
+ final long result = mBiometricService.mImpl.getLastAuthenticationTime(userId,
+ Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL);
+
+ assertEquals(expectedResult, result);
+ verify(mKeystoreAuthService).getLastAuthTime(eq(secureUserId), eq(hardwareAuthenticators));
+ }
+
// Helper methods
private int invokeCanAuthenticate(BiometricService service, int authenticators)
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index d52b7ea..6a28009 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -27,6 +27,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertEquals;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -1648,4 +1650,24 @@
assertThat(mHdmiControlService.isSystemAudioActivated()).isTrue();
}
+
+ @Test
+ public void newDeviceConnectedIfOnlyOneGiveOsdNameSent() {
+ mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .isEmpty();
+ HdmiCecMessage reportPhysicalAddress =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ HdmiCecMessage giveOsdName = HdmiCecMessageBuilder.buildGiveOsdNameCommand(
+ ADDR_TV, ADDR_PLAYBACK_2);
+ mNativeWrapper.onCecMessage(reportPhysicalAddress);
+ mTestLooper.dispatchAll();
+
+ // Wait until HdmiCecNetwork or NewDeviceAction is in progress
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+
+ // TV should only send <Give Osd Name> once
+ assertEquals(1, Collections.frequency(mNativeWrapper.getResultMessages(), giveOsdName));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 1c33d0d..18961c0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -34,6 +34,7 @@
import com.android.internal.widget.LockscreenCredential;
import com.android.server.ServiceThread;
+import com.android.server.locksettings.SyntheticPasswordManager.SyntheticPassword;
import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.pm.UserManagerInternal;
@@ -203,6 +204,10 @@
}
@Override
+ void initKeystoreSuperKeys(int userId, SyntheticPassword sp, boolean allowExisting) {
+ }
+
+ @Override
protected boolean isCredentialSharableWithParent(int userId) {
UserInfo userInfo = mUserManager.getUserInfo(userId);
return userInfo.isCloneProfile() || userInfo.isManagedProfile();
diff --git a/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java b/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java
index 949f8e7..0e881ef 100644
--- a/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/LockdownVpnTrackerTest.java
@@ -221,7 +221,7 @@
callCallbacksForNetworkConnect(defaultCallback, mNetwork);
// Vpn is starting
- verify(mVpn).startLegacyVpnPrivileged(mProfile, mNetwork, TEST_CELL_LP);
+ verify(mVpn).startLegacyVpnPrivileged(mProfile);
verify(mNotificationManager).notify(any(), eq(SystemMessage.NOTE_VPN_STATUS),
argThat(notification -> isExpectedNotification(notification,
R.string.vpn_lockdown_connecting, R.drawable.vpn_disconnected)));
@@ -242,7 +242,7 @@
// LockdownVpnTracker#handleStateChangedLocked. This is a bug.
// TODO: consider fixing this.
verify(mVpn, never()).stopVpnRunnerPrivileged();
- verify(mVpn, never()).startLegacyVpnPrivileged(any(), any(), any());
+ verify(mVpn, never()).startLegacyVpnPrivileged(any());
verify(mNotificationManager, never()).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS));
}
@@ -302,7 +302,7 @@
// Vpn is restarted.
verify(mVpn).stopVpnRunnerPrivileged();
- verify(mVpn).startLegacyVpnPrivileged(mProfile, mNetwork2, wifiLp);
+ verify(mVpn).startLegacyVpnPrivileged(mProfile);
verify(mNotificationManager, never()).cancel(any(), eq(SystemMessage.NOTE_VPN_STATUS));
verify(mNotificationManager).notify(any(), eq(SystemMessage.NOTE_VPN_STATUS),
argThat(notification -> isExpectedNotification(notification,
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
index af144cf..13dc120 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
@@ -57,6 +57,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
+import com.android.modules.utils.build.SdkLevel;
import org.junit.After;
import org.junit.Before;
@@ -263,7 +264,11 @@
verify(mCm).addUidToMeteredNetworkDenyList(TEST_UID);
mNMService.setDataSaverModeEnabled(true);
- verify(mNetdService).bandwidthEnableDataSaver(true);
+ if (SdkLevel.isAtLeastV()) {
+ verify(mCm).setDataSaverEnabled(true);
+ } else {
+ verify(mNetdService).bandwidthEnableDataSaver(true);
+ }
mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, false);
assertTrue("Should be true since data saver is on and the uid is not allowlisted",
@@ -279,7 +284,11 @@
mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, false);
verify(mCm).removeUidFromMeteredNetworkAllowList(TEST_UID);
mNMService.setDataSaverModeEnabled(false);
- verify(mNetdService).bandwidthEnableDataSaver(false);
+ if (SdkLevel.isAtLeastV()) {
+ verify(mCm).setDataSaverEnabled(false);
+ } else {
+ verify(mNetdService).bandwidthEnableDataSaver(false);
+ }
assertFalse("Network should not be restricted when data saver is off",
mNMService.isNetworkRestricted(TEST_UID));
}
diff --git a/services/tests/servicestests/src/com/android/server/power/OWNERS b/services/tests/servicestests/src/com/android/server/power/OWNERS
index ef4c0bf..fe93ebb 100644
--- a/services/tests/servicestests/src/com/android/server/power/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/power/OWNERS
@@ -1,3 +1,3 @@
include /services/core/java/com/android/server/power/OWNERS
-per-file ThermalManagerServiceTest.java=wvw@google.com, xwxw@google.com
\ No newline at end of file
+per-file ThermalManagerServiceTest.java=file:/THERMAL_OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/utils/OWNERS b/services/tests/servicestests/src/com/android/server/utils/OWNERS
index 5e24828..f5b19a1 100644
--- a/services/tests/servicestests/src/com/android/server/utils/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/utils/OWNERS
@@ -1,2 +1,5 @@
per-file EventLoggerTest.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS
per-file EventLoggerTest.java = jmtrivi@google.com
+
+# Bug component : 158088 = per-file AnrTimer*.java
+per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 0ccb0d0..9e0d69b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1040,14 +1040,25 @@
// If the task has a non-stopped activity, the removal will wait for its onDestroy.
final Task task = tasks.get(0);
+ final ActivityRecord bottom = new ActivityBuilder(mAtm).setTask(task).build();
final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).build();
- top.lastVisibleTime = 123;
+ bottom.lastVisibleTime = top.lastVisibleTime = 123;
top.setState(ActivityRecord.State.RESUMED, "test");
mRecentTasks.removeTasksByPackageName(task.getBasePackageName(), TEST_USER_0_ID);
assertTrue(task.mKillProcessesOnDestroyed);
top.setState(ActivityRecord.State.DESTROYING, "test");
+ bottom.destroyed("test");
+ assertTrue("Wait for all destroyed", task.mKillProcessesOnDestroyed);
top.destroyed("test");
- assertFalse(task.mKillProcessesOnDestroyed);
+ assertFalse("Consume kill", task.mKillProcessesOnDestroyed);
+
+ // If the process is died, the state should be cleared.
+ final Task lastTask = tasks.get(0);
+ lastTask.intent.setComponent(top.mActivityComponent);
+ lastTask.addChild(top);
+ lastTask.mKillProcessesOnDestroyed = true;
+ top.handleAppDied();
+ assertFalse(lastTask.mKillProcessesOnDestroyed);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 3bc6450..b92cc64 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -870,7 +870,7 @@
new TestDisplayContent.Builder(mAtm, 1000, 1500)
.setSystemDecorations(true).build();
- // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false.
+ // Use invalid user id to let StorageManager.isCeStorageUnlocked() return false.
final int currentUser = mRootWindowContainer.mCurrentUser;
mRootWindowContainer.mCurrentUser = -1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 3ec6f42..973ab84 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -448,7 +448,6 @@
mDisplayContent.updateImeParent();
// Ime should on top of the popup IME layering target window.
- mDisplayContent.assignChildLayers(mTransaction);
assertWindowHigher(mImeWindow, popupImeTargetWin);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 216f45a..d722f2f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -279,6 +279,7 @@
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY
| Context.BIND_SCHEDULE_LIKE_TOP_APP
+ | Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
new UserHandle(mUser));
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index c152a41..25bd14f 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -2951,11 +2951,11 @@
for(String key : bundle.keySet()) {
if (key != null) {
- final Object value = bundle.get(key);
- final Object newValue = newBundle.get(key);
if (!newBundle.containsKey(key)) {
return false;
}
+ final Object value = bundle.get(key);
+ final Object newValue = newBundle.get(key);
if (value instanceof Bundle && newValue instanceof Bundle) {
if (!areBundlesEqual((Bundle) value, (Bundle) newValue)) {
return false;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a72f780..89ef523 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2352,6 +2352,11 @@
* <p>
* <b>Note</b>: {@link android.app.Notification.CallStyle} notifications should be posted after
* the call is placed in order for the notification to be non-dismissible.
+ * <p><b>Note</b>: Call Forwarding MMI codes can only be dialed by applications that are
+ * configured as the user defined default dialer or system dialer role. If a call containing a
+ * call forwarding MMI code is placed by an application that is not in one of these roles, the
+ * dialer will be launched with a UI showing the MMI code already populated so that the user can
+ * confirm the action before the call is placed.
* @param address The address to make the call to.
* @param extras Bundle of extras to use with the call.
*/
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 3158ad8..287aa65 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -7,10 +7,12 @@
tgunn@google.com
huiwang@google.com
jayachandranc@google.com
-chinmayd@google.com
amruthr@google.com
sasindran@google.com
# Requiring TL ownership for new carrier config keys.
per-file CarrierConfigManager.java=set noparent
per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com
+
+#Domain Selection is jointly owned, add additional owners for domain selection specific files
+per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index f61cce6..daaab33 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -156,6 +156,7 @@
method @Deprecated public int getInt(int);
method @Deprecated public long getLong(int);
method @Deprecated public android.net.Uri getNotificationUri();
+ method @Deprecated public java.util.List<android.net.Uri> getNotificationUris();
method @Deprecated public int getPosition();
method @Deprecated public short getShort(int);
method @Deprecated public String getString(int);
@@ -179,6 +180,7 @@
method @Deprecated public android.os.Bundle respond(android.os.Bundle);
method @Deprecated public void setExtras(android.os.Bundle);
method @Deprecated public void setNotificationUri(android.content.ContentResolver, android.net.Uri);
+ method @Deprecated public void setNotificationUris(android.content.ContentResolver, java.util.List<android.net.Uri>);
method @Deprecated public void unregisterContentObserver(android.database.ContentObserver);
method @Deprecated public void unregisterDataSetObserver(android.database.DataSetObserver);
}
diff --git a/tests/HwAccelerationTest/OWNERS b/tests/HwAccelerationTest/OWNERS
deleted file mode 100644
index c88a9f8..0000000
--- a/tests/HwAccelerationTest/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /libs/hwui/OWNERS
diff --git a/tests/NetworkSecurityConfigTest/OWNERS b/tests/NetworkSecurityConfigTest/OWNERS
index aa87958..90e1bed 100644
--- a/tests/NetworkSecurityConfigTest/OWNERS
+++ b/tests/NetworkSecurityConfigTest/OWNERS
@@ -1 +1,2 @@
include /services/core/java/com/android/server/net/OWNERS
+include /core/java/android/security/net/OWNERS
diff --git a/tests/SilkFX/OWNERS b/tests/SilkFX/OWNERS
deleted file mode 100644
index c88a9f8..0000000
--- a/tests/SilkFX/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /libs/hwui/OWNERS
diff --git a/tests/SmokeTestApps/Android.bp b/tests/SmokeTestApps/Android.bp
index 3505fe1..38ee8ac 100644
--- a/tests/SmokeTestApps/Android.bp
+++ b/tests/SmokeTestApps/Android.bp
@@ -11,4 +11,7 @@
name: "SmokeTestTriggerApps",
srcs: ["src/**/*.java"],
sdk_version: "current",
+ errorprone: {
+ enabled: false,
+ },
}
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
index 1dfd5c0..d0e5626 100644
--- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -93,7 +93,7 @@
}
@Test
- @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS)
+ @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
fun grantCannotActivelyUnlockDevice() {
// On automotive, trust agents can actively unlock the device.
assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
@@ -120,7 +120,7 @@
}
@Test
- @RequiresFlagsDisabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS)
+ @RequiresFlagsDisabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
fun grantCouldCauseWrongDeviceLockedStateDueToBug() {
// On automotive, trust agents can actively unlock the device.
assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
index 5a8f828..0121809 100644
--- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -36,7 +36,8 @@
class LockStateTrackingRule : TestRule {
private val context: Context = getApplicationContext()
private val windowManager = checkNotNull(WindowManagerGlobal.getWindowManagerService())
- private val keyguardManager = context.getSystemService(KeyguardManager::class.java) as KeyguardManager
+ private val keyguardManager =
+ context.getSystemService(KeyguardManager::class.java) as KeyguardManager
@Volatile lateinit var trustState: TrustState
private set
@@ -63,7 +64,7 @@
wait("not trusted") { trustState.trusted == false }
}
- // TODO(b/299298338) remove this when removing FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS
+ // TODO(b/299298338) remove this when removing FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2
fun assertUnlockedButNotReally() {
wait("device unlocked") { !keyguardManager.isDeviceLocked }
wait("not trusted") { trustState.trusted == false }
@@ -87,7 +88,7 @@
trustGrantedMessages: MutableList<String>
) {
Log.d(TAG, "Device became trusted=$enabled")
- trustState = trustState.copy(trusted=enabled)
+ trustState = trustState.copy(trusted = enabled)
}
}
diff --git a/tests/HwAccelerationTest/.classpath b/tests/graphics/HwAccelerationTest/.classpath
similarity index 100%
rename from tests/HwAccelerationTest/.classpath
rename to tests/graphics/HwAccelerationTest/.classpath
diff --git a/tests/HwAccelerationTest/.gitignore b/tests/graphics/HwAccelerationTest/.gitignore
similarity index 100%
rename from tests/HwAccelerationTest/.gitignore
rename to tests/graphics/HwAccelerationTest/.gitignore
diff --git a/tests/HwAccelerationTest/Android.bp b/tests/graphics/HwAccelerationTest/Android.bp
similarity index 100%
rename from tests/HwAccelerationTest/Android.bp
rename to tests/graphics/HwAccelerationTest/Android.bp
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/graphics/HwAccelerationTest/AndroidManifest.xml
similarity index 98%
rename from tests/HwAccelerationTest/AndroidManifest.xml
rename to tests/graphics/HwAccelerationTest/AndroidManifest.xml
index 80c7a21..db3a992 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/graphics/HwAccelerationTest/AndroidManifest.xml
@@ -789,6 +789,15 @@
</intent-filter>
</activity>
+ <activity android:name="BackdropBlurActivity"
+ android:label="RenderEffect/BackdropBlur"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="BlurActivity"
android:label="RenderEffect/Blur"
android:exported="true">
diff --git a/tests/HwAccelerationTest/default.properties b/tests/graphics/HwAccelerationTest/default.properties
similarity index 100%
rename from tests/HwAccelerationTest/default.properties
rename to tests/graphics/HwAccelerationTest/default.properties
diff --git a/tests/HwAccelerationTest/jni/Android.bp b/tests/graphics/HwAccelerationTest/jni/Android.bp
similarity index 100%
rename from tests/HwAccelerationTest/jni/Android.bp
rename to tests/graphics/HwAccelerationTest/jni/Android.bp
diff --git a/tests/HwAccelerationTest/jni/native-lib.cpp b/tests/graphics/HwAccelerationTest/jni/native-lib.cpp
similarity index 100%
rename from tests/HwAccelerationTest/jni/native-lib.cpp
rename to tests/graphics/HwAccelerationTest/jni/native-lib.cpp
diff --git a/tests/HwAccelerationTest/res/anim/accelerate_interpolator_2.xml b/tests/graphics/HwAccelerationTest/res/anim/accelerate_interpolator_2.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/anim/accelerate_interpolator_2.xml
rename to tests/graphics/HwAccelerationTest/res/anim/accelerate_interpolator_2.xml
diff --git a/tests/HwAccelerationTest/res/anim/fade_in.xml b/tests/graphics/HwAccelerationTest/res/anim/fade_in.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/anim/fade_in.xml
rename to tests/graphics/HwAccelerationTest/res/anim/fade_in.xml
diff --git a/tests/HwAccelerationTest/res/anim/fade_out.xml b/tests/graphics/HwAccelerationTest/res/anim/fade_out.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/anim/fade_out.xml
rename to tests/graphics/HwAccelerationTest/res/anim/fade_out.xml
diff --git a/tests/HwAccelerationTest/res/anim/slide_off_left.xml b/tests/graphics/HwAccelerationTest/res/anim/slide_off_left.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/anim/slide_off_left.xml
rename to tests/graphics/HwAccelerationTest/res/anim/slide_off_left.xml
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/appwidget_background.xml b/tests/graphics/HwAccelerationTest/res/drawable-hdpi/appwidget_background.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-hdpi/appwidget_background.xml
rename to tests/graphics/HwAccelerationTest/res/drawable-hdpi/appwidget_background.xml
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/icon.png b/tests/graphics/HwAccelerationTest/res/drawable-hdpi/icon.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-hdpi/icon.png
rename to tests/graphics/HwAccelerationTest/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg b/tests/graphics/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
rename to tests/graphics/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png b/tests/graphics/HwAccelerationTest/res/drawable-hdpi/sunset2.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-hdpi/sunset2.png
rename to tests/graphics/HwAccelerationTest/res/drawable-hdpi/sunset2.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png b/tests/graphics/HwAccelerationTest/res/drawable-hdpi/sunset3.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-hdpi/sunset3.png
rename to tests/graphics/HwAccelerationTest/res/drawable-hdpi/sunset3.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/widget_header.png b/tests/graphics/HwAccelerationTest/res/drawable-hdpi/widget_header.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-hdpi/widget_header.png
rename to tests/graphics/HwAccelerationTest/res/drawable-hdpi/widget_header.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-mdpi/expander_ic_maximized.9.png b/tests/graphics/HwAccelerationTest/res/drawable-mdpi/expander_ic_maximized.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-mdpi/expander_ic_maximized.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-mdpi/expander_ic_maximized.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-mdpi/expander_ic_minimized.9.png b/tests/graphics/HwAccelerationTest/res/drawable-mdpi/expander_ic_minimized.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-mdpi/expander_ic_minimized.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-mdpi/expander_ic_minimized.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/appwidget_bg.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/appwidget_bg.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/appwidget_bg.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/appwidget_bg.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/appwidget_bg_focus.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/appwidget_bg_focus.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/appwidget_bg_focus.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/appwidget_bg_focus.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/appwidget_bg_press.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/appwidget_bg_press.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/appwidget_bg_press.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/appwidget_bg_press.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/green_gradient.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/green_gradient.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/green_gradient.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/green_gradient.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/large_photo.jpg b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/large_photo.jpg
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/large_photo.jpg
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/large_photo.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/patch.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/patch.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/patch.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/patch.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/patch2.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/patch2.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/patch2.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/patch2.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_bg_holo_dark.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/progress_vertical_bg_holo_dark.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_bg_holo_dark.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/progress_vertical_bg_holo_dark.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_primary_holo_dark.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/progress_vertical_primary_holo_dark.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_primary_holo_dark.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/progress_vertical_primary_holo_dark.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_secondary_holo_dark.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/progress_vertical_secondary_holo_dark.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/progress_vertical_secondary_holo_dark.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/progress_vertical_secondary_holo_dark.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scratches.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/scratches.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/scratches.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/scratches.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_primary_holo.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_primary_holo.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_primary_holo.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_primary_holo.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_secondary_holo.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_secondary_holo.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_secondary_holo.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_secondary_holo.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_dark.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_dark.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_dark.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_dark.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_light.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_light.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_light.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/scrubber_vertical_track_holo_light.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/spot_mask.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/spot_mask.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/spot_mask.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/spot_mask.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/very_large_photo.jpg b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/very_large_photo.jpg
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/very_large_photo.jpg
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/very_large_photo.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/widget_title_bg.9.png b/tests/graphics/HwAccelerationTest/res/drawable-nodpi/widget_title_bg.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable-nodpi/widget_title_bg.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable-nodpi/widget_title_bg.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/appwidget_background.xml b/tests/graphics/HwAccelerationTest/res/drawable/appwidget_background.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/appwidget_background.xml
rename to tests/graphics/HwAccelerationTest/res/drawable/appwidget_background.xml
diff --git a/tests/HwAccelerationTest/res/drawable/appwidget_bg.9.png b/tests/graphics/HwAccelerationTest/res/drawable/appwidget_bg.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/appwidget_bg.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable/appwidget_bg.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/appwidget_bg_focus.9.png b/tests/graphics/HwAccelerationTest/res/drawable/appwidget_bg_focus.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/appwidget_bg_focus.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable/appwidget_bg_focus.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/appwidget_bg_press.9.png b/tests/graphics/HwAccelerationTest/res/drawable/appwidget_bg_press.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/appwidget_bg_press.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable/appwidget_bg_press.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png b/tests/graphics/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable/btn_toggle_off.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png b/tests/graphics/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable/btn_toggle_on.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/default_wallpaper.png b/tests/graphics/HwAccelerationTest/res/drawable/default_wallpaper.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/default_wallpaper.png
rename to tests/graphics/HwAccelerationTest/res/drawable/default_wallpaper.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/gradient.xml b/tests/graphics/HwAccelerationTest/res/drawable/gradient.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/gradient.xml
rename to tests/graphics/HwAccelerationTest/res/drawable/gradient.xml
diff --git a/tests/HwAccelerationTest/res/drawable/green_gradient.9.png b/tests/graphics/HwAccelerationTest/res/drawable/green_gradient.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/green_gradient.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable/green_gradient.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/icon.png b/tests/graphics/HwAccelerationTest/res/drawable/icon.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/icon.png
rename to tests/graphics/HwAccelerationTest/res/drawable/icon.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/progress_vertical_holo_dark.xml b/tests/graphics/HwAccelerationTest/res/drawable/progress_vertical_holo_dark.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/progress_vertical_holo_dark.xml
rename to tests/graphics/HwAccelerationTest/res/drawable/progress_vertical_holo_dark.xml
diff --git a/tests/HwAccelerationTest/res/drawable/robot.png b/tests/graphics/HwAccelerationTest/res/drawable/robot.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/robot.png
rename to tests/graphics/HwAccelerationTest/res/drawable/robot.png
Binary files differ
diff --git a/tests/graphics/HwAccelerationTest/res/drawable/robot_repeated.xml b/tests/graphics/HwAccelerationTest/res/drawable/robot_repeated.xml
new file mode 100644
index 0000000..bbb15b7
--- /dev/null
+++ b/tests/graphics/HwAccelerationTest/res/drawable/robot_repeated.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/robot"
+ android:tileMode="repeat" android:gravity="fill" />
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/res/drawable/round_rect_background.xml b/tests/graphics/HwAccelerationTest/res/drawable/round_rect_background.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/round_rect_background.xml
rename to tests/graphics/HwAccelerationTest/res/drawable/round_rect_background.xml
diff --git a/tests/HwAccelerationTest/res/drawable/scrubber_progress_vertical_holo_dark.xml b/tests/graphics/HwAccelerationTest/res/drawable/scrubber_progress_vertical_holo_dark.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/scrubber_progress_vertical_holo_dark.xml
rename to tests/graphics/HwAccelerationTest/res/drawable/scrubber_progress_vertical_holo_dark.xml
diff --git a/tests/HwAccelerationTest/res/drawable/sunset1.jpg b/tests/graphics/HwAccelerationTest/res/drawable/sunset1.jpg
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/sunset1.jpg
rename to tests/graphics/HwAccelerationTest/res/drawable/sunset1.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset2.png b/tests/graphics/HwAccelerationTest/res/drawable/sunset2.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/sunset2.png
rename to tests/graphics/HwAccelerationTest/res/drawable/sunset2.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset3.png b/tests/graphics/HwAccelerationTest/res/drawable/sunset3.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/sunset3.png
rename to tests/graphics/HwAccelerationTest/res/drawable/sunset3.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/widget_header.png b/tests/graphics/HwAccelerationTest/res/drawable/widget_header.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/widget_header.png
rename to tests/graphics/HwAccelerationTest/res/drawable/widget_header.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/widget_title_bg.9.png b/tests/graphics/HwAccelerationTest/res/drawable/widget_title_bg.9.png
similarity index 100%
rename from tests/HwAccelerationTest/res/drawable/widget_title_bg.9.png
rename to tests/graphics/HwAccelerationTest/res/drawable/widget_title_bg.9.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/layout/_advanced_blend.xml b/tests/graphics/HwAccelerationTest/res/layout/_advanced_blend.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/_advanced_blend.xml
rename to tests/graphics/HwAccelerationTest/res/layout/_advanced_blend.xml
diff --git a/tests/HwAccelerationTest/res/layout/_advanced_gradient.xml b/tests/graphics/HwAccelerationTest/res/layout/_advanced_gradient.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/_advanced_gradient.xml
rename to tests/graphics/HwAccelerationTest/res/layout/_advanced_gradient.xml
diff --git a/tests/HwAccelerationTest/res/layout/_layers.xml b/tests/graphics/HwAccelerationTest/res/layout/_layers.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/_layers.xml
rename to tests/graphics/HwAccelerationTest/res/layout/_layers.xml
diff --git a/tests/HwAccelerationTest/res/layout/_lines.xml b/tests/graphics/HwAccelerationTest/res/layout/_lines.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/_lines.xml
rename to tests/graphics/HwAccelerationTest/res/layout/_lines.xml
diff --git a/tests/HwAccelerationTest/res/layout/_newlayers.xml b/tests/graphics/HwAccelerationTest/res/layout/_newlayers.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/_newlayers.xml
rename to tests/graphics/HwAccelerationTest/res/layout/_newlayers.xml
diff --git a/tests/HwAccelerationTest/res/layout/_paths.xml b/tests/graphics/HwAccelerationTest/res/layout/_paths.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/_paths.xml
rename to tests/graphics/HwAccelerationTest/res/layout/_paths.xml
diff --git a/tests/HwAccelerationTest/res/layout/_shaders.xml b/tests/graphics/HwAccelerationTest/res/layout/_shaders.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/_shaders.xml
rename to tests/graphics/HwAccelerationTest/res/layout/_shaders.xml
diff --git a/tests/HwAccelerationTest/res/layout/colored_shadows_activity.xml b/tests/graphics/HwAccelerationTest/res/layout/colored_shadows_activity.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/colored_shadows_activity.xml
rename to tests/graphics/HwAccelerationTest/res/layout/colored_shadows_activity.xml
diff --git a/tests/HwAccelerationTest/res/layout/colored_shadows_row.xml b/tests/graphics/HwAccelerationTest/res/layout/colored_shadows_row.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/colored_shadows_row.xml
rename to tests/graphics/HwAccelerationTest/res/layout/colored_shadows_row.xml
diff --git a/tests/HwAccelerationTest/res/layout/date_picker.xml b/tests/graphics/HwAccelerationTest/res/layout/date_picker.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/date_picker.xml
rename to tests/graphics/HwAccelerationTest/res/layout/date_picker.xml
diff --git a/tests/HwAccelerationTest/res/layout/flipper_item.xml b/tests/graphics/HwAccelerationTest/res/layout/flipper_item.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/flipper_item.xml
rename to tests/graphics/HwAccelerationTest/res/layout/flipper_item.xml
diff --git a/tests/HwAccelerationTest/res/layout/form.xml b/tests/graphics/HwAccelerationTest/res/layout/form.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/form.xml
rename to tests/graphics/HwAccelerationTest/res/layout/form.xml
diff --git a/tests/HwAccelerationTest/res/layout/image_filter_activity.xml b/tests/graphics/HwAccelerationTest/res/layout/image_filter_activity.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/image_filter_activity.xml
rename to tests/graphics/HwAccelerationTest/res/layout/image_filter_activity.xml
diff --git a/tests/HwAccelerationTest/res/layout/labels.xml b/tests/graphics/HwAccelerationTest/res/layout/labels.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/labels.xml
rename to tests/graphics/HwAccelerationTest/res/layout/labels.xml
diff --git a/tests/HwAccelerationTest/res/layout/list_activity.xml b/tests/graphics/HwAccelerationTest/res/layout/list_activity.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/list_activity.xml
rename to tests/graphics/HwAccelerationTest/res/layout/list_activity.xml
diff --git a/tests/HwAccelerationTest/res/layout/pen_stylus.xml b/tests/graphics/HwAccelerationTest/res/layout/pen_stylus.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/pen_stylus.xml
rename to tests/graphics/HwAccelerationTest/res/layout/pen_stylus.xml
diff --git a/tests/HwAccelerationTest/res/layout/projection.xml b/tests/graphics/HwAccelerationTest/res/layout/projection.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/projection.xml
rename to tests/graphics/HwAccelerationTest/res/layout/projection.xml
diff --git a/tests/HwAccelerationTest/res/layout/projection_clipping.xml b/tests/graphics/HwAccelerationTest/res/layout/projection_clipping.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/projection_clipping.xml
rename to tests/graphics/HwAccelerationTest/res/layout/projection_clipping.xml
diff --git a/tests/HwAccelerationTest/res/layout/scrolling_stretch_surfaceview.xml b/tests/graphics/HwAccelerationTest/res/layout/scrolling_stretch_surfaceview.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/scrolling_stretch_surfaceview.xml
rename to tests/graphics/HwAccelerationTest/res/layout/scrolling_stretch_surfaceview.xml
diff --git a/tests/HwAccelerationTest/res/layout/stack.xml b/tests/graphics/HwAccelerationTest/res/layout/stack.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/stack.xml
rename to tests/graphics/HwAccelerationTest/res/layout/stack.xml
diff --git a/tests/HwAccelerationTest/res/layout/stack_item.xml b/tests/graphics/HwAccelerationTest/res/layout/stack_item.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/stack_item.xml
rename to tests/graphics/HwAccelerationTest/res/layout/stack_item.xml
diff --git a/tests/HwAccelerationTest/res/layout/stretch_layout.xml b/tests/graphics/HwAccelerationTest/res/layout/stretch_layout.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/stretch_layout.xml
rename to tests/graphics/HwAccelerationTest/res/layout/stretch_layout.xml
diff --git a/tests/HwAccelerationTest/res/layout/text_fade.xml b/tests/graphics/HwAccelerationTest/res/layout/text_fade.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/text_fade.xml
rename to tests/graphics/HwAccelerationTest/res/layout/text_fade.xml
diff --git a/tests/HwAccelerationTest/res/layout/text_large.xml b/tests/graphics/HwAccelerationTest/res/layout/text_large.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/text_large.xml
rename to tests/graphics/HwAccelerationTest/res/layout/text_large.xml
diff --git a/tests/HwAccelerationTest/res/layout/text_medium.xml b/tests/graphics/HwAccelerationTest/res/layout/text_medium.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/text_medium.xml
rename to tests/graphics/HwAccelerationTest/res/layout/text_medium.xml
diff --git a/tests/HwAccelerationTest/res/layout/text_small.xml b/tests/graphics/HwAccelerationTest/res/layout/text_small.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/text_small.xml
rename to tests/graphics/HwAccelerationTest/res/layout/text_small.xml
diff --git a/tests/HwAccelerationTest/res/layout/transforms_and_animations.xml b/tests/graphics/HwAccelerationTest/res/layout/transforms_and_animations.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/transforms_and_animations.xml
rename to tests/graphics/HwAccelerationTest/res/layout/transforms_and_animations.xml
diff --git a/tests/HwAccelerationTest/res/layout/view_layer_invalidation.xml b/tests/graphics/HwAccelerationTest/res/layout/view_layer_invalidation.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/view_layer_invalidation.xml
rename to tests/graphics/HwAccelerationTest/res/layout/view_layer_invalidation.xml
diff --git a/tests/HwAccelerationTest/res/layout/view_layers.xml b/tests/graphics/HwAccelerationTest/res/layout/view_layers.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/view_layers.xml
rename to tests/graphics/HwAccelerationTest/res/layout/view_layers.xml
diff --git a/tests/HwAccelerationTest/res/layout/view_layers_3.xml b/tests/graphics/HwAccelerationTest/res/layout/view_layers_3.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/view_layers_3.xml
rename to tests/graphics/HwAccelerationTest/res/layout/view_layers_3.xml
diff --git a/tests/HwAccelerationTest/res/layout/view_layers_4.xml b/tests/graphics/HwAccelerationTest/res/layout/view_layers_4.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/view_layers_4.xml
rename to tests/graphics/HwAccelerationTest/res/layout/view_layers_4.xml
diff --git a/tests/HwAccelerationTest/res/layout/view_layers_5.xml b/tests/graphics/HwAccelerationTest/res/layout/view_layers_5.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/view_layers_5.xml
rename to tests/graphics/HwAccelerationTest/res/layout/view_layers_5.xml
diff --git a/tests/HwAccelerationTest/res/layout/view_properties.xml b/tests/graphics/HwAccelerationTest/res/layout/view_properties.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/view_properties.xml
rename to tests/graphics/HwAccelerationTest/res/layout/view_properties.xml
diff --git a/tests/HwAccelerationTest/res/layout/view_runtime_shader.xml b/tests/graphics/HwAccelerationTest/res/layout/view_runtime_shader.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/view_runtime_shader.xml
rename to tests/graphics/HwAccelerationTest/res/layout/view_runtime_shader.xml
diff --git a/tests/HwAccelerationTest/res/layout/widget.xml b/tests/graphics/HwAccelerationTest/res/layout/widget.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/widget.xml
rename to tests/graphics/HwAccelerationTest/res/layout/widget.xml
diff --git a/tests/HwAccelerationTest/res/layout/z_ordering.xml b/tests/graphics/HwAccelerationTest/res/layout/z_ordering.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/layout/z_ordering.xml
rename to tests/graphics/HwAccelerationTest/res/layout/z_ordering.xml
diff --git a/tests/HwAccelerationTest/res/raw/colorgrid_video.mp4 b/tests/graphics/HwAccelerationTest/res/raw/colorgrid_video.mp4
similarity index 100%
rename from tests/HwAccelerationTest/res/raw/colorgrid_video.mp4
rename to tests/graphics/HwAccelerationTest/res/raw/colorgrid_video.mp4
Binary files differ
diff --git a/tests/HwAccelerationTest/res/values/strings.xml b/tests/graphics/HwAccelerationTest/res/values/strings.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/values/strings.xml
rename to tests/graphics/HwAccelerationTest/res/values/strings.xml
diff --git a/tests/HwAccelerationTest/res/values/styles.xml b/tests/graphics/HwAccelerationTest/res/values/styles.xml
similarity index 100%
rename from tests/HwAccelerationTest/res/values/styles.xml
rename to tests/graphics/HwAccelerationTest/res/values/styles.xml
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedBlendActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/AdvancedBlendActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedBlendActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/AdvancedBlendActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/AdvancedGradientsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Alpha8BitmapActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Alpha8BitmapActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/Alpha8BitmapActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Alpha8BitmapActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/AlphaLayersActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Animated3dActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Animated3dActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/Animated3dActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Animated3dActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/AssetsAtlasActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/AssetsAtlasActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/AssetsAtlasActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/AssetsAtlasActivity.java
diff --git a/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java
new file mode 100644
index 0000000..8086b29
--- /dev/null
+++ b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Outline;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ScrollView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BackdropBlurActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final ScrollView scrollView = new ScrollView(this);
+ final FrameLayout innerFrame = new FrameLayout(this);
+ final View backgroundView = new View(this);
+ backgroundView.setBackgroundResource(R.drawable.robot_repeated);
+ innerFrame.addView(backgroundView, ViewGroup.LayoutParams.MATCH_PARENT, 10000);
+ scrollView.addView(innerFrame,
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ final FrameLayout contentView = new FrameLayout(this);
+ contentView.addView(scrollView,
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ contentView.addView(new BackdropBlurView(this), 300, 300);
+ setContentView(contentView);
+ }
+
+ private static class BackdropBlurView extends View {
+ private final float mBlurRadius = 60f;
+ private final float mSaturation = 1.8f;
+
+ private float mDownOffsetX;
+ private float mDownOffsetY;
+
+ BackdropBlurView(Context c) {
+ super(c);
+
+ // init RenderEffect.
+ final RenderEffect blurEffect = RenderEffect.createBlurEffect(
+ mBlurRadius, mBlurRadius,
+ null, Shader.TileMode.MIRROR // TileMode.MIRROR is better for blur.
+ );
+
+ final ColorMatrix colorMatrix = new ColorMatrix();
+ colorMatrix.setSaturation(mSaturation);
+ final RenderEffect effect = RenderEffect.createColorFilterEffect(
+ new ColorMatrixColorFilter(colorMatrix), blurEffect
+ );
+ setBackdropRenderEffect(effect);
+
+ // clip to a round outline.
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View v, Outline outline) {
+ outline.setOval(0, 0, v.getWidth(), v.getHeight());
+ }
+ });
+ setClipToOutline(true);
+
+ animate().setInterpolator(new DecelerateInterpolator(2.0f));
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.drawColor(0x99F0F0F0);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownOffsetX = event.getRawX() - getTranslationX();
+ mDownOffsetY = event.getRawY() - getTranslationY();
+ animate().scaleX(1.5f).scaleY(1.5f).start();
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ animate().scaleX(1f).scaleY(1f).start();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ setTranslationX(event.getRawX() - mDownOffsetX);
+ setTranslationY(event.getRawY() - mDownOffsetY);
+ break;
+ }
+ return true;
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BigGradientActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BigGradientActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/BigGradientActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BigGradientActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshLayerActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshLayerActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshLayerActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapMeshLayerActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMutateActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapMutateActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/BitmapMutateActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapMutateActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Bitmaps3dActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Bitmaps3dActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/Bitmaps3dActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Bitmaps3dActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsRectActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapsRectActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsRectActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapsRectActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsSkewActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapsSkewActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsSkewActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BitmapsSkewActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CanvasTextureViewActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/CanvasTextureViewActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/CanvasTextureViewActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/CanvasTextureViewActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClearActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ClearActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ClearActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ClearActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipOutlineActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ClipOutlineActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ClipOutlineActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ClipOutlineActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegion2Activity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ClipRegion2Activity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegion2Activity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ClipRegion2Activity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegion3Activity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ClipRegion3Activity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegion3Activity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ClipRegion3Activity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ColoredShadowsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DatePicker.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/DatePicker.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/DatePicker.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/DatePicker.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DatePickerActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/DatePickerActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/DatePickerActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/DatePickerActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DisplayListLayersActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/DisplayListLayersActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/DisplayListLayersActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/DisplayListLayersActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/DrawIntoHwBitmapActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/FramebufferBlendActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/FramebufferBlendActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/FramebufferBlendActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/FramebufferBlendActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GLDepthTestActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GLTextureViewActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GetBitmapActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GetBitmapActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GetBitmapSurfaceViewActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GlyphCacheActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GlyphCacheActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/GlyphCacheActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GlyphCacheActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GradientStopsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GradientStopsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/GradientStopsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GradientStopsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/GradientsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasTextureViewActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasTextureViewActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasTextureViewActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasTextureViewActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HwTests.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/HwTests.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/HwTests.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/HwTests.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LabelsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/LabelsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/LabelsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/LabelsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/LayersActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LooperAcceleration.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/LooperAcceleration.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/LooperAcceleration.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/LooperAcceleration.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MarqueeActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MarqueeActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MarqueeActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MarqueeActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MatrixActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MatrixActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MatrixActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MatrixActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MaxBitmapSizeActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MaxBitmapSizeActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MaxBitmapSizeActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MaxBitmapSizeActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MipMapActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MipMapActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MipMapActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MipMapActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MoreNinePatchesActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MoreNinePatchesActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MoreNinePatchesActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MoreNinePatchesActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MoreShadersActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MovingSurfaceViewActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiLayersActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MultiLayersActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MultiLayersActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MultiLayersActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/NewLayersActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/NinePatchesActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/NinePatchesActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/NinePatchesActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/NinePatchesActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/NoAATextActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/NoAATextActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/NoAATextActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/NoAATextActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/OpaqueActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/OpaqueActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/OpaqueActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/OpaqueActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PaintDrawFilterActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PaintDrawFilterActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PaintDrawFilterActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PaintDrawFilterActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathDestructionActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PathDestructionActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PathDestructionActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PathDestructionActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathOpsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PathOpsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PathOpsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PathOpsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PathsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PictureCaptureDemo.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PictureCaptureDemo.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PictureCaptureDemo.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PictureCaptureDemo.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PixelCopyWindow.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PixelCopyWindow.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PixelCopyWindow.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PixelCopyWindow.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PointsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PointsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PointsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PointsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PosTextActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PosTextActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PosTextActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PosTextActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionClippingActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ProjectionClippingActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionClippingActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ProjectionClippingActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ResizeActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ResizeActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ResizeActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ResizeActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RevealActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/RevealActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/RevealActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/RevealActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Rotate3dTextActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Rotate3dTextActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/Rotate3dTextActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Rotate3dTextActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RotationActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/RotationActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/RotationActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/RotationActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ScaledPathsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScaledPathsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ScaledPathsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScaledPathsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ScrollingStretchSurfaceViewActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingStretchSurfaceViewActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ScrollingStretchSurfaceViewActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ScrollingStretchSurfaceViewActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ShadersActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SimplePatchActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/SimplePatchActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/SimplePatchActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/SimplePatchActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SimplePathsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/SimplePathsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/SimplePathsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/SimplePathsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SingleFrameTextureViewTestActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/SingleFrameTextureViewTestActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/SingleFrameTextureViewTestActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/SingleFrameTextureViewTestActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SmallCircleActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/SmallCircleActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/SmallCircleActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/SmallCircleActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StackActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/StackActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/StackActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/StackActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TJunctionActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TJunctionActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/TJunctionActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TJunctionActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextFadeActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextFadeActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/TextFadeActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextFadeActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextGammaActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextGammaActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/TextGammaActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextGammaActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextPathActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextPathActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/TextPathActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextPathActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ThinPatchesActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Transform3dActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Transform3dActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/Transform3dActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/Transform3dActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TransformsAndAnimationsActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TransformsAndAnimationsActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/TransformsAndAnimationsActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TransformsAndAnimationsActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/VideoViewCaptureActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/VideoViewCaptureActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/VideoViewCaptureActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/VideoViewCaptureActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewFlipperActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewFlipperActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ViewFlipperActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewFlipperActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayerInvalidationActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayerInvalidationActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayerInvalidationActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayerInvalidationActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewPropertyAlphaActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewPropertyAlphaActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ViewPropertyAlphaActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ViewPropertyAlphaActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/XfermodeActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/XfermodeActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/XfermodeActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/XfermodeActivity.java
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ZOrderingActivity.java b/tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ZOrderingActivity.java
similarity index 100%
rename from tests/HwAccelerationTest/src/com/android/test/hwui/ZOrderingActivity.java
rename to tests/graphics/HwAccelerationTest/src/com/android/test/hwui/ZOrderingActivity.java
diff --git a/tests/graphics/OWNERS b/tests/graphics/OWNERS
new file mode 100644
index 0000000..fb7fc14
--- /dev/null
+++ b/tests/graphics/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 1075130
+
+include /libs/hwui/OWNERS
diff --git a/tests/RenderThreadTest/Android.bp b/tests/graphics/RenderThreadTest/Android.bp
similarity index 100%
rename from tests/RenderThreadTest/Android.bp
rename to tests/graphics/RenderThreadTest/Android.bp
diff --git a/tests/RenderThreadTest/AndroidManifest.xml b/tests/graphics/RenderThreadTest/AndroidManifest.xml
similarity index 100%
rename from tests/RenderThreadTest/AndroidManifest.xml
rename to tests/graphics/RenderThreadTest/AndroidManifest.xml
diff --git a/tests/RenderThreadTest/res/drawable-hdpi/ic_launcher.png b/tests/graphics/RenderThreadTest/res/drawable-hdpi/ic_launcher.png
similarity index 100%
rename from tests/RenderThreadTest/res/drawable-hdpi/ic_launcher.png
rename to tests/graphics/RenderThreadTest/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/RenderThreadTest/res/drawable-mdpi/ic_launcher.png b/tests/graphics/RenderThreadTest/res/drawable-mdpi/ic_launcher.png
similarity index 100%
rename from tests/RenderThreadTest/res/drawable-mdpi/ic_launcher.png
rename to tests/graphics/RenderThreadTest/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/RenderThreadTest/res/drawable-xhdpi/ic_launcher.png b/tests/graphics/RenderThreadTest/res/drawable-xhdpi/ic_launcher.png
similarity index 100%
rename from tests/RenderThreadTest/res/drawable-xhdpi/ic_launcher.png
rename to tests/graphics/RenderThreadTest/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg b/tests/graphics/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg
similarity index 100%
rename from tests/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg
rename to tests/graphics/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg
Binary files differ
diff --git a/tests/RenderThreadTest/res/layout/activity_main.xml b/tests/graphics/RenderThreadTest/res/layout/activity_main.xml
similarity index 100%
rename from tests/RenderThreadTest/res/layout/activity_main.xml
rename to tests/graphics/RenderThreadTest/res/layout/activity_main.xml
diff --git a/tests/RenderThreadTest/res/layout/activity_sub.xml b/tests/graphics/RenderThreadTest/res/layout/activity_sub.xml
similarity index 100%
rename from tests/RenderThreadTest/res/layout/activity_sub.xml
rename to tests/graphics/RenderThreadTest/res/layout/activity_sub.xml
diff --git a/tests/RenderThreadTest/res/layout/item_layout.xml b/tests/graphics/RenderThreadTest/res/layout/item_layout.xml
similarity index 100%
rename from tests/RenderThreadTest/res/layout/item_layout.xml
rename to tests/graphics/RenderThreadTest/res/layout/item_layout.xml
diff --git a/tests/RenderThreadTest/res/values/strings.xml b/tests/graphics/RenderThreadTest/res/values/strings.xml
similarity index 100%
rename from tests/RenderThreadTest/res/values/strings.xml
rename to tests/graphics/RenderThreadTest/res/values/strings.xml
diff --git a/tests/RenderThreadTest/res/values/styles.xml b/tests/graphics/RenderThreadTest/res/values/styles.xml
similarity index 100%
rename from tests/RenderThreadTest/res/values/styles.xml
rename to tests/graphics/RenderThreadTest/res/values/styles.xml
diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/graphics/RenderThreadTest/src/com/example/renderthread/MainActivity.java
similarity index 100%
rename from tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
rename to tests/graphics/RenderThreadTest/src/com/example/renderthread/MainActivity.java
diff --git a/tests/RenderThreadTest/src/com/example/renderthread/SubActivity.java b/tests/graphics/RenderThreadTest/src/com/example/renderthread/SubActivity.java
similarity index 100%
rename from tests/RenderThreadTest/src/com/example/renderthread/SubActivity.java
rename to tests/graphics/RenderThreadTest/src/com/example/renderthread/SubActivity.java
diff --git a/tests/SilkFX/Android.bp b/tests/graphics/SilkFX/Android.bp
similarity index 100%
rename from tests/SilkFX/Android.bp
rename to tests/graphics/SilkFX/Android.bp
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/graphics/SilkFX/AndroidManifest.xml
similarity index 100%
rename from tests/SilkFX/AndroidManifest.xml
rename to tests/graphics/SilkFX/AndroidManifest.xml
diff --git a/tests/SilkFX/assets/gainmaps/city_night.jpg b/tests/graphics/SilkFX/assets/gainmaps/city_night.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/city_night.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/city_night.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/desert_palms.jpg b/tests/graphics/SilkFX/assets/gainmaps/desert_palms.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/desert_palms.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/desert_palms.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/desert_sunset.jpg b/tests/graphics/SilkFX/assets/gainmaps/desert_sunset.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/desert_sunset.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/desert_sunset.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/desert_wanda.jpg b/tests/graphics/SilkFX/assets/gainmaps/desert_wanda.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/desert_wanda.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/desert_wanda.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/fountain_night.jpg b/tests/graphics/SilkFX/assets/gainmaps/fountain_night.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/fountain_night.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/fountain_night.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/grand_canyon.jpg b/tests/graphics/SilkFX/assets/gainmaps/grand_canyon.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/grand_canyon.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/grand_canyon.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/lamps.jpg b/tests/graphics/SilkFX/assets/gainmaps/lamps.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/lamps.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/lamps.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/mountain_lake.jpg b/tests/graphics/SilkFX/assets/gainmaps/mountain_lake.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/mountain_lake.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/mountain_lake.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/mountains.jpg b/tests/graphics/SilkFX/assets/gainmaps/mountains.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/mountains.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/mountains.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/sunflower.jpg b/tests/graphics/SilkFX/assets/gainmaps/sunflower.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/sunflower.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/sunflower.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/train_station_night.jpg b/tests/graphics/SilkFX/assets/gainmaps/train_station_night.jpg
similarity index 100%
rename from tests/SilkFX/assets/gainmaps/train_station_night.jpg
rename to tests/graphics/SilkFX/assets/gainmaps/train_station_night.jpg
Binary files differ
diff --git a/tests/SilkFX/res/drawable-hdpi/background1.jpeg b/tests/graphics/SilkFX/res/drawable-hdpi/background1.jpeg
similarity index 100%
rename from tests/SilkFX/res/drawable-hdpi/background1.jpeg
rename to tests/graphics/SilkFX/res/drawable-hdpi/background1.jpeg
Binary files differ
diff --git a/tests/SilkFX/res/drawable-hdpi/background2.jpeg b/tests/graphics/SilkFX/res/drawable-hdpi/background2.jpeg
similarity index 100%
rename from tests/SilkFX/res/drawable-hdpi/background2.jpeg
rename to tests/graphics/SilkFX/res/drawable-hdpi/background2.jpeg
Binary files differ
diff --git a/tests/SilkFX/res/drawable-hdpi/background3.jpeg b/tests/graphics/SilkFX/res/drawable-hdpi/background3.jpeg
similarity index 100%
rename from tests/SilkFX/res/drawable-hdpi/background3.jpeg
rename to tests/graphics/SilkFX/res/drawable-hdpi/background3.jpeg
Binary files differ
diff --git a/tests/SilkFX/res/drawable-hdpi/noise.png b/tests/graphics/SilkFX/res/drawable-hdpi/noise.png
similarity index 100%
rename from tests/SilkFX/res/drawable-hdpi/noise.png
rename to tests/graphics/SilkFX/res/drawable-hdpi/noise.png
Binary files differ
diff --git a/tests/SilkFX/res/drawable-nodpi/blue_sweep_gradient.xml b/tests/graphics/SilkFX/res/drawable-nodpi/blue_sweep_gradient.xml
similarity index 100%
rename from tests/SilkFX/res/drawable-nodpi/blue_sweep_gradient.xml
rename to tests/graphics/SilkFX/res/drawable-nodpi/blue_sweep_gradient.xml
diff --git a/tests/SilkFX/res/drawable-nodpi/dark_gradient.xml b/tests/graphics/SilkFX/res/drawable-nodpi/dark_gradient.xml
similarity index 100%
rename from tests/SilkFX/res/drawable-nodpi/dark_gradient.xml
rename to tests/graphics/SilkFX/res/drawable-nodpi/dark_gradient.xml
diff --git a/tests/SilkFX/res/drawable-nodpi/dark_notification.png b/tests/graphics/SilkFX/res/drawable-nodpi/dark_notification.png
similarity index 100%
rename from tests/SilkFX/res/drawable-nodpi/dark_notification.png
rename to tests/graphics/SilkFX/res/drawable-nodpi/dark_notification.png
Binary files differ
diff --git a/tests/SilkFX/res/drawable-nodpi/green_sweep_gradient.xml b/tests/graphics/SilkFX/res/drawable-nodpi/green_sweep_gradient.xml
similarity index 100%
rename from tests/SilkFX/res/drawable-nodpi/green_sweep_gradient.xml
rename to tests/graphics/SilkFX/res/drawable-nodpi/green_sweep_gradient.xml
diff --git a/tests/SilkFX/res/drawable-nodpi/grey_sweep_gradient.xml b/tests/graphics/SilkFX/res/drawable-nodpi/grey_sweep_gradient.xml
similarity index 100%
rename from tests/SilkFX/res/drawable-nodpi/grey_sweep_gradient.xml
rename to tests/graphics/SilkFX/res/drawable-nodpi/grey_sweep_gradient.xml
diff --git a/tests/SilkFX/res/drawable-nodpi/light_gradient.xml b/tests/graphics/SilkFX/res/drawable-nodpi/light_gradient.xml
similarity index 100%
rename from tests/SilkFX/res/drawable-nodpi/light_gradient.xml
rename to tests/graphics/SilkFX/res/drawable-nodpi/light_gradient.xml
diff --git a/tests/SilkFX/res/drawable-nodpi/light_notification.png b/tests/graphics/SilkFX/res/drawable-nodpi/light_notification.png
similarity index 100%
rename from tests/SilkFX/res/drawable-nodpi/light_notification.png
rename to tests/graphics/SilkFX/res/drawable-nodpi/light_notification.png
Binary files differ
diff --git a/tests/SilkFX/res/drawable-nodpi/red_sweep_gradient.xml b/tests/graphics/SilkFX/res/drawable-nodpi/red_sweep_gradient.xml
similarity index 100%
rename from tests/SilkFX/res/drawable-nodpi/red_sweep_gradient.xml
rename to tests/graphics/SilkFX/res/drawable-nodpi/red_sweep_gradient.xml
diff --git a/tests/SilkFX/res/drawable/background_blur_drawable.xml b/tests/graphics/SilkFX/res/drawable/background_blur_drawable.xml
similarity index 100%
rename from tests/SilkFX/res/drawable/background_blur_drawable.xml
rename to tests/graphics/SilkFX/res/drawable/background_blur_drawable.xml
diff --git a/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml b/tests/graphics/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
similarity index 100%
rename from tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
rename to tests/graphics/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
diff --git a/tests/SilkFX/res/layout-television/activity_glass.xml b/tests/graphics/SilkFX/res/layout-television/activity_glass.xml
similarity index 100%
rename from tests/SilkFX/res/layout-television/activity_glass.xml
rename to tests/graphics/SilkFX/res/layout-television/activity_glass.xml
diff --git a/tests/SilkFX/res/layout/activity_background_blur.xml b/tests/graphics/SilkFX/res/layout/activity_background_blur.xml
similarity index 100%
rename from tests/SilkFX/res/layout/activity_background_blur.xml
rename to tests/graphics/SilkFX/res/layout/activity_background_blur.xml
diff --git a/tests/SilkFX/res/layout/activity_glass.xml b/tests/graphics/SilkFX/res/layout/activity_glass.xml
similarity index 100%
rename from tests/SilkFX/res/layout/activity_glass.xml
rename to tests/graphics/SilkFX/res/layout/activity_glass.xml
diff --git a/tests/SilkFX/res/layout/bling_notifications.xml b/tests/graphics/SilkFX/res/layout/bling_notifications.xml
similarity index 100%
rename from tests/SilkFX/res/layout/bling_notifications.xml
rename to tests/graphics/SilkFX/res/layout/bling_notifications.xml
diff --git a/tests/SilkFX/res/layout/color_grid.xml b/tests/graphics/SilkFX/res/layout/color_grid.xml
similarity index 100%
rename from tests/SilkFX/res/layout/color_grid.xml
rename to tests/graphics/SilkFX/res/layout/color_grid.xml
diff --git a/tests/SilkFX/res/layout/color_mode_controls.xml b/tests/graphics/SilkFX/res/layout/color_mode_controls.xml
similarity index 100%
rename from tests/SilkFX/res/layout/color_mode_controls.xml
rename to tests/graphics/SilkFX/res/layout/color_mode_controls.xml
diff --git a/tests/SilkFX/res/layout/common_base.xml b/tests/graphics/SilkFX/res/layout/common_base.xml
similarity index 100%
rename from tests/SilkFX/res/layout/common_base.xml
rename to tests/graphics/SilkFX/res/layout/common_base.xml
diff --git a/tests/SilkFX/res/layout/gainmap_decode_test.xml b/tests/graphics/SilkFX/res/layout/gainmap_decode_test.xml
similarity index 100%
rename from tests/SilkFX/res/layout/gainmap_decode_test.xml
rename to tests/graphics/SilkFX/res/layout/gainmap_decode_test.xml
diff --git a/tests/SilkFX/res/layout/gainmap_image.xml b/tests/graphics/SilkFX/res/layout/gainmap_image.xml
similarity index 100%
rename from tests/SilkFX/res/layout/gainmap_image.xml
rename to tests/graphics/SilkFX/res/layout/gainmap_image.xml
diff --git a/tests/SilkFX/res/layout/gainmap_metadata.xml b/tests/graphics/SilkFX/res/layout/gainmap_metadata.xml
similarity index 100%
rename from tests/SilkFX/res/layout/gainmap_metadata.xml
rename to tests/graphics/SilkFX/res/layout/gainmap_metadata.xml
diff --git a/tests/SilkFX/res/layout/gainmap_transform_test.xml b/tests/graphics/SilkFX/res/layout/gainmap_transform_test.xml
similarity index 100%
rename from tests/SilkFX/res/layout/gainmap_transform_test.xml
rename to tests/graphics/SilkFX/res/layout/gainmap_transform_test.xml
diff --git a/tests/SilkFX/res/layout/gradient_sweep.xml b/tests/graphics/SilkFX/res/layout/gradient_sweep.xml
similarity index 100%
rename from tests/SilkFX/res/layout/gradient_sweep.xml
rename to tests/graphics/SilkFX/res/layout/gradient_sweep.xml
diff --git a/tests/SilkFX/res/layout/hdr_glows.xml b/tests/graphics/SilkFX/res/layout/hdr_glows.xml
similarity index 100%
rename from tests/SilkFX/res/layout/hdr_glows.xml
rename to tests/graphics/SilkFX/res/layout/hdr_glows.xml
diff --git a/tests/SilkFX/res/layout/hdr_image_viewer.xml b/tests/graphics/SilkFX/res/layout/hdr_image_viewer.xml
similarity index 100%
rename from tests/SilkFX/res/layout/hdr_image_viewer.xml
rename to tests/graphics/SilkFX/res/layout/hdr_image_viewer.xml
diff --git a/tests/SilkFX/res/values/style.xml b/tests/graphics/SilkFX/res/values/style.xml
similarity index 100%
rename from tests/SilkFX/res/values/style.xml
rename to tests/graphics/SilkFX/res/values/style.xml
diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/Main.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/Main.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/Main.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/app/BaseDemoActivity.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/HdrImageViewer.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/app/HdrImageViewer.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/app/HdrImageViewer.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/app/HdrImageViewer.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/app/WindowObserver.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/HDRIndicator.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/common/HDRIndicator.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/common/HDRIndicator.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/common/HDRIndicator.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/BlingyNotification.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/BlingyNotification.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/hdr/BlingyNotification.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/BlingyNotification.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/ColorGrid.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/ColorGrid.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/hdr/ColorGrid.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/ColorGrid.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowActivity.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GlowActivity.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/hdr/GlowActivity.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GlowActivity.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GlowingCard.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GlowingCard.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/hdr/GlowingCard.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/GlowingCard.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/materials/GlassActivity.kt
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/graphics/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
similarity index 100%
rename from tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
rename to tests/graphics/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
diff --git a/tests/VectorDrawableTest/Android.bp b/tests/graphics/VectorDrawableTest/Android.bp
similarity index 100%
rename from tests/VectorDrawableTest/Android.bp
rename to tests/graphics/VectorDrawableTest/Android.bp
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/graphics/VectorDrawableTest/AndroidManifest.xml
similarity index 100%
rename from tests/VectorDrawableTest/AndroidManifest.xml
rename to tests/graphics/VectorDrawableTest/AndroidManifest.xml
diff --git a/tests/VectorDrawableTest/OWNERS b/tests/graphics/VectorDrawableTest/OWNERS
similarity index 100%
rename from tests/VectorDrawableTest/OWNERS
rename to tests/graphics/VectorDrawableTest/OWNERS
diff --git a/tests/VectorDrawableTest/res/anim/alpha_animation_progress_bar.xml b/tests/graphics/VectorDrawableTest/res/anim/alpha_animation_progress_bar.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/alpha_animation_progress_bar.xml
rename to tests/graphics/VectorDrawableTest/res/anim/alpha_animation_progress_bar.xml
diff --git a/tests/VectorDrawableTest/res/anim/animation_favorite.xml b/tests/graphics/VectorDrawableTest/res/anim/animation_favorite.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/animation_favorite.xml
rename to tests/graphics/VectorDrawableTest/res/anim/animation_favorite.xml
diff --git a/tests/VectorDrawableTest/res/anim/animation_favorite02.xml b/tests/graphics/VectorDrawableTest/res/anim/animation_favorite02.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/animation_favorite02.xml
rename to tests/graphics/VectorDrawableTest/res/anim/animation_favorite02.xml
diff --git a/tests/VectorDrawableTest/res/anim/animation_grouping_1_01.xml b/tests/graphics/VectorDrawableTest/res/anim/animation_grouping_1_01.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/animation_grouping_1_01.xml
rename to tests/graphics/VectorDrawableTest/res/anim/animation_grouping_1_01.xml
diff --git a/tests/VectorDrawableTest/res/anim/animation_grouping_1_02.xml b/tests/graphics/VectorDrawableTest/res/anim/animation_grouping_1_02.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/animation_grouping_1_02.xml
rename to tests/graphics/VectorDrawableTest/res/anim/animation_grouping_1_02.xml
diff --git a/tests/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect1_scale.xml b/tests/graphics/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect1_scale.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect1_scale.xml
rename to tests/graphics/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect1_scale.xml
diff --git a/tests/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect1_translate.xml b/tests/graphics/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect1_translate.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect1_translate.xml
rename to tests/graphics/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect1_translate.xml
diff --git a/tests/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect2_scale.xml b/tests/graphics/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect2_scale.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect2_scale.xml
rename to tests/graphics/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect2_scale.xml
diff --git a/tests/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect2_translate.xml b/tests/graphics/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect2_translate.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect2_translate.xml
rename to tests/graphics/VectorDrawableTest/res/anim/animation_linear_progress_bar_rect2_translate.xml
diff --git a/tests/VectorDrawableTest/res/anim/blink.xml b/tests/graphics/VectorDrawableTest/res/anim/blink.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/blink.xml
rename to tests/graphics/VectorDrawableTest/res/anim/blink.xml
diff --git a/tests/VectorDrawableTest/res/anim/ic_hourglass_animation_fill_outlines.xml b/tests/graphics/VectorDrawableTest/res/anim/ic_hourglass_animation_fill_outlines.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/ic_hourglass_animation_fill_outlines.xml
rename to tests/graphics/VectorDrawableTest/res/anim/ic_hourglass_animation_fill_outlines.xml
diff --git a/tests/VectorDrawableTest/res/anim/ic_hourglass_animation_hourglass_frame.xml b/tests/graphics/VectorDrawableTest/res/anim/ic_hourglass_animation_hourglass_frame.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/ic_hourglass_animation_hourglass_frame.xml
rename to tests/graphics/VectorDrawableTest/res/anim/ic_hourglass_animation_hourglass_frame.xml
diff --git a/tests/VectorDrawableTest/res/anim/ic_hourglass_animation_mask_1.xml b/tests/graphics/VectorDrawableTest/res/anim/ic_hourglass_animation_mask_1.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/ic_hourglass_animation_mask_1.xml
rename to tests/graphics/VectorDrawableTest/res/anim/ic_hourglass_animation_mask_1.xml
diff --git a/tests/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_arrows_1.xml b/tests/graphics/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_arrows_1.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_arrows_1.xml
rename to tests/graphics/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_arrows_1.xml
diff --git a/tests/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_device_1.xml b/tests/graphics/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_device_1.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_device_1.xml
rename to tests/graphics/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_device_1.xml
diff --git a/tests/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_device_2.xml b/tests/graphics/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_device_2.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_device_2.xml
rename to tests/graphics/VectorDrawableTest/res/anim/ic_rotate_2_portrait_v2_animation_device_2.xml
diff --git a/tests/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_cross_1.xml b/tests/graphics/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_cross_1.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_cross_1.xml
rename to tests/graphics/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_cross_1.xml
diff --git a/tests/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_ic_signal_airplane.xml b/tests/graphics/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_ic_signal_airplane.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_ic_signal_airplane.xml
rename to tests/graphics/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_ic_signal_airplane.xml
diff --git a/tests/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_mask_2.xml b/tests/graphics/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_mask_2.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_mask_2.xml
rename to tests/graphics/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_mask_2.xml
diff --git a/tests/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_path_1_1.xml b/tests/graphics/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_path_1_1.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_path_1_1.xml
rename to tests/graphics/VectorDrawableTest/res/anim/ic_signal_airplane_v2_animation_path_1_1.xml
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation01.xml b/tests/graphics/VectorDrawableTest/res/anim/trim_path_animation01.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/trim_path_animation01.xml
rename to tests/graphics/VectorDrawableTest/res/anim/trim_path_animation01.xml
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation02.xml b/tests/graphics/VectorDrawableTest/res/anim/trim_path_animation02.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/trim_path_animation02.xml
rename to tests/graphics/VectorDrawableTest/res/anim/trim_path_animation02.xml
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml b/tests/graphics/VectorDrawableTest/res/anim/trim_path_animation03.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/trim_path_animation03.xml
rename to tests/graphics/VectorDrawableTest/res/anim/trim_path_animation03.xml
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml b/tests/graphics/VectorDrawableTest/res/anim/trim_path_animation04.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/trim_path_animation04.xml
rename to tests/graphics/VectorDrawableTest/res/anim/trim_path_animation04.xml
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation05.xml b/tests/graphics/VectorDrawableTest/res/anim/trim_path_animation05.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/trim_path_animation05.xml
rename to tests/graphics/VectorDrawableTest/res/anim/trim_path_animation05.xml
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation06.xml b/tests/graphics/VectorDrawableTest/res/anim/trim_path_animation06.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/trim_path_animation06.xml
rename to tests/graphics/VectorDrawableTest/res/anim/trim_path_animation06.xml
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation_progress_bar.xml b/tests/graphics/VectorDrawableTest/res/anim/trim_path_animation_progress_bar.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/anim/trim_path_animation_progress_bar.xml
rename to tests/graphics/VectorDrawableTest/res/anim/trim_path_animation_progress_bar.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_linear.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_clamp.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear_clamp.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_linear_clamp.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear_clamp.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_item.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear_item.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_linear_item.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear_item.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap_mirror.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap_mirror.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap_mirror.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear_item_overlap_mirror.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_linear_item_repeat.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear_item_repeat.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_linear_item_repeat.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_linear_item_repeat.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_radial.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_clamp.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial_clamp.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_radial_clamp.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial_clamp.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial_item.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_repeat.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial_item_repeat.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_radial_item_repeat.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial_item_repeat.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial_item_short.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short_mirror.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial_item_short_mirror.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_radial_item_short_mirror.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_radial_item_short_mirror.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_sweep.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_clamp.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep_clamp.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_sweep_clamp.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep_clamp.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep_item.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_sweep_item.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep_item.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep_item_long.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep_item_long.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long_mirror.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep_item_long_mirror.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_long_mirror.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep_item_long_mirror.xml
diff --git a/tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_repeat.xml b/tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep_item_repeat.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/fill_gradient_sweep_item_repeat.xml
rename to tests/graphics/VectorDrawableTest/res/color/fill_gradient_sweep_item_repeat.xml
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient.xml b/tests/graphics/VectorDrawableTest/res/color/stroke_gradient.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/stroke_gradient.xml
rename to tests/graphics/VectorDrawableTest/res/color/stroke_gradient.xml
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_clamp.xml b/tests/graphics/VectorDrawableTest/res/color/stroke_gradient_clamp.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/stroke_gradient_clamp.xml
rename to tests/graphics/VectorDrawableTest/res/color/stroke_gradient_clamp.xml
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_item.xml b/tests/graphics/VectorDrawableTest/res/color/stroke_gradient_item.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/stroke_gradient_item.xml
rename to tests/graphics/VectorDrawableTest/res/color/stroke_gradient_item.xml
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha.xml b/tests/graphics/VectorDrawableTest/res/color/stroke_gradient_item_alpha.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha.xml
rename to tests/graphics/VectorDrawableTest/res/color/stroke_gradient_item_alpha.xml
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha_mirror.xml b/tests/graphics/VectorDrawableTest/res/color/stroke_gradient_item_alpha_mirror.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/stroke_gradient_item_alpha_mirror.xml
rename to tests/graphics/VectorDrawableTest/res/color/stroke_gradient_item_alpha_mirror.xml
diff --git a/tests/VectorDrawableTest/res/color/stroke_gradient_item_repeat.xml b/tests/graphics/VectorDrawableTest/res/color/stroke_gradient_item_repeat.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/stroke_gradient_item_repeat.xml
rename to tests/graphics/VectorDrawableTest/res/color/stroke_gradient_item_repeat.xml
diff --git a/tests/VectorDrawableTest/res/color/vector_icon_fill_state_list.xml b/tests/graphics/VectorDrawableTest/res/color/vector_icon_fill_state_list.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/vector_icon_fill_state_list.xml
rename to tests/graphics/VectorDrawableTest/res/color/vector_icon_fill_state_list.xml
diff --git a/tests/VectorDrawableTest/res/color/vector_icon_fill_state_list_simple.xml b/tests/graphics/VectorDrawableTest/res/color/vector_icon_fill_state_list_simple.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/vector_icon_fill_state_list_simple.xml
rename to tests/graphics/VectorDrawableTest/res/color/vector_icon_fill_state_list_simple.xml
diff --git a/tests/VectorDrawableTest/res/color/vector_icon_stroke_state_list.xml b/tests/graphics/VectorDrawableTest/res/color/vector_icon_stroke_state_list.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/vector_icon_stroke_state_list.xml
rename to tests/graphics/VectorDrawableTest/res/color/vector_icon_stroke_state_list.xml
diff --git a/tests/VectorDrawableTest/res/color/vector_icon_stroke_state_list_simple.xml b/tests/graphics/VectorDrawableTest/res/color/vector_icon_stroke_state_list_simple.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/color/vector_icon_stroke_state_list_simple.xml
rename to tests/graphics/VectorDrawableTest/res/color/vector_icon_stroke_state_list_simple.xml
diff --git a/tests/VectorDrawableTest/res/drawable-hdpi/icon.png b/tests/graphics/VectorDrawableTest/res/drawable-hdpi/icon.png
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable-hdpi/icon.png
rename to tests/graphics/VectorDrawableTest/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg b/tests/graphics/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg
rename to tests/graphics/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg
Binary files differ
diff --git a/tests/VectorDrawableTest/res/drawable/animated_vector_drawable_attr_icon.xml b/tests/graphics/VectorDrawableTest/res/drawable/animated_vector_drawable_attr_icon.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/animated_vector_drawable_attr_icon.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/animated_vector_drawable_attr_icon.xml
diff --git a/tests/VectorDrawableTest/res/drawable/animated_vector_drawable_attr_icon_animated.xml b/tests/graphics/VectorDrawableTest/res/drawable/animated_vector_drawable_attr_icon_animated.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/animated_vector_drawable_attr_icon_animated.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/animated_vector_drawable_attr_icon_animated.xml
diff --git a/tests/VectorDrawableTest/res/drawable/animation_drawable_vector.xml b/tests/graphics/VectorDrawableTest/res/drawable/animation_drawable_vector.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/animation_drawable_vector.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/animation_drawable_vector.xml
diff --git a/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml b/tests/graphics/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
diff --git a/tests/VectorDrawableTest/res/drawable/animation_vector_drawable_favorite.xml b/tests/graphics/VectorDrawableTest/res/drawable/animation_vector_drawable_favorite.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/animation_vector_drawable_favorite.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/animation_vector_drawable_favorite.xml
diff --git a/tests/VectorDrawableTest/res/drawable/animation_vector_drawable_grouping_1.xml b/tests/graphics/VectorDrawableTest/res/drawable/animation_vector_drawable_grouping_1.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/animation_vector_drawable_grouping_1.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/animation_vector_drawable_grouping_1.xml
diff --git a/tests/VectorDrawableTest/res/drawable/animation_vector_linear_progress_bar.xml b/tests/graphics/VectorDrawableTest/res/drawable/animation_vector_linear_progress_bar.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/animation_vector_linear_progress_bar.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/animation_vector_linear_progress_bar.xml
diff --git a/tests/VectorDrawableTest/res/drawable/animation_vector_progress_bar.xml b/tests/graphics/VectorDrawableTest/res/drawable/animation_vector_progress_bar.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/animation_vector_progress_bar.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/animation_vector_progress_bar.xml
diff --git a/tests/VectorDrawableTest/res/drawable/btn_radio_on_to_off_bundle.xml b/tests/graphics/VectorDrawableTest/res/drawable/btn_radio_on_to_off_bundle.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/btn_radio_on_to_off_bundle.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/btn_radio_on_to_off_bundle.xml
diff --git a/tests/VectorDrawableTest/res/drawable/ic_hourglass.xml b/tests/graphics/VectorDrawableTest/res/drawable/ic_hourglass.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/ic_hourglass.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/ic_hourglass.xml
diff --git a/tests/VectorDrawableTest/res/drawable/ic_hourglass_animation.xml b/tests/graphics/VectorDrawableTest/res/drawable/ic_hourglass_animation.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/ic_hourglass_animation.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/ic_hourglass_animation.xml
diff --git a/tests/VectorDrawableTest/res/drawable/ic_rotate_2_portrait_v2.xml b/tests/graphics/VectorDrawableTest/res/drawable/ic_rotate_2_portrait_v2.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/ic_rotate_2_portrait_v2.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/ic_rotate_2_portrait_v2.xml
diff --git a/tests/VectorDrawableTest/res/drawable/ic_rotate_2_portrait_v2_animation.xml b/tests/graphics/VectorDrawableTest/res/drawable/ic_rotate_2_portrait_v2_animation.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/ic_rotate_2_portrait_v2_animation.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/ic_rotate_2_portrait_v2_animation.xml
diff --git a/tests/VectorDrawableTest/res/drawable/ic_signal_airplane_v2.xml b/tests/graphics/VectorDrawableTest/res/drawable/ic_signal_airplane_v2.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/ic_signal_airplane_v2.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/ic_signal_airplane_v2.xml
diff --git a/tests/VectorDrawableTest/res/drawable/ic_signal_airplane_v2_animation.xml b/tests/graphics/VectorDrawableTest/res/drawable/ic_signal_airplane_v2_animation.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/ic_signal_airplane_v2_animation.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/ic_signal_airplane_v2_animation.xml
diff --git a/tests/VectorDrawableTest/res/drawable/icon.png b/tests/graphics/VectorDrawableTest/res/drawable/icon.png
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/icon.png
rename to tests/graphics/VectorDrawableTest/res/drawable/icon.png
Binary files differ
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_drawable04.xml b/tests/graphics/VectorDrawableTest/res/drawable/state_animation_drawable04.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/state_animation_drawable04.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/state_animation_drawable04.xml
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_drawable04_false.xml b/tests/graphics/VectorDrawableTest/res/drawable/state_animation_drawable04_false.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/state_animation_drawable04_false.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/state_animation_drawable04_false.xml
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01.xml b/tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable01.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable01.xml
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01_false.xml b/tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable01_false.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable01_false.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable01_false.xml
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable02.xml b/tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable02.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable02.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable02.xml
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable02_false.xml b/tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable02_false.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable02_false.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable02_false.xml
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03.xml b/tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable03.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable03.xml
diff --git a/tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03_false.xml b/tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable03_false.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/state_animation_vector_drawable03_false.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/state_animation_vector_drawable03_false.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable01.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable01.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable02.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable02.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable02.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable03.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable03.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable04.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable04.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable05.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable05.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable06.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable06.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable07.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable07.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable08.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable08.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable08.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable09.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable09.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable10.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable10.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable11.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable11.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable11.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable12.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable12.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable13.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable13.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable13.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable14.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable14.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable15.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable15.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable15.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable16.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable16.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable16.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable17.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable17.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable18.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable18.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable19.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable19.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable20.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable20.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable20.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable21.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable21.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable21.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable21.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable22.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable22.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable22.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable23.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable23.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable23.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable24.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable24.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable24.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable25.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable25.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable25.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable25.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable26.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable26.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable26.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable26.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable27.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable27.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable27.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable27.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable28.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable28.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable28.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable28.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable29.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable29.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable29.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable29.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable30.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable30.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable30.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable30.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_favorite.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_group_clip.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_group_clip.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable_group_clip.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_group_clip.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_grouping_1.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_grouping_1.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable_grouping_1.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_grouping_1.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_progress_bar.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_progress_bar.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable_progress_bar.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_progress_bar.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_scale0.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_scale0.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable_scale0.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_scale0.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_scale1.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_scale1.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable_scale1.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_scale1.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_scale2.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_scale2.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable_scale2.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_scale2.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_scale3.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_scale3.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_drawable_scale3.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_drawable_scale3.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_create.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_create.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_create.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_delete.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_delete.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_filltype_evenodd.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_filltype_evenodd.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_filltype_evenodd.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_filltype_evenodd.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_filltype_nonzero.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_filltype_nonzero.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_filltype_nonzero.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_filltype_nonzero.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_1.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_1.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1_clamp.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_1_clamp.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_gradient_1_clamp.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_1_clamp.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_2.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_2.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2_repeat.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_2_repeat.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_gradient_2_repeat.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_2_repeat.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_3.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_3.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3_mirror.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_3_mirror.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_gradient_3_mirror.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_gradient_3_mirror.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_heart.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_heart.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_settings.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_settings.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_state_list_simple.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_state_list_simple.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_state_list_simple.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_state_list_simple.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_state_list_theme.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_icon_state_list_theme.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_icon_state_list_theme.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_icon_state_list_theme.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_test01.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_test01.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_test01.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_test01.xml
diff --git a/tests/VectorDrawableTest/res/drawable/vector_test02.xml b/tests/graphics/VectorDrawableTest/res/drawable/vector_test02.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/drawable/vector_test02.xml
rename to tests/graphics/VectorDrawableTest/res/drawable/vector_test02.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/btn_radio_to_off_mtrl_animation_interpolator_0.xml b/tests/graphics/VectorDrawableTest/res/interpolator/btn_radio_to_off_mtrl_animation_interpolator_0.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/btn_radio_to_off_mtrl_animation_interpolator_0.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/btn_radio_to_off_mtrl_animation_interpolator_0.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/custom_path_interpolator.xml b/tests/graphics/VectorDrawableTest/res/interpolator/custom_path_interpolator.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/custom_path_interpolator.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/custom_path_interpolator.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/custom_path_interpolator_favorite.xml b/tests/graphics/VectorDrawableTest/res/interpolator/custom_path_interpolator_favorite.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/custom_path_interpolator_favorite.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/custom_path_interpolator_favorite.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/custom_path_interpolator_grouping_1_01.xml b/tests/graphics/VectorDrawableTest/res/interpolator/custom_path_interpolator_grouping_1_01.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/custom_path_interpolator_grouping_1_01.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/custom_path_interpolator_grouping_1_01.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_arrows_1_rotation_interpolator.xml b/tests/graphics/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_arrows_1_rotation_interpolator.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_arrows_1_rotation_interpolator.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_arrows_1_rotation_interpolator.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_arrows_1_scalex_interpolator.xml b/tests/graphics/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_arrows_1_scalex_interpolator.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_arrows_1_scalex_interpolator.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_arrows_1_scalex_interpolator.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_device_1_rotation_interpolator.xml b/tests/graphics/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_device_1_rotation_interpolator.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_device_1_rotation_interpolator.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_device_1_rotation_interpolator.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_device_2_pathdata_interpolator.xml b/tests/graphics/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_device_2_pathdata_interpolator.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_device_2_pathdata_interpolator.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/ic_rotate_2_portrait_v2_device_2_pathdata_interpolator.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_signal_airplane_v2_path_1_1_pathdata_interpolator.xml b/tests/graphics/VectorDrawableTest/res/interpolator/ic_signal_airplane_v2_path_1_1_pathdata_interpolator.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/ic_signal_airplane_v2_path_1_1_pathdata_interpolator.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/ic_signal_airplane_v2_path_1_1_pathdata_interpolator.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/trim_end_interpolator.xml b/tests/graphics/VectorDrawableTest/res/interpolator/trim_end_interpolator.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/trim_end_interpolator.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/trim_end_interpolator.xml
diff --git a/tests/VectorDrawableTest/res/interpolator/trim_start_interpolator.xml b/tests/graphics/VectorDrawableTest/res/interpolator/trim_start_interpolator.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/interpolator/trim_start_interpolator.xml
rename to tests/graphics/VectorDrawableTest/res/interpolator/trim_start_interpolator.xml
diff --git a/tests/VectorDrawableTest/res/layout/activity_animated_vector_drawable_attr.xml b/tests/graphics/VectorDrawableTest/res/layout/activity_animated_vector_drawable_attr.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/layout/activity_animated_vector_drawable_attr.xml
rename to tests/graphics/VectorDrawableTest/res/layout/activity_animated_vector_drawable_attr.xml
diff --git a/tests/VectorDrawableTest/res/values/attrs.xml b/tests/graphics/VectorDrawableTest/res/values/attrs.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/values/attrs.xml
rename to tests/graphics/VectorDrawableTest/res/values/attrs.xml
diff --git a/tests/VectorDrawableTest/res/values/colors.xml b/tests/graphics/VectorDrawableTest/res/values/colors.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/values/colors.xml
rename to tests/graphics/VectorDrawableTest/res/values/colors.xml
diff --git a/tests/VectorDrawableTest/res/values/strings.xml b/tests/graphics/VectorDrawableTest/res/values/strings.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/values/strings.xml
rename to tests/graphics/VectorDrawableTest/res/values/strings.xml
diff --git a/tests/VectorDrawableTest/res/values/styles.xml b/tests/graphics/VectorDrawableTest/res/values/styles.xml
similarity index 100%
rename from tests/VectorDrawableTest/res/values/styles.xml
rename to tests/graphics/VectorDrawableTest/res/values/styles.xml
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/AnimatedStateVectorDrawableTest.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableAttr.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableAttr.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableAttr.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableAttr.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableDupPerf.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableDupPerf.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableDupPerf.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableDupPerf.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/BoundsCheckTest.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/BoundsCheckTest.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/BoundsCheckTest.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/BoundsCheckTest.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/ScaleDrawableTests.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/ScaleDrawableTests.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/ScaleDrawableTests.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/ScaleDrawableTests.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorCheckbox.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorCheckbox.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/VectorCheckbox.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorCheckbox.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableDupPerf.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableDupPerf.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableDupPerf.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableDupPerf.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableStaticPerf.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableStaticPerf.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableStaticPerf.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableStaticPerf.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableTest.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableTest.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableTest.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableTest.java
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorPathChecking.java b/tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorPathChecking.java
similarity index 100%
rename from tests/VectorDrawableTest/src/com/android/test/dynamic/VectorPathChecking.java
rename to tests/graphics/VectorDrawableTest/src/com/android/test/dynamic/VectorPathChecking.java
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index a1a39ff..59dc689 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -117,6 +117,16 @@
return buildTestConfig(UNDERLYING_NETWORK_TEMPLATES);
}
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig.Builder newTestBuilderMinimal() {
+ final VcnGatewayConnectionConfig.Builder builder = newBuilder();
+ for (int caps : EXPOSED_CAPS) {
+ builder.addExposedCapability(caps);
+ }
+
+ return builder;
+ }
+
private static VcnGatewayConnectionConfig.Builder newBuilder() {
// Append a unique identifier to the name prefix to guarantee that all created
// VcnGatewayConnectionConfigs have a unique name (required by VcnConfig).
@@ -125,6 +135,17 @@
TUNNEL_CONNECTION_PARAMS);
}
+ private static VcnGatewayConnectionConfig.Builder newBuilderMinimal() {
+ final VcnGatewayConnectionConfig.Builder builder =
+ new VcnGatewayConnectionConfig.Builder(
+ "newBuilderMinimal", TUNNEL_CONNECTION_PARAMS);
+ for (int caps : EXPOSED_CAPS) {
+ builder.addExposedCapability(caps);
+ }
+
+ return builder;
+ }
+
private static VcnGatewayConnectionConfig buildTestConfigWithExposedCapsAndOptions(
VcnGatewayConnectionConfig.Builder builder,
Set<Integer> gatewayOptions,
@@ -273,6 +294,7 @@
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
assertEquals(MAX_MTU, config.getMaxMtu());
+ assertTrue(config.isSafeModeEnabled());
assertFalse(
config.hasGatewayOption(
@@ -290,6 +312,14 @@
}
@Test
+ public void testBuilderAndGettersSafeModeDisabled() {
+ final VcnGatewayConnectionConfig config =
+ newBuilderMinimal().setSafeModeEnabled(false).build();
+
+ assertFalse(config.isSafeModeEnabled());
+ }
+
+ @Test
public void testPersistableBundle() {
final VcnGatewayConnectionConfig config = buildTestConfig();
@@ -305,6 +335,14 @@
}
@Test
+ public void testPersistableBundleSafeModeDisabled() {
+ final VcnGatewayConnectionConfig config =
+ newBuilderMinimal().setSafeModeEnabled(false).build();
+
+ assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
+ }
+
+ @Test
public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
@@ -411,4 +449,18 @@
assertEquals(config, configEqual);
assertNotEquals(config, configNotEqual);
}
+
+ @Test
+ public void testSafeModeEnableDisableEquality() throws Exception {
+ final VcnGatewayConnectionConfig config = newBuilderMinimal().build();
+ final VcnGatewayConnectionConfig configEqual = newBuilderMinimal().build();
+
+ assertEquals(config.isSafeModeEnabled(), configEqual.isSafeModeEnabled());
+
+ final VcnGatewayConnectionConfig configNotEqual =
+ newBuilderMinimal().setSafeModeEnabled(false).build();
+
+ assertEquals(config, configEqual);
+ assertNotEquals(config, configNotEqual);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 302af52..f846164 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -75,6 +75,9 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
+import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
+import com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import com.android.server.vcn.util.MtuUtils;
@@ -651,6 +654,74 @@
verifySafeModeStateAndCallbackFired(2 /* invocationCount */, true /* isInSafeMode */);
}
+ private void verifySetSafeModeAlarm(
+ boolean safeModeEnabledByCaller,
+ boolean safeModeConfigFlagEnabled,
+ boolean expectingSafeModeEnabled)
+ throws Exception {
+ final VcnGatewayConnectionConfig config =
+ VcnGatewayConnectionConfigTest.newTestBuilderMinimal()
+ .setSafeModeEnabled(safeModeEnabledByCaller)
+ .build();
+ final VcnGatewayConnection.Dependencies deps =
+ mock(VcnGatewayConnection.Dependencies.class);
+ setUpWakeupMessage(
+ mSafeModeTimeoutAlarm, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM, deps);
+ doReturn(safeModeConfigFlagEnabled).when(mFeatureFlags).safeModeConfig();
+
+ final VcnGatewayConnection connection =
+ new VcnGatewayConnection(
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ config,
+ mGatewayStatusCallback,
+ true /* isMobileDataEnabled */,
+ deps);
+
+ connection.setSafeModeAlarm();
+
+ final int expectedCallCnt = expectingSafeModeEnabled ? 1 : 0;
+ verify(deps, times(expectedCallCnt))
+ .newWakeupMessage(
+ eq(mVcnContext),
+ any(),
+ eq(VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM),
+ any());
+ }
+
+ @Test
+ public void testSafeModeEnabled_configFlagEnabled() throws Exception {
+ verifySetSafeModeAlarm(
+ true /* safeModeEnabledByCaller */,
+ true /* safeModeConfigFlagEnabled */,
+ true /* expectingSafeModeEnabled */);
+ }
+
+ @Test
+ public void testSafeModeEnabled_configFlagDisabled() throws Exception {
+ verifySetSafeModeAlarm(
+ true /* safeModeEnabledByCaller */,
+ false /* safeModeConfigFlagEnabled */,
+ true /* expectingSafeModeEnabled */);
+ }
+
+ @Test
+ public void testSafeModeDisabled_configFlagEnabled() throws Exception {
+ verifySetSafeModeAlarm(
+ false /* safeModeEnabledByCaller */,
+ true /* safeModeConfigFlagEnabled */,
+ false /* expectingSafeModeEnabled */);
+ }
+
+ @Test
+ public void testSafeModeDisabled_configFlagDisabled() throws Exception {
+ verifySetSafeModeAlarm(
+ false /* safeModeEnabledByCaller */,
+ false /* safeModeConfigFlagEnabled */,
+ true /* expectingSafeModeEnabled */);
+ }
+
private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
triggerChildOpened();
mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 5efbf59..edced87 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -53,6 +53,7 @@
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionConnectionInfo;
+import android.net.vcn.FeatureFlags;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
@@ -165,6 +166,7 @@
@NonNull protected final Context mContext;
@NonNull protected final TestLooper mTestLooper;
@NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
+ @NonNull protected final FeatureFlags mFeatureFlags;
@NonNull protected final VcnContext mVcnContext;
@NonNull protected final VcnGatewayConnectionConfig mConfig;
@NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
@@ -190,6 +192,7 @@
mContext = mock(Context.class);
mTestLooper = new TestLooper();
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+ mFeatureFlags = mock(FeatureFlags.class);
mVcnContext = mock(VcnContext.class);
mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
@@ -222,6 +225,7 @@
doReturn(mContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
+ doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags();
doReturn(mUnderlyingNetworkController)
.when(mDeps)
@@ -241,8 +245,15 @@
doReturn(ELAPSED_REAL_TIME).when(mDeps).getElapsedRealTime();
}
+ protected void setUpWakeupMessage(
+ @NonNull WakeupMessage msg,
+ @NonNull String cmdName,
+ VcnGatewayConnection.Dependencies deps) {
+ doReturn(msg).when(deps).newWakeupMessage(eq(mVcnContext), any(), eq(cmdName), any());
+ }
+
private void setUpWakeupMessage(@NonNull WakeupMessage msg, @NonNull String cmdName) {
- doReturn(msg).when(mDeps).newWakeupMessage(eq(mVcnContext), any(), eq(cmdName), any());
+ setUpWakeupMessage(msg, cmdName, mDeps);
}
@Before
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 0d6dc35..40cba3e 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -119,9 +119,9 @@
"io/Util.cpp",
"io/ZipArchive.cpp",
"link/AutoVersioner.cpp",
+ "link/FeatureFlagsFilter.cpp",
"link/ManifestFixer.cpp",
"link/NoDefaultResourceRemover.cpp",
- "link/ProductFilter.cpp",
"link/PrivateAttributeMover.cpp",
"link/ReferenceLinker.cpp",
"link/ResourceExcluder.cpp",
@@ -134,6 +134,7 @@
"optimize/ResourceFilter.cpp",
"optimize/Obfuscator.cpp",
"optimize/VersionCollapser.cpp",
+ "process/ProductFilter.cpp",
"process/SymbolTable.cpp",
"split/TableSplitter.cpp",
"text/Printer.cpp",
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 03f9715..bb7b13a 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -45,6 +45,7 @@
#include "io/StringStream.h"
#include "io/Util.h"
#include "io/ZipArchive.h"
+#include "process/ProductFilter.h"
#include "trace/TraceBuffer.h"
#include "util/Files.h"
#include "util/Util.h"
@@ -179,6 +180,15 @@
if (!res_parser.Parse(&xml_parser)) {
return false;
}
+
+ if (options.product_.has_value()) {
+ if (!ProductFilter({*options.product_}, /* remove_default_config_values = */ true)
+ .Consume(context, &table)) {
+ context->GetDiagnostics()->Error(android::DiagMessage(path_data.source)
+ << "failed to filter product");
+ return false;
+ }
+ }
}
if (options.pseudolocalize && translatable_file) {
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 14a730a..984d890 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -42,6 +42,7 @@
// See comments on aapt::ResourceParserOptions.
bool preserve_visibility_of_styleables = false;
bool verbose = false;
+ std::optional<std::string> product_;
};
/** Parses flags and compiles resources to be used in linking. */
@@ -76,6 +77,10 @@
AddOptionalFlag("--source-path",
"Sets the compiled resource file source file path to the given string.",
&options_.source_path);
+ AddOptionalFlag("--filter-product",
+ "Leave only resources specific to the given product. All "
+ "other resources (including defaults) are removed.",
+ &options_.product_);
}
int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 97404fc..cf4dd79 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -66,6 +66,7 @@
#include "optimize/ResourceDeduper.h"
#include "optimize/VersionCollapser.h"
#include "process/IResourceTableConsumer.h"
+#include "process/ProductFilter.h"
#include "process/SymbolTable.h"
#include "split/TableSplitter.h"
#include "trace/TraceBuffer.h"
@@ -387,7 +388,7 @@
// Build up the rules for degrading newer attributes to older ones.
// NOTE(adamlesinski): These rules are hardcoded right now, but they should be
// generated from the attribute definitions themselves (b/62028956).
- if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingHorizontal)) {
+ if (symm->FindById(R::attr::paddingHorizontal)) {
std::vector<ReplacementAttr> replacements{
{"paddingLeft", R::attr::paddingLeft, Attribute(android::ResTable_map::TYPE_DIMENSION)},
{"paddingRight", R::attr::paddingRight, Attribute(android::ResTable_map::TYPE_DIMENSION)},
@@ -396,7 +397,7 @@
util::make_unique<DegradeToManyRule>(std::move(replacements));
}
- if (const SymbolTable::Symbol* s = symm->FindById(R::attr::paddingVertical)) {
+ if (symm->FindById(R::attr::paddingVertical)) {
std::vector<ReplacementAttr> replacements{
{"paddingTop", R::attr::paddingTop, Attribute(android::ResTable_map::TYPE_DIMENSION)},
{"paddingBottom", R::attr::paddingBottom, Attribute(android::ResTable_map::TYPE_DIMENSION)},
@@ -405,7 +406,7 @@
util::make_unique<DegradeToManyRule>(std::move(replacements));
}
- if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginHorizontal)) {
+ if (symm->FindById(R::attr::layout_marginHorizontal)) {
std::vector<ReplacementAttr> replacements{
{"layout_marginLeft", R::attr::layout_marginLeft,
Attribute(android::ResTable_map::TYPE_DIMENSION)},
@@ -416,7 +417,7 @@
util::make_unique<DegradeToManyRule>(std::move(replacements));
}
- if (const SymbolTable::Symbol* s = symm->FindById(R::attr::layout_marginVertical)) {
+ if (symm->FindById(R::attr::layout_marginVertical)) {
std::vector<ReplacementAttr> replacements{
{"layout_marginTop", R::attr::layout_marginTop,
Attribute(android::ResTable_map::TYPE_DIMENSION)},
@@ -2127,7 +2128,7 @@
<< "can't select products when building static library");
}
} else {
- ProductFilter product_filter(options_.products);
+ ProductFilter product_filter(options_.products, /* remove_default_config_values = */ false);
if (!product_filter.Consume(context_, &final_table_)) {
context_->GetDiagnostics()->Error(android::DiagMessage() << "failed stripping products");
return 1;
@@ -2512,6 +2513,28 @@
}
}
+ // Parse the feature flag values. An argument that starts with '@' points to a file to read flag
+ // values from.
+ std::vector<std::string> all_feature_flags_args;
+ for (const std::string& arg : feature_flags_args_) {
+ if (util::StartsWith(arg, "@")) {
+ const std::string path = arg.substr(1, arg.size() - 1);
+ std::string error;
+ if (!file::AppendArgsFromFile(path, &all_feature_flags_args, &error)) {
+ context.GetDiagnostics()->Error(android::DiagMessage(path) << error);
+ return 1;
+ }
+ } else {
+ all_feature_flags_args.push_back(arg);
+ }
+ }
+
+ for (const std::string& arg : all_feature_flags_args) {
+ if (ParseFeatureFlagsParameter(arg, context.GetDiagnostics(), &options_.feature_flag_values)) {
+ return 1;
+ }
+ }
+
if (context.GetPackageType() != PackageType::kStaticLib && stable_id_file_path_) {
if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path_.value(),
&options_.stable_id_map)) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 1b1e93bd..26713fd 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -17,11 +17,17 @@
#ifndef AAPT2_LINK_H
#define AAPT2_LINK_H
+#include <optional>
#include <regex>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
#include "Command.h"
#include "Resource.h"
#include "androidfw/IDiagnostics.h"
+#include "cmd/Util.h"
#include "format/binary/TableFlattener.h"
#include "format/proto/ProtoSerialize.h"
#include "link/ManifestFixer.h"
@@ -72,6 +78,7 @@
bool use_sparse_encoding = false;
std::unordered_set<std::string> extensions_to_not_compress;
std::optional<std::regex> regex_to_not_compress;
+ FeatureFlagValues feature_flag_values;
// Static lib options.
bool no_static_lib_packages = false;
@@ -323,6 +330,7 @@
"should only be used together with the --static-lib flag.",
&options_.merge_only);
AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
+ AddOptionalFlagList("--feature-flags", "Placeholder, to be implemented.", &feature_flags_args_);
}
int Action(const std::vector<std::string>& args) override;
@@ -347,6 +355,7 @@
std::optional<std::string> stable_id_file_path_;
std::vector<std::string> split_args_;
std::optional<std::string> trace_folder_;
+ std::vector<std::string> feature_flags_args_;
};
}// namespace aapt
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index a92f24b..678d846 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -113,6 +113,56 @@
return std::move(filter);
}
+bool ParseFeatureFlagsParameter(StringPiece arg, android::IDiagnostics* diag,
+ FeatureFlagValues* out_feature_flag_values) {
+ if (arg.empty()) {
+ return true;
+ }
+
+ for (StringPiece flag_and_value : util::Tokenize(arg, ',')) {
+ std::vector<std::string> parts = util::Split(flag_and_value, '=');
+ if (parts.empty()) {
+ continue;
+ }
+
+ if (parts.size() > 2) {
+ diag->Error(android::DiagMessage()
+ << "Invalid feature flag and optional value '" << flag_and_value
+ << "'. Must be in the format 'flag_name[=true|false]");
+ return false;
+ }
+
+ StringPiece flag_name = util::TrimWhitespace(parts[0]);
+ if (flag_name.empty()) {
+ diag->Error(android::DiagMessage() << "No name given for one or more flags in: " << arg);
+ return false;
+ }
+
+ std::optional<bool> flag_value = {};
+ if (parts.size() == 2) {
+ StringPiece str_flag_value = util::TrimWhitespace(parts[1]);
+ if (!str_flag_value.empty()) {
+ flag_value = ResourceUtils::ParseBool(parts[1]);
+ if (!flag_value.has_value()) {
+ diag->Error(android::DiagMessage() << "Invalid value for feature flag '" << flag_and_value
+ << "'. Value must be 'true' or 'false'");
+ return false;
+ }
+ }
+ }
+
+ if (auto [it, inserted] =
+ out_feature_flag_values->try_emplace(std::string(flag_name), flag_value);
+ !inserted) {
+ // We are allowing the same flag to appear multiple times, last value wins.
+ diag->Note(android::DiagMessage()
+ << "Value for feature flag '" << flag_name << "' was given more than once");
+ it->second = flag_value;
+ }
+ }
+ return true;
+}
+
// Adjust the SplitConstraints so that their SDK version is stripped if it
// is less than or equal to the minSdk. Otherwise the resources that have had
// their SDK version stripped due to minSdk won't ever match.
diff --git a/tools/aapt2/cmd/Util.h b/tools/aapt2/cmd/Util.h
index 712c07b..9ece5dd 100644
--- a/tools/aapt2/cmd/Util.h
+++ b/tools/aapt2/cmd/Util.h
@@ -17,8 +17,13 @@
#ifndef AAPT_SPLIT_UTIL_H
#define AAPT_SPLIT_UTIL_H
+#include <functional>
+#include <map>
+#include <memory>
+#include <optional>
#include <regex>
#include <set>
+#include <string>
#include <unordered_set>
#include "AppInfo.h"
@@ -32,6 +37,8 @@
namespace aapt {
+using FeatureFlagValues = std::map<std::string, std::optional<bool>, std::less<>>;
+
// Parses a configuration density (ex. hdpi, xxhdpi, 234dpi, anydpi, etc).
// Returns Nothing and logs a human friendly error message if the string was not legal.
std::optional<uint16_t> ParseTargetDensityParameter(android::StringPiece arg,
@@ -48,6 +55,13 @@
std::unique_ptr<IConfigFilter> ParseConfigFilterParameters(const std::vector<std::string>& args,
android::IDiagnostics* diag);
+// Parses a feature flags parameter, which can contain one or more pairs of flag names and optional
+// values, and fills in `out_feature_flag_values` with the parsed values. The pairs in the argument
+// are separated by ',' and the name is separated from the value by '=' if there is a value given.
+// Example arg: "flag1=true,flag2=false,flag3=,flag4" where flag3 and flag4 have no given value.
+bool ParseFeatureFlagsParameter(android::StringPiece arg, android::IDiagnostics* diag,
+ FeatureFlagValues* out_feature_flag_values);
+
// Adjust the SplitConstraints so that their SDK version is stripped if it
// is less than or equal to the min_sdk. Otherwise the resources that have had
// their SDK version stripped due to min_sdk won't ever match.
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 139bfbc..723d87e 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -25,6 +25,7 @@
#include "util/Files.h"
using ::android::ConfigDescription;
+using testing::Pair;
using testing::UnorderedElementsAre;
namespace aapt {
@@ -354,6 +355,51 @@
EXPECT_CONFIG_EQ(constraints, expected_configuration);
}
+TEST(UtilTest, ParseFeatureFlagsParameter_Empty) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_TRUE(ParseFeatureFlagsParameter("", diagnostics, &feature_flag_values));
+ EXPECT_TRUE(feature_flag_values.empty());
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_TooManyParts) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_FALSE(ParseFeatureFlagsParameter("foo=bar=baz", diagnostics, &feature_flag_values));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_NoNameGiven) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_FALSE(ParseFeatureFlagsParameter("foo=true,=false", diagnostics, &feature_flag_values));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_InvalidValue) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_FALSE(ParseFeatureFlagsParameter("foo=true,bar=42", diagnostics, &feature_flag_values));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_DuplicateFlag) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_TRUE(
+ ParseFeatureFlagsParameter("foo=true,bar=true,foo=false", diagnostics, &feature_flag_values));
+ EXPECT_THAT(feature_flag_values, UnorderedElementsAre(Pair("foo", std::optional<bool>(false)),
+ Pair("bar", std::optional<bool>(true))));
+}
+
+TEST(UtilTest, ParseFeatureFlagsParameter_Valid) {
+ auto diagnostics = test::ContextBuilder().Build()->GetDiagnostics();
+ FeatureFlagValues feature_flag_values;
+ ASSERT_TRUE(ParseFeatureFlagsParameter("foo= true, bar =FALSE,baz=, quux", diagnostics,
+ &feature_flag_values));
+ EXPECT_THAT(feature_flag_values,
+ UnorderedElementsAre(Pair("foo", std::optional<bool>(true)),
+ Pair("bar", std::optional<bool>(false)),
+ Pair("baz", std::nullopt), Pair("quux", std::nullopt)));
+}
+
TEST (UtilTest, AdjustSplitConstraintsForMinSdk) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
diff --git a/tools/aapt2/link/FeatureFlagsFilter.cpp b/tools/aapt2/link/FeatureFlagsFilter.cpp
new file mode 100644
index 0000000..fdf3f74
--- /dev/null
+++ b/tools/aapt2/link/FeatureFlagsFilter.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "link/FeatureFlagsFilter.h"
+
+#include <string_view>
+
+#include "androidfw/IDiagnostics.h"
+#include "androidfw/Source.h"
+#include "util/Util.h"
+#include "xml/XmlDom.h"
+#include "xml/XmlUtil.h"
+
+using ::aapt::xml::Element;
+using ::aapt::xml::Node;
+using ::aapt::xml::NodeCast;
+
+namespace aapt {
+
+class FlagsVisitor : public xml::Visitor {
+ public:
+ explicit FlagsVisitor(android::IDiagnostics* diagnostics,
+ const FeatureFlagValues& feature_flag_values,
+ const FeatureFlagsFilterOptions& options)
+ : diagnostics_(diagnostics), feature_flag_values_(feature_flag_values), options_(options) {
+ }
+
+ void Visit(xml::Element* node) override {
+ std::erase_if(node->children,
+ [this](std::unique_ptr<xml::Node>& node) { return ShouldRemove(node); });
+ VisitChildren(node);
+ }
+
+ bool HasError() const {
+ return has_error_;
+ }
+
+ private:
+ bool ShouldRemove(std::unique_ptr<xml::Node>& node) {
+ if (const auto* el = NodeCast<Element>(node.get())) {
+ auto* attr = el->FindAttribute(xml::kSchemaAndroid, "featureFlag");
+ if (attr == nullptr) {
+ return false;
+ }
+
+ bool negated = false;
+ std::string_view flag_name = util::TrimWhitespace(attr->value);
+ if (flag_name.starts_with('!')) {
+ negated = true;
+ flag_name = flag_name.substr(1);
+ }
+
+ if (auto it = feature_flag_values_.find(std::string(flag_name));
+ it != feature_flag_values_.end()) {
+ if (it->second.has_value()) {
+ if (options_.remove_disabled_elements) {
+ // Remove if flag==true && attr=="!flag" (negated) OR flag==false && attr=="flag"
+ return *it->second == negated;
+ }
+ } else if (options_.flags_must_have_value) {
+ diagnostics_->Error(android::DiagMessage(node->line_number)
+ << "attribute 'android:featureFlag' has flag '" << flag_name
+ << "' without a true/false value from --feature_flags parameter");
+ has_error_ = true;
+ return false;
+ }
+ } else if (options_.fail_on_unrecognized_flags) {
+ diagnostics_->Error(android::DiagMessage(node->line_number)
+ << "attribute 'android:featureFlag' has flag '" << flag_name
+ << "' not found in flags from --feature_flags parameter");
+ has_error_ = true;
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ android::IDiagnostics* diagnostics_;
+ const FeatureFlagValues& feature_flag_values_;
+ const FeatureFlagsFilterOptions& options_;
+ bool has_error_ = false;
+};
+
+bool FeatureFlagsFilter::Consume(IAaptContext* context, xml::XmlResource* doc) {
+ FlagsVisitor visitor(context->GetDiagnostics(), feature_flag_values_, options_);
+ doc->root->Accept(&visitor);
+ return !visitor.HasError();
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/FeatureFlagsFilter.h b/tools/aapt2/link/FeatureFlagsFilter.h
new file mode 100644
index 0000000..1d342a7
--- /dev/null
+++ b/tools/aapt2/link/FeatureFlagsFilter.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include "android-base/macros.h"
+#include "cmd/Util.h"
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+struct FeatureFlagsFilterOptions {
+ // If true, elements whose featureFlag values are false (i.e., disabled feature) will be removed.
+ bool remove_disabled_elements = true;
+
+ // If true, `Consume()` will return false (error) if a flag was found that is not in
+ // `feature_flag_values`.
+ bool fail_on_unrecognized_flags = true;
+
+ // If true, `Consume()` will return false (error) if a flag was found whose value in
+ // `feature_flag_values` is not defined (std::nullopt).
+ bool flags_must_have_value = true;
+};
+
+// Looks for the `android:featureFlag` attribute in each XML element, validates the flag names and
+// values, and removes elements according to the values in `feature_flag_values`. An element will be
+// removed if the flag's given value is FALSE. A "!" before the flag name in the attribute indicates
+// a boolean NOT operation, i.e., an element will be removed if the flag's given value is TRUE. For
+// example, if the XML is the following:
+//
+// <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+// <permission android:name="FOO" android:featureFlag="!flag"
+// android:protectionLevel="normal" />
+// <permission android:name="FOO" android:featureFlag="flag"
+// android:protectionLevel="dangerous" />
+// </manifest>
+//
+// If `feature_flag_values` contains {"flag", true}, then the <permission> element with
+// protectionLevel="normal" will be removed, and the <permission> element with
+// protectionLevel="normal" will be kept.
+//
+// The `Consume()` function will return false if there is an invalid flag found (see
+// FeatureFlagsFilterOptions for customizing the filter's validation behavior). Do not use the XML
+// further if there are errors as there may be elements removed already.
+class FeatureFlagsFilter : public IXmlResourceConsumer {
+ public:
+ explicit FeatureFlagsFilter(FeatureFlagValues feature_flag_values,
+ FeatureFlagsFilterOptions options)
+ : feature_flag_values_(std::move(feature_flag_values)), options_(options) {
+ }
+
+ bool Consume(IAaptContext* context, xml::XmlResource* doc) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FeatureFlagsFilter);
+
+ const FeatureFlagValues feature_flag_values_;
+ const FeatureFlagsFilterOptions options_;
+};
+
+} // namespace aapt
diff --git a/tools/aapt2/link/FeatureFlagsFilter_test.cpp b/tools/aapt2/link/FeatureFlagsFilter_test.cpp
new file mode 100644
index 0000000..53086cc
--- /dev/null
+++ b/tools/aapt2/link/FeatureFlagsFilter_test.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "link/FeatureFlagsFilter.h"
+
+#include <string_view>
+
+#include "test/Test.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace aapt {
+
+// Returns null if there was an error from FeatureFlagsFilter.
+std::unique_ptr<xml::XmlResource> VerifyWithOptions(std::string_view str,
+ const FeatureFlagValues& feature_flag_values,
+ const FeatureFlagsFilterOptions& options) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(str);
+ FeatureFlagsFilter filter(feature_flag_values, options);
+ if (filter.Consume(test::ContextBuilder().Build().get(), doc.get())) {
+ return doc;
+ }
+ return {};
+}
+
+// Returns null if there was an error from FeatureFlagsFilter.
+std::unique_ptr<xml::XmlResource> Verify(std::string_view str,
+ const FeatureFlagValues& feature_flag_values) {
+ return VerifyWithOptions(str, feature_flag_values, {});
+}
+
+TEST(FeatureFlagsFilterTest, NoFeatureFlagAttributes) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" />
+ </manifest>)EOF",
+ {{"flag", false}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+TEST(FeatureFlagsFilterTest, RemoveElementWithDisabledFlag) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="flag" />
+ </manifest>)EOF",
+ {{"flag", false}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, RemoveElementWithNegatedEnabledFlag) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="!flag" />
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, KeepElementWithEnabledFlag) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="flag" />
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+
+TEST(FeatureFlagsFilterTest, SideBySideEnabledAndDisabled) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="!flag"
+ android:protectionLevel="normal" />
+ <permission android:name="FOO" android:featureFlag="flag"
+ android:protectionLevel="dangerous" />
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto children = root->GetChildElements();
+ ASSERT_EQ(children.size(), 1);
+ auto attr = children[0]->FindAttribute(xml::kSchemaAndroid, "protectionLevel");
+ ASSERT_THAT(attr, NotNull());
+ ASSERT_EQ(attr->value, "dangerous");
+}
+
+TEST(FeatureFlagsFilterTest, RemoveDeeplyNestedElement) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <application>
+ <provider />
+ <activity>
+ <layout android:featureFlag="!flag" />
+ </activity>
+ </application>
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto application = root->FindChild({}, "application");
+ ASSERT_THAT(application, NotNull());
+ auto activity = application->FindChild({}, "activity");
+ ASSERT_THAT(activity, NotNull());
+ auto maybe_removed = activity->FindChild({}, "layout");
+ ASSERT_THAT(maybe_removed, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, KeepDeeplyNestedElement) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <application>
+ <provider />
+ <activity>
+ <layout android:featureFlag="flag" />
+ </activity>
+ </application>
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto application = root->FindChild({}, "application");
+ ASSERT_THAT(application, NotNull());
+ auto activity = application->FindChild({}, "activity");
+ ASSERT_THAT(activity, NotNull());
+ auto maybe_removed = activity->FindChild({}, "layout");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+
+TEST(FeatureFlagsFilterTest, FailOnEmptyFeatureFlagAttribute) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag=" " />
+ </manifest>)EOF",
+ {{"flag", false}});
+ ASSERT_THAT(doc, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, FailOnFlagWithNoGivenValue) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="flag" />
+ </manifest>)EOF",
+ {{"flag", std::nullopt}});
+ ASSERT_THAT(doc, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, FailOnUnrecognizedFlag) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="unrecognized" />
+ </manifest>)EOF",
+ {{"flag", true}});
+ ASSERT_THAT(doc, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, FailOnMultipleValidationErrors) {
+ auto doc = Verify(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="bar" />
+ <permission android:name="FOO" android:featureFlag="unrecognized" />
+ </manifest>)EOF",
+ {{"flag", std::nullopt}});
+ ASSERT_THAT(doc, IsNull());
+}
+
+TEST(FeatureFlagsFilterTest, OptionRemoveDisabledElementsIsFalse) {
+ auto doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="flag" />
+ </manifest>)EOF",
+ {{"flag", false}}, {.remove_disabled_elements = false});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+
+TEST(FeatureFlagsFilterTest, OptionFlagsMustHaveValueIsFalse) {
+ auto doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="flag" />
+ </manifest>)EOF",
+ {{"flag", std::nullopt}}, {.flags_must_have_value = false});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+
+TEST(FeatureFlagsFilterTest, OptionFailOnUnrecognizedFlagsIsFalse) {
+ auto doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android">
+ <permission android:name="FOO" android:featureFlag="unrecognized" />
+ </manifest>)EOF",
+ {{"flag", true}}, {.fail_on_unrecognized_flags = false});
+ ASSERT_THAT(doc, NotNull());
+ auto root = doc->root.get();
+ ASSERT_THAT(root, NotNull());
+ auto maybe_removed = root->FindChild({}, "permission");
+ ASSERT_THAT(maybe_removed, NotNull());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 44cd276..18165f7 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -20,12 +20,12 @@
#include <set>
#include <unordered_set>
-#include "android-base/macros.h"
-#include "androidfw/ConfigDescription.h"
-#include "androidfw/StringPiece.h"
-
#include "Resource.h"
#include "SdkConstants.h"
+#include "android-base/macros.h"
+#include "android-base/result.h"
+#include "androidfw/ConfigDescription.h"
+#include "androidfw/StringPiece.h"
#include "process/IResourceTableConsumer.h"
#include "xml/XmlDom.h"
@@ -92,28 +92,6 @@
DISALLOW_COPY_AND_ASSIGN(PrivateAttributeMover);
};
-class ResourceConfigValue;
-
-class ProductFilter : public IResourceTableConsumer {
- public:
- using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
-
- explicit ProductFilter(std::unordered_set<std::string> products) : products_(products) {
- }
-
- ResourceConfigValueIter SelectProductToKeep(const ResourceNameRef& name,
- const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end,
- android::IDiagnostics* diag);
-
- bool Consume(IAaptContext* context, ResourceTable* table) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ProductFilter);
-
- std::unordered_set<std::string> products_;
-};
-
// Removes namespace nodes and URI information from the XmlResource.
//
// Once an XmlResource is processed by this consumer, it is no longer able to have its attributes
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index c4f6e70..0b16e2c 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -338,7 +338,7 @@
}
bool has_gl_es_version = false;
- if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "glEsVersion")) {
+ if (el->FindAttribute(xml::kSchemaAndroid, "glEsVersion")) {
if (has_name) {
diag->Error(android::DiagMessage(el->line_number)
<< "cannot define both android:name and android:glEsVersion in <uses-feature>");
diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/link/ProductFilter_test.cpp
deleted file mode 100644
index 2cb9afa..0000000
--- a/tools/aapt2/link/ProductFilter_test.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "link/Linkers.h"
-
-#include "test/Test.h"
-
-using ::android::ConfigDescription;
-
-namespace aapt {
-
-TEST(ProductFilterTest, SelectTwoProducts) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- const ConfigDescription land = test::ParseConfigOrDie("land");
- const ConfigDescription port = test::ParseConfigOrDie("port");
-
- ResourceTable table;
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/default.xml")).Build(),
- land)
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/tablet.xml")).Build(),
- land, "tablet")
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/default.xml")).Build(),
- port)
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/tablet.xml")).Build(),
- port, "tablet")
- .Build(),
- context->GetDiagnostics()));
-
- ProductFilter filter({"tablet"});
- ASSERT_TRUE(filter.Consume(context.get(), &table));
-
- EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", land, ""));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", land, "tablet"));
- EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", port, ""));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", port, "tablet"));
-}
-
-TEST(ProductFilterTest, SelectDefaultProduct) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- ResourceTable table;
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("default.xml")).Build())
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("tablet.xml")).Build(), {},
- "tablet")
- .Build(),
- context->GetDiagnostics()));
- ;
-
- ProductFilter filter(std::unordered_set<std::string>{});
- ASSERT_TRUE(filter.Consume(context.get(), &table));
-
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one",
- ConfigDescription::DefaultConfig(), ""));
- EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one",
- ConfigDescription::DefaultConfig(), "tablet"));
-}
-
-TEST(ProductFilterTest, FailOnAmbiguousProduct) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- ResourceTable table;
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("default.xml")).Build())
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("tablet.xml")).Build(), {},
- "tablet")
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("no-sdcard.xml")).Build(),
- {}, "no-sdcard")
- .Build(),
- context->GetDiagnostics()));
-
- ProductFilter filter({"tablet", "no-sdcard"});
- ASSERT_FALSE(filter.Consume(context.get(), &table));
-}
-
-TEST(ProductFilterTest, FailOnMultipleDefaults) {
- std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-
- ResourceTable table;
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source(".xml")).Build())
- .Build(),
- context->GetDiagnostics()));
-
- ASSERT_TRUE(table.AddResource(
- NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
- .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("default.xml")).Build(), {},
- "default")
- .Build(),
- context->GetDiagnostics()));
-
- ProductFilter filter(std::unordered_set<std::string>{});
- ASSERT_FALSE(filter.Consume(context.get(), &table));
-}
-
-} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter.cpp b/tools/aapt2/process/ProductFilter.cpp
similarity index 60%
rename from tools/aapt2/link/ProductFilter.cpp
rename to tools/aapt2/process/ProductFilter.cpp
index 9544986..0b1c0a6 100644
--- a/tools/aapt2/link/ProductFilter.cpp
+++ b/tools/aapt2/process/ProductFilter.cpp
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-#include "link/Linkers.h"
+#include "process/ProductFilter.h"
+
+#include <algorithm>
#include "ResourceTable.h"
#include "trace/TraceBuffer.h"
namespace aapt {
-ProductFilter::ResourceConfigValueIter ProductFilter::SelectProductToKeep(
- const ResourceNameRef& name, const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end, android::IDiagnostics* diag) {
+std::optional<ProductFilter::ResourceConfigValueIter> ProductFilter::SelectProductToKeep(
+ const ResourceNameRef& name, ResourceConfigValueIter begin, ResourceConfigValueIter end,
+ android::IDiagnostics* diag) {
ResourceConfigValueIter default_product_iter = end;
ResourceConfigValueIter selected_product_iter = end;
@@ -36,12 +38,11 @@
<< "selection of product '" << config_value->product << "' for resource "
<< name << " is ambiguous");
- ResourceConfigValue* previously_selected_config_value =
- selected_product_iter->get();
+ ResourceConfigValue* previously_selected_config_value = selected_product_iter->get();
diag->Note(android::DiagMessage(previously_selected_config_value->value->GetSource())
<< "product '" << previously_selected_config_value->product
<< "' is also a candidate");
- return end;
+ return std::nullopt;
}
// Select this product.
@@ -54,11 +55,10 @@
diag->Error(android::DiagMessage(config_value->value->GetSource())
<< "multiple default products defined for resource " << name);
- ResourceConfigValue* previously_default_config_value =
- default_product_iter->get();
+ ResourceConfigValue* previously_default_config_value = default_product_iter->get();
diag->Note(android::DiagMessage(previously_default_config_value->value->GetSource())
<< "default product also defined here");
- return end;
+ return std::nullopt;
}
// Mark the default.
@@ -66,9 +66,16 @@
}
}
+ if (remove_default_config_values_) {
+ // If we are leaving only a specific product, return early here instead of selecting the default
+ // value. Returning end here will cause this value set to be skipped, and will be removed with
+ // ClearEmptyValues method.
+ return selected_product_iter;
+ }
+
if (default_product_iter == end) {
diag->Error(android::DiagMessage() << "no default product defined for resource " << name);
- return end;
+ return std::nullopt;
}
if (selected_product_iter == end) {
@@ -89,20 +96,27 @@
ResourceConfigValueIter start_range_iter = iter;
while (iter != entry->values.end()) {
++iter;
- if (iter == entry->values.end() ||
- (*iter)->config != (*start_range_iter)->config) {
+ if (iter == entry->values.end() || (*iter)->config != (*start_range_iter)->config) {
// End of the array, or we saw a different config,
// so this must be the end of a range of products.
// Select the product to keep from the set of products defined.
ResourceNameRef name(pkg->name, type->named_type, entry->name);
- auto value_to_keep = SelectProductToKeep(
- name, start_range_iter, iter, context->GetDiagnostics());
- if (value_to_keep == iter) {
+ auto value_to_keep =
+ SelectProductToKeep(name, start_range_iter, iter, context->GetDiagnostics());
+ if (!value_to_keep.has_value()) {
// An error occurred, we could not pick a product.
error = true;
- } else {
+ } else if (auto val = value_to_keep.value(); val != iter) {
// We selected a product to keep. Move it to the new array.
- new_values.push_back(std::move(*value_to_keep));
+ if (remove_default_config_values_) {
+ // We are filtering values with the given product. The selected value here will be
+ // a new default value, and all other values will be removed.
+ new_values.push_back(
+ std::make_unique<ResourceConfigValue>((*val)->config, android::StringPiece{}));
+ new_values.back()->value = std::move((*val)->value);
+ } else {
+ new_values.push_back(std::move(*val));
+ }
}
// Start the next range of products.
@@ -115,7 +129,27 @@
}
}
}
+
+ if (remove_default_config_values_) {
+ ClearEmptyValues(table);
+ }
+
return !error;
}
+void ProductFilter::ClearEmptyValues(ResourceTable* table) {
+ // Clear any empty packages/types/entries, as remove_default_config_values_ may remove an entire
+ // value set.
+ CHECK(remove_default_config_values_)
+ << __func__ << " should only be called when remove_default_config_values_ is set";
+
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ std::erase_if(type->entries, [](auto& entry) { return entry->values.empty(); });
+ }
+ std::erase_if(pkg->types, [](auto& type) { return type->entries.empty(); });
+ }
+ std::erase_if(table->packages, [](auto& package) { return package->types.empty(); });
+}
+
} // namespace aapt
diff --git a/tools/aapt2/process/ProductFilter.h b/tools/aapt2/process/ProductFilter.h
new file mode 100644
index 0000000..0ec2f00
--- /dev/null
+++ b/tools/aapt2/process/ProductFilter.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "Resource.h"
+#include "android-base/macros.h"
+#include "androidfw/ConfigDescription.h"
+#include "androidfw/IDiagnostics.h"
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+class ResourceConfigValue;
+
+class ProductFilter : public IResourceTableConsumer {
+ public:
+ using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
+
+ // Setting remove_default_config_values will remove all values other than
+ // specified product, including default. For example, if the following table
+ //
+ // <string name="foo" product="default">foo_default</string>
+ // <string name="foo" product="tablet">foo_tablet</string>
+ // <string name="bar">bar</string>
+ //
+ // is consumed with tablet, it will result in
+ //
+ // <string name="foo">foo_tablet</string>
+ //
+ // removing foo_default and bar. This option is to generate an RRO package
+ // with given product.
+ explicit ProductFilter(std::unordered_set<std::string> products,
+ bool remove_default_config_values)
+ : products_(std::move(products)),
+ remove_default_config_values_(remove_default_config_values) {
+ }
+
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProductFilter);
+
+ // SelectProductToKeep returns an iterator for the selected value.
+ //
+ // Returns std::nullopt in case of failure (e.g. ambiguous values, missing or duplicated default
+ // values).
+ // Returns `end` if keep_as_default_product is set and no value for the specified product was
+ // found.
+ std::optional<ResourceConfigValueIter> SelectProductToKeep(const ResourceNameRef& name,
+ ResourceConfigValueIter begin,
+ ResourceConfigValueIter end,
+ android::IDiagnostics* diag);
+
+ void ClearEmptyValues(ResourceTable* table);
+
+ std::unordered_set<std::string> products_;
+ bool remove_default_config_values_;
+};
+
+} // namespace aapt
diff --git a/tools/aapt2/process/ProductFilter_test.cpp b/tools/aapt2/process/ProductFilter_test.cpp
new file mode 100644
index 0000000..27a82dc
--- /dev/null
+++ b/tools/aapt2/process/ProductFilter_test.cpp
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "process/ProductFilter.h"
+
+#include "test/Test.h"
+
+using ::android::ConfigDescription;
+
+namespace aapt {
+
+TEST(ProductFilterTest, SelectTwoProducts) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ const ConfigDescription land = test::ParseConfigOrDie("land");
+ const ConfigDescription port = test::ParseConfigOrDie("port");
+
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/default.xml")).Build(),
+ land)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/tablet.xml")).Build(),
+ land, "tablet")
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/default.xml")).Build(),
+ port)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/tablet.xml")).Build(),
+ port, "tablet")
+ .Build(),
+ context->GetDiagnostics()));
+
+ ProductFilter filter({"tablet"}, /* remove_default_config_values = */ false);
+ ASSERT_TRUE(filter.Consume(context.get(), &table));
+
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", land, ""));
+ EXPECT_NE(nullptr,
+ test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", land, "tablet"));
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", port, ""));
+ EXPECT_NE(nullptr,
+ test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", port, "tablet"));
+}
+
+TEST(ProductFilterTest, SelectDefaultProduct) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("default.xml")).Build())
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("tablet.xml")).Build(), {},
+ "tablet")
+ .Build(),
+ context->GetDiagnostics()));
+ ;
+
+ ProductFilter filter(std::unordered_set<std::string>{},
+ /* remove_default_config_values = */ false);
+ ASSERT_TRUE(filter.Consume(context.get(), &table));
+
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/one",
+ ConfigDescription::DefaultConfig(), ""));
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfigAndProduct<Id>(&table, "android:string/one",
+ ConfigDescription::DefaultConfig(), "tablet"));
+}
+
+TEST(ProductFilterTest, FailOnAmbiguousProduct) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("default.xml")).Build())
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("tablet.xml")).Build(), {},
+ "tablet")
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("no-sdcard.xml")).Build(),
+ {}, "no-sdcard")
+ .Build(),
+ context->GetDiagnostics()));
+
+ ProductFilter filter({"tablet", "no-sdcard"}, /* remove_default_config_values = */ false);
+ ASSERT_FALSE(filter.Consume(context.get(), &table));
+}
+
+TEST(ProductFilterTest, FailOnMultipleDefaults) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source(".xml")).Build())
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("default.xml")).Build(), {},
+ "default")
+ .Build(),
+ context->GetDiagnostics()));
+
+ ProductFilter filter(std::unordered_set<std::string>{},
+ /* remove_default_config_values = */ false);
+ ASSERT_FALSE(filter.Consume(context.get(), &table));
+}
+
+TEST(ProductFilterTest, RemoveDefaultConfigValues) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ const ConfigDescription land = test::ParseConfigOrDie("land");
+ const ConfigDescription port = test::ParseConfigOrDie("port");
+
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/default.xml")).Build(),
+ land)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/tablet.xml")).Build(),
+ land, "tablet")
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/two"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/default.xml")).Build(),
+ land)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/default.xml")).Build(),
+ port)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/tablet.xml")).Build(),
+ port, "tablet")
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/two"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/default.xml")).Build(),
+ port)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ProductFilter filter({"tablet"}, /* remove_default_config_values = */ true);
+ ASSERT_TRUE(filter.Consume(context.get(), &table));
+
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", land, ""));
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/two", land, ""));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", port, ""));
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/two", port, ""));
+}
+
+} // namespace aapt
diff --git a/tools/fonts/update_font_metadata.py b/tools/fonts/update_font_metadata.py
index c07a98a..04a5528 100755
--- a/tools/fonts/update_font_metadata.py
+++ b/tools/fonts/update_font_metadata.py
@@ -19,7 +19,7 @@
args_parser.add_argument('--revision', help='Updated font revision. Use + to update revision based on the current revision')
args = args_parser.parse_args()
- font = ttLib.TTFont(args.input)
+ font = ttLib.TTFont(args.input, recalcTimestamp=False)
update_font_revision(font, args.revision)
font.save(args.output)
diff --git a/tools/lint/README.md b/tools/lint/README.md
index b235ad6..ff8e442 100644
--- a/tools/lint/README.md
+++ b/tools/lint/README.md
@@ -103,10 +103,15 @@
As noted above, this baseline file contains warnings too, which might be undesirable. For example,
CI tools might surface these warnings in code reviews. In order to create this file without
-warnings, we need to pass another flag to lint: `--nowarn`. The easiest way to do this is to
-locally change the soong code in
-[lint.go](http://cs/aosp-master/build/soong/java/lint.go;l=451;rcl=2e778d5bc4a8d1d77b4f4a3029a4a254ad57db75)
-adding `cmd.Flag("--nowarn")` and running lint again.
+warnings, we need to pass another flag to lint: `--nowarn`. One option is to add the flag to your
+Android.bp file and then run lint again:
+
+```
+ lint: {
+ extra_check_modules: ["AndroidFrameworkLintChecker"],
+ flags: ["--nowarn"],
+ }
+```
# Documentation
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt
index e03d92ab..f1727b7 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt
@@ -30,6 +30,7 @@
const val IINTERFACE_INTERFACE = "android.os.IInterface"
const val AIDL_PERMISSION_HELPER_SUFFIX = "_enforcePermission"
+const val PERMISSION_PREFIX_LITERAL = "android.permission."
/**
* If a non java (e.g. c++) backend is enabled, the @EnforcePermission
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
index d41fee3..24d203f 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
@@ -24,33 +24,31 @@
import org.jetbrains.uast.UMethod
/**
- * Given a UMethod, determine if this method is
- * the entrypoint to an interface generated by AIDL,
- * returning the interface name if so, otherwise returning null
+ * Given a UMethod, determine if this method is the entrypoint to an interface
+ * generated by AIDL, returning the interface name if so, otherwise returning
+ * null
*/
fun getContainingAidlInterface(context: JavaContext, node: UMethod): String? {
- if (!isContainedInSubclassOfStub(context, node)) return null
- for (superMethod in node.findSuperMethods()) {
- for (extendsInterface in superMethod.containingClass?.extendsList?.referenceElements
- ?: continue) {
- if (extendsInterface.qualifiedName == IINTERFACE_INTERFACE) {
- return superMethod.containingClass?.name
- }
- }
+ val containingStub = containingStub(context, node) ?: return null
+ val superMethod = node.findSuperMethods(containingStub)
+ if (superMethod.isEmpty()) return null
+ return containingStub.containingClass?.name
+}
+
+/* Returns the containing Stub class if any. This is not sufficient to infer
+ * that the method itself extends an AIDL generated method. See
+ * getContainingAidlInterface for that purpose.
+ */
+fun containingStub(context: JavaContext, node: UMethod?): PsiClass? {
+ var superClass = node?.containingClass?.superClass
+ while (superClass != null) {
+ if (isStub(context, superClass)) return superClass
+ superClass = superClass.superClass
}
return null
}
-fun isContainedInSubclassOfStub(context: JavaContext, node: UMethod?): Boolean {
- var superClass = node?.containingClass?.superClass
- while (superClass != null) {
- if (isStub(context, superClass)) return true
- superClass = superClass.superClass
- }
- return false
-}
-
-fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean {
+private fun isStub(context: JavaContext, psiClass: PsiClass?): Boolean {
if (psiClass == null) return false
if (psiClass.name != "Stub") return false
if (!context.evaluator.isStatic(psiClass)) return false
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 935bade..624a198 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -20,6 +20,7 @@
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
import com.google.android.lint.parcel.SaferParcelChecker
+import com.google.android.lint.aidl.PermissionAnnotationDetector
import com.google.auto.service.AutoService
@AutoService(IssueRegistry::class)
@@ -37,6 +38,7 @@
SaferParcelChecker.ISSUE_UNSAFE_API_USAGE,
// TODO: Currently crashes due to OOM issue
// PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS,
+ PermissionAnnotationDetector.ISSUE_MISSING_PERMISSION_ANNOTATION,
PermissionMethodDetector.ISSUE_PERMISSION_METHOD_USAGE,
PermissionMethodDetector.ISSUE_CAN_BE_PERMISSION_METHOD,
)
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt
new file mode 100644
index 0000000..6b50cfd
--- /dev/null
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint.aidl
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UMethod
+
+/**
+ * Ensures all AIDL-generated methods are annotated.
+ *
+ * This detector is run on system_server to validate that any method that may
+ * be exposed via an AIDL interface is permission-annotated. That is, it must
+ * have one of the following annotation:
+ * - @EnforcePermission
+ * - @RequiresNoPermission
+ * - @PermissionManuallyEnforced
+ */
+class PermissionAnnotationDetector : AidlImplementationDetector() {
+
+ override fun visitAidlMethod(
+ context: JavaContext,
+ node: UMethod,
+ interfaceName: String,
+ body: UBlockExpression
+ ) {
+ if (context.evaluator.isAbstract(node)) return
+
+ if (AIDL_PERMISSION_ANNOTATIONS.any { node.hasAnnotation(it) }) return
+
+ context.report(
+ ISSUE_MISSING_PERMISSION_ANNOTATION,
+ node,
+ context.getLocation(node),
+ "The method ${node.name} is not permission-annotated."
+ )
+ }
+
+ companion object {
+
+ private val EXPLANATION_MISSING_PERMISSION_ANNOTATION = """
+ Interfaces that are exposed by system_server are required to have an annotation which
+ denotes the type of permission enforced. There are 3 possible options:
+ - @EnforcePermission
+ - @RequiresNoPermission
+ - @PermissionManuallyEnforced
+ See the documentation of each annotation for further details.
+
+ The annotation on the Java implementation must be the same that the AIDL interface
+ definition. This is verified by a lint in the build system.
+ """.trimIndent()
+
+ @JvmField
+ val ISSUE_MISSING_PERMISSION_ANNOTATION = Issue.create(
+ id = "MissingPermissionAnnotation",
+ briefDescription = "No permission annotation on exposed AIDL interface.",
+ explanation = EXPLANATION_MISSING_PERMISSION_ANNOTATION,
+ category = Category.CORRECTNESS,
+ priority = 5,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ PermissionAnnotationDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ ),
+ enabledByDefault = false
+ )
+ }
+}
diff --git a/tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt
new file mode 100644
index 0000000..bce848a
--- /dev/null
+++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint.aidl
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class PermissionAnnotationDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = PermissionAnnotationDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ PermissionAnnotationDetector.ISSUE_MISSING_PERMISSION_ANNOTATION,
+ )
+
+ override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+ /** No issue scenario */
+
+ fun testDoesNotDetectIssuesInCorrectScenario() {
+ lint().files(
+ java(
+ """
+ public class Foo extends IFoo.Stub {
+ @Override
+ @android.annotation.EnforcePermission("android.Manifest.permission.READ_CONTACTS")
+ public void testMethod() { }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testMissingAnnotation() {
+ lint().files(
+ java(
+ """
+ public class Bar extends IBar.Stub {
+ public void testMethod() { }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Bar.java:2: Error: The method testMethod is not permission-annotated. [MissingPermissionAnnotation]
+ public void testMethod() { }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ )
+ }
+
+ fun testNoIssueWhenExtendingWithAnotherSubclass() {
+ lint().files(
+ java(
+ """
+ public class Foo extends IFoo.Stub {
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod() { }
+ // not an AIDL method, just another method
+ public void someRandomMethod() { }
+ }
+ """).indented(),
+ java(
+ """
+ public class Baz extends Bar {
+ @Override
+ public void someRandomMethod() { }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ /* Stubs */
+
+ // A service with permission annotation on the method.
+ private val interfaceIFoo: TestFile = java(
+ """
+ public interface IFoo extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IFoo {
+ }
+ @Override
+ @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethod();
+ @Override
+ @android.annotation.RequiresNoPermission
+ public void testMethodNoPermission();
+ @Override
+ @android.annotation.PermissionManuallyEnforced
+ public void testMethodManual();
+ }
+ """
+ ).indented()
+
+ // A service with no permission annotation.
+ private val interfaceIBar: TestFile = java(
+ """
+ public interface IBar extends android.os.IInterface {
+ public static abstract class Stub extends android.os.Binder implements IBar {
+ }
+ public void testMethod();
+ }
+ """
+ ).indented()
+
+ private val stubs = arrayOf(interfaceIFoo, interfaceIBar)
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
index a20266a..28eab8f 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
@@ -20,7 +20,6 @@
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
import com.google.android.lint.aidl.EnforcePermissionDetector
-import com.google.android.lint.aidl.EnforcePermissionHelperDetector
import com.google.android.lint.aidl.SimpleManualPermissionEnforcementDetector
import com.google.auto.service.AutoService
@@ -30,7 +29,8 @@
override val issues = listOf(
EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
- EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION,
SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
)
@@ -45,4 +45,4 @@
feedbackUrl = "http://b/issues/new?component=315013",
contact = "repsonsible-apis@google.com"
)
-}
\ No newline at end of file
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index 0baac2c..4455a9c 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -30,29 +30,34 @@
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.google.android.lint.findCallExpression
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiArrayInitializerMemberValue
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
-import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UDeclarationsExpression
import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UMethod
-import org.jetbrains.uast.toUElement
+import org.jetbrains.uast.skipParenthesizedExprDown
+
+import java.util.EnumSet
/**
- * Lint Detector that ensures that any method overriding a method annotated
- * with @EnforcePermission is also annotated with the exact same annotation.
- * The intent is to surface the effective permission checks to the service
- * implementations.
+ * Lint Detector that ensures consistency when using the @EnforcePermission
+ * annotation. Multiple verifications are implemented:
*
- * This is done with 2 mechanisms:
* 1. Visit any annotation usage, to ensure that any derived class will have
- * the correct annotation on each methods. This is for the top to bottom
- * propagation.
- * 2. Visit any annotation, to ensure that if a method is annotated, it has
+ * the correct annotation on each methods. Even if the subclass does not
+ * have the annotation, visitAnnotationUsage will be called which allows us
+ * to capture the issue.
+ * 2. Visit any method, to ensure that if a method is annotated, it has
* its ancestor also annotated. This is to avoid having an annotation on a
* Java method without the corresponding annotation on the AIDL interface.
+ * 3. When annotated, ensures that the first instruction is to call the helper
+ * method (or the parent helper).
*/
class EnforcePermissionDetector : Detector(), SourceCodeScanner {
@@ -60,9 +65,8 @@
return listOf(ANNOTATION_ENFORCE_PERMISSION)
}
- override fun getApplicableUastTypes(): List<Class<out UElement>> {
- return listOf(UAnnotation::class.java)
- }
+ override fun getApplicableUastTypes(): List<Class<out UElement?>> =
+ listOf(UMethod::class.java)
private fun annotationValueGetChildren(elem: PsiElement): Array<PsiElement> {
if (elem is PsiArrayInitializerMemberValue)
@@ -93,7 +97,7 @@
val v1 = ConstantEvaluator.evaluate(context, value1)
val v2 = ConstantEvaluator.evaluate(context, value2)
if (v1 != null && v2 != null) {
- if (v1 != v2) {
+ if (v1 != v2 && !isOneShortPermissionOfOther(v1, v2)) {
return false
}
} else {
@@ -105,7 +109,7 @@
for (j in children1.indices) {
val c1 = ConstantEvaluator.evaluate(context, children1[j])
val c2 = ConstantEvaluator.evaluate(context, children2[j])
- if (c1 != c2) {
+ if (c1 != c2 && !isOneShortPermissionOfOther(c1, c2)) {
return false
}
}
@@ -114,6 +118,12 @@
return true
}
+ private fun isOneShortPermissionOfOther(
+ permission1: Any?,
+ permission2: Any?
+ ): Boolean = permission1 == (permission2 as? String)?.removePrefix(PERMISSION_PREFIX_LITERAL) ||
+ permission2 == (permission1 as? String)?.removePrefix(PERMISSION_PREFIX_LITERAL)
+
private fun compareMethods(
context: JavaContext,
element: UElement,
@@ -121,11 +131,6 @@
overriddenMethod: PsiMethod,
checkEquivalence: Boolean = true
) {
- // If method is not from a Stub subclass, this method shouldn't use @EP at all.
- // This is handled by EnforcePermissionHelperDetector.
- if (!isContainedInSubclassOfStub(context, overridingMethod.toUElement() as? UMethod)) {
- return
- }
val overridingAnnotation = overridingMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION)
val overriddenAnnotation = overriddenMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION)
val location = context.getLocation(element)
@@ -161,40 +166,112 @@
) {
if (usageInfo.type == AnnotationUsageType.METHOD_OVERRIDE &&
annotationInfo.origin == AnnotationOrigin.METHOD) {
+ /* Ignore implementations that are not a sub-class of Stub (i.e., Proxy). */
+ val uMethod = element as? UMethod ?: return
+ if (getContainingAidlInterface(context, uMethod) == null) {
+ return
+ }
val overridingMethod = element.sourcePsi as PsiMethod
val overriddenMethod = usageInfo.referenced as PsiMethod
compareMethods(context, element, overridingMethod, overriddenMethod)
}
}
- override fun createUastHandler(context: JavaContext): UElementHandler {
- return object : UElementHandler() {
- override fun visitAnnotation(node: UAnnotation) {
- if (node.qualifiedName != ANNOTATION_ENFORCE_PERMISSION) {
- return
- }
- val method = node.uastParent as? UMethod ?: return
- val overridingMethod = method as PsiMethod
- val parents = overridingMethod.findSuperMethods()
- for (overriddenMethod in parents) {
- // The equivalence check can be skipped, if both methods are
- // annotated, it will be verified by visitAnnotationUsage.
- compareMethods(context, method, overridingMethod,
- overriddenMethod, checkEquivalence = false)
- }
+ override fun createUastHandler(context: JavaContext): UElementHandler = AidlStubHandler(context)
+
+ private inner class AidlStubHandler(val context: JavaContext) : UElementHandler() {
+ override fun visitMethod(node: UMethod) {
+ if (context.evaluator.isAbstract(node)) return
+ if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return
+
+ val stubClass = containingStub(context, node)
+ if (stubClass == null) {
+ context.report(
+ ISSUE_MISUSING_ENFORCE_PERMISSION,
+ node,
+ context.getLocation(node),
+ "The class of ${node.name} does not inherit from an AIDL generated Stub class"
+ )
+ return
+ }
+
+ /* Check that we are connected to the super class */
+ val overridingMethod = node as PsiMethod
+ val parents = overridingMethod.findSuperMethods(stubClass)
+ if (parents.isEmpty()) {
+ context.report(
+ ISSUE_MISUSING_ENFORCE_PERMISSION,
+ node,
+ context.getLocation(node),
+ "The method ${node.name} does not override an AIDL generated method"
+ )
+ return
+ }
+ for (overriddenMethod in parents) {
+ // The equivalence check can be skipped, if both methods are
+ // annotated, it will be verified by visitAnnotationUsage.
+ compareMethods(context, node, overridingMethod,
+ overriddenMethod, checkEquivalence = false)
+ }
+
+ /* Check that the helper is called as a first instruction */
+ val targetExpression = getHelperMethodCallSourceString(node)
+ val message =
+ "Method must start with $targetExpression or super.${node.name}(), if applicable"
+
+ val firstExpression = (node.uastBody as? UBlockExpression)
+ ?.expressions?.firstOrNull()
+
+ if (firstExpression == null) {
+ context.report(
+ ISSUE_ENFORCE_PERMISSION_HELPER,
+ context.getLocation(node),
+ message,
+ )
+ return
+ }
+
+ val firstExpressionSource = firstExpression.skipParenthesizedExprDown()
+ .asSourceString()
+ .filterNot(Char::isWhitespace)
+
+ if (firstExpressionSource != targetExpression &&
+ firstExpressionSource != "super.$targetExpression") {
+ // calling super.<methodName>() is also legal
+ val directSuper = context.evaluator.getSuperMethod(node)
+ val firstCall = findCallExpression(firstExpression)?.resolve()
+ if (directSuper != null && firstCall == directSuper) return
+
+ val locationTarget = getLocationTarget(firstExpression)
+ val expressionLocation = context.getLocation(locationTarget)
+
+ context.report(
+ ISSUE_ENFORCE_PERMISSION_HELPER,
+ context.getLocation(node),
+ message,
+ getHelperMethodFix(node, expressionLocation),
+ )
}
}
}
companion object {
+
+ private const val HELPER_SUFFIX = "_enforcePermission"
+
val EXPLANATION = """
- The @EnforcePermission annotation is used to indicate that the underlying binder code
- has already verified the caller's permissions before calling the appropriate method. The
- verification code is usually generated by the AIDL compiler, which also takes care of
- annotating the generated Java code.
+ The @EnforcePermission annotation is used to delegate the verification of the caller's
+ permissions to a generated AIDL method.
In order to surface that information to platform developers, the same annotation must be
used on the implementation class or methods.
+
+ The @EnforcePermission annotation can only be used on methods whose class extends from
+ the Stub class generated by the AIDL compiler. When @EnforcePermission is applied, the
+ AIDL compiler generates a Stub method to do the permission check called yourMethodName$HELPER_SUFFIX.
+
+ yourMethodName$HELPER_SUFFIX must be executed before any other operation. To do that, you can
+ either call it directly, or call it indirectly via super.yourMethodName().
"""
val ISSUE_MISSING_ENFORCE_PERMISSION: Issue = Issue.create(
@@ -206,7 +283,7 @@
severity = Severity.ERROR,
implementation = Implementation(
EnforcePermissionDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
)
)
@@ -219,8 +296,47 @@
severity = Severity.ERROR,
implementation = Implementation(
EnforcePermissionDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
)
)
+
+ val ISSUE_ENFORCE_PERMISSION_HELPER: Issue = Issue.create(
+ id = "MissingEnforcePermissionHelper",
+ briefDescription = """Missing permission-enforcing method call in AIDL method
+ |annotated with @EnforcePermission""".trimMargin(),
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+
+ val ISSUE_MISUSING_ENFORCE_PERMISSION: Issue = Issue.create(
+ id = "MisusingEnforcePermissionAnnotation",
+ briefDescription = "@EnforcePermission cannot be used here",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+
+ /**
+ * handles an edge case with UDeclarationsExpression, where sourcePsi is null,
+ * resulting in an incorrect Location if used directly
+ */
+ private fun getLocationTarget(firstExpression: UExpression): PsiElement? {
+ if (firstExpression.sourcePsi != null) return firstExpression.sourcePsi
+ if (firstExpression is UDeclarationsExpression) {
+ return firstExpression.declarations.firstOrNull()?.sourcePsi
+ }
+ return null
+ }
}
}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
deleted file mode 100644
index df13af5..0000000
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2022 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.google.android.lint.aidl
-
-import com.android.tools.lint.client.api.UElementHandler
-import com.android.tools.lint.detector.api.Category
-import com.android.tools.lint.detector.api.Detector
-import com.android.tools.lint.detector.api.Implementation
-import com.android.tools.lint.detector.api.Issue
-import com.android.tools.lint.detector.api.JavaContext
-import com.android.tools.lint.detector.api.Scope
-import com.android.tools.lint.detector.api.Severity
-import com.android.tools.lint.detector.api.SourceCodeScanner
-import com.google.android.lint.findCallExpression
-import com.intellij.psi.PsiElement
-import org.jetbrains.uast.UBlockExpression
-import org.jetbrains.uast.UDeclarationsExpression
-import org.jetbrains.uast.UElement
-import org.jetbrains.uast.UExpression
-import org.jetbrains.uast.UMethod
-import org.jetbrains.uast.skipParenthesizedExprDown
-
-class EnforcePermissionHelperDetector : Detector(), SourceCodeScanner {
- override fun getApplicableUastTypes(): List<Class<out UElement?>> =
- listOf(UMethod::class.java)
-
- override fun createUastHandler(context: JavaContext): UElementHandler = AidlStubHandler(context)
-
- private inner class AidlStubHandler(val context: JavaContext) : UElementHandler() {
- override fun visitMethod(node: UMethod) {
- if (context.evaluator.isAbstract(node)) return
- if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return
-
- if (!isContainedInSubclassOfStub(context, node)) {
- context.report(
- ISSUE_MISUSING_ENFORCE_PERMISSION,
- node,
- context.getLocation(node),
- "The class of ${node.name} does not inherit from an AIDL generated Stub class"
- )
- return
- }
-
- val targetExpression = getHelperMethodCallSourceString(node)
- val message =
- "Method must start with $targetExpression or super.${node.name}(), if applicable"
-
- val firstExpression = (node.uastBody as? UBlockExpression)
- ?.expressions?.firstOrNull()
-
- if (firstExpression == null) {
- context.report(
- ISSUE_ENFORCE_PERMISSION_HELPER,
- context.getLocation(node),
- message,
- )
- return
- }
-
- val firstExpressionSource = firstExpression.skipParenthesizedExprDown()
- .asSourceString()
- .filterNot(Char::isWhitespace)
-
- if (firstExpressionSource != targetExpression &&
- firstExpressionSource != "super.$targetExpression") {
- // calling super.<methodName>() is also legal
- val directSuper = context.evaluator.getSuperMethod(node)
- val firstCall = findCallExpression(firstExpression)?.resolve()
- if (directSuper != null && firstCall == directSuper) return
-
- val locationTarget = getLocationTarget(firstExpression)
- val expressionLocation = context.getLocation(locationTarget)
-
- context.report(
- ISSUE_ENFORCE_PERMISSION_HELPER,
- context.getLocation(node),
- message,
- getHelperMethodFix(node, expressionLocation),
- )
- }
- }
- }
-
- companion object {
- private const val HELPER_SUFFIX = "_enforcePermission"
-
- private const val EXPLANATION = """
- The @EnforcePermission annotation can only be used on methods whose class extends from
- the Stub class generated by the AIDL compiler. When @EnforcePermission is applied, the
- AIDL compiler generates a Stub method to do the permission check called yourMethodName$HELPER_SUFFIX.
-
- yourMethodName$HELPER_SUFFIX must be executed before any other operation. To do that, you can
- either call it directly, or call it indirectly via super.yourMethodName().
- """
-
- val ISSUE_ENFORCE_PERMISSION_HELPER: Issue = Issue.create(
- id = "MissingEnforcePermissionHelper",
- briefDescription = """Missing permission-enforcing method call in AIDL method
- |annotated with @EnforcePermission""".trimMargin(),
- explanation = EXPLANATION,
- category = Category.SECURITY,
- priority = 6,
- severity = Severity.ERROR,
- implementation = Implementation(
- EnforcePermissionHelperDetector::class.java,
- Scope.JAVA_FILE_SCOPE
- )
- )
-
- val ISSUE_MISUSING_ENFORCE_PERMISSION: Issue = Issue.create(
- id = "MisusingEnforcePermissionAnnotation",
- briefDescription = "@EnforcePermission cannot be used here",
- explanation = EXPLANATION,
- category = Category.SECURITY,
- priority = 6,
- severity = Severity.ERROR,
- implementation = Implementation(
- EnforcePermissionDetector::class.java,
- Scope.JAVA_FILE_SCOPE
- )
- )
-
- /**
- * handles an edge case with UDeclarationsExpression, where sourcePsi is null,
- * resulting in an incorrect Location if used directly
- */
- private fun getLocationTarget(firstExpression: UExpression): PsiElement? {
- if (firstExpression.sourcePsi != null) return firstExpression.sourcePsi
- if (firstExpression is UDeclarationsExpression) {
- return firstExpression.declarations.firstOrNull()?.sourcePsi
- }
- return null
- }
- }
-}
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
index 75b0073..2afca05 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
@@ -28,7 +28,9 @@
override fun getIssues(): List<Issue> = listOf(
EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
- EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
+ EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION
)
override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
@@ -41,7 +43,9 @@
public class TestClass2 extends IFooMethod.Stub {
@Override
@EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -58,7 +62,9 @@
public class TestClass11 extends IFooMethod.Stub {
@Override
@EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAll() {}
+ public void testMethodAll() {
+ testMethodAll_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -75,7 +81,10 @@
public class TestClass111 extends IFooMethod.Stub {
@Override
@EnforcePermission(allOf={"android.permission.INTERNET", android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAllLiteral() {}
+ public void testMethodAllLiteral() {
+ testMethodAllLiteral_enforcePermission();
+
+ }
}
""").indented(),
*stubs
@@ -92,7 +101,9 @@
public class TestClass12 extends IFooMethod.Stub {
@Override
@EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAny() {}
+ public void testMethodAny() {
+ testMethodAny_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -109,7 +120,9 @@
public class TestClass121 extends IFooMethod.Stub {
@Override
@EnforcePermission(anyOf={"android.permission.INTERNET", android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAnyLiteral() {}
+ public void testMethodAnyLiteral() {
+ testMethodAnyLiteral_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -124,7 +137,9 @@
package test.pkg;
public class TestClass4 extends IFooMethod.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -132,21 +147,67 @@
.run()
.expect("""
src/test/pkg/TestClass4.java:4: Error: The method TestClass4.testMethod is annotated with @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) \
- which differs from the overridden method Stub.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). \
+ which differs from the overridden method IFooMethod.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethod() {}
+ public void testMethod() {
~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
}
+ fun testDetectIssuesAnnotationOnNonStubMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass42 extends IFooMethod.Stub {
+ @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+ public void aRegularMethodNotPartOfStub() {
+ }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""
+ src/test/pkg/TestClass42.java:3: Error: The method aRegularMethodNotPartOfStub does not override an AIDL generated method [MisusingEnforcePermissionAnnotation]
+ @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+ ^
+ 1 errors, 0 warnings
+ """.addLineContinuation())
+ }
+
+ fun testDetectNoIssuesAnnotationOnNonStubMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass43 extends IFooMethod.Stub {
+ public void aRegularMethodNotPartOfStub() {
+ }
+ }
+ """).indented(), java(
+ """
+ package test.pkg;
+ public class TestClass44 extends TestClass43 {
+ @Override
+ public void aRegularMethodNotPartOfStub() {
+ }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
fun testDetectIssuesEmptyAnnotationOnMethod() {
lint().files(java(
"""
package test.pkg;
public class TestClass41 extends IFooMethod.Stub {
@android.annotation.EnforcePermission
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -154,9 +215,9 @@
.run()
.expect("""
src/test/pkg/TestClass41.java:4: Error: The method TestClass41.testMethod is annotated with @android.annotation.EnforcePermission \
- which differs from the overridden method Stub.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). \
+ which differs from the overridden method IFooMethod.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethod() {}
+ public void testMethod() {
~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -168,7 +229,9 @@
package test.pkg;
public class TestClass9 extends IFooMethod.Stub {
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.NFC})
- public void testMethodAny() {}
+ public void testMethodAny() {
+ testMethodAny_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -177,10 +240,10 @@
.expect("""
src/test/pkg/TestClass9.java:4: Error: The method TestClass9.testMethodAny is annotated with \
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.NFC}) \
- which differs from the overridden method Stub.testMethodAny: \
+ which differs from the overridden method IFooMethod.testMethodAny: \
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE}). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAny() {}
+ public void testMethodAny() {
~~~~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -192,7 +255,9 @@
package test.pkg;
public class TestClass91 extends IFooMethod.Stub {
@android.annotation.EnforcePermission(anyOf={"android.permission.INTERNET", "android.permissionoopsthisisatypo.READ_PHONE_STATE"})
- public void testMethodAnyLiteral() {}
+ public void testMethodAnyLiteral() {
+ testMethodAnyLiteral_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -201,10 +266,10 @@
.expect("""
src/test/pkg/TestClass91.java:4: Error: The method TestClass91.testMethodAnyLiteral is annotated with \
@android.annotation.EnforcePermission(anyOf={"android.permission.INTERNET", "android.permissionoopsthisisatypo.READ_PHONE_STATE"}) \
- which differs from the overridden method Stub.testMethodAnyLiteral: \
+ which differs from the overridden method IFooMethod.testMethodAnyLiteral: \
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAnyLiteral() {}
+ public void testMethodAnyLiteral() {
~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -216,7 +281,9 @@
package test.pkg;
public class TestClass10 extends IFooMethod.Stub {
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.NFC})
- public void testMethodAll() {}
+ public void testMethodAll() {
+ testMethodAll_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -225,10 +292,10 @@
.expect("""
src/test/pkg/TestClass10.java:4: Error: The method TestClass10.testMethodAll is annotated with \
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.NFC}) \
- which differs from the overridden method Stub.testMethodAll: \
+ which differs from the overridden method IFooMethod.testMethodAll: \
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE}). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAll() {}
+ public void testMethodAll() {
~~~~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -240,7 +307,9 @@
package test.pkg;
public class TestClass101 extends IFooMethod.Stub {
@android.annotation.EnforcePermission(allOf={"android.permission.INTERNET", "android.permissionoopsthisisatypo.READ_PHONE_STATE"})
- public void testMethodAllLiteral() {}
+ public void testMethodAllLiteral() {
+ testMethodAllLiteral_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -249,10 +318,10 @@
.expect("""
src/test/pkg/TestClass101.java:4: Error: The method TestClass101.testMethodAllLiteral is annotated with \
@android.annotation.EnforcePermission(allOf={"android.permission.INTERNET", "android.permissionoopsthisisatypo.READ_PHONE_STATE"}) \
- which differs from the overridden method Stub.testMethodAllLiteral: \
+ which differs from the overridden method IFooMethod.testMethodAllLiteral: \
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAllLiteral() {}
+ public void testMethodAllLiteral() {
~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -263,16 +332,18 @@
"""
package test.pkg;
public class TestClass6 extends IFooMethod.Stub {
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
)
.run()
.expect("""
- src/test/pkg/TestClass6.java:3: Error: The method TestClass6.testMethod overrides the method Stub.testMethod which is annotated with @EnforcePermission. \
+ src/test/pkg/TestClass6.java:3: Error: The method TestClass6.testMethod overrides the method IFooMethod.testMethod which is annotated with @EnforcePermission. \
The same annotation must be used on TestClass6.testMethod [MissingEnforcePermissionAnnotation]
- public void testMethod() {}
+ public void testMethod() {
~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -284,16 +355,18 @@
package test.pkg;
public class TestClass7 extends IBar.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
)
.run()
.expect("""
- src/test/pkg/TestClass7.java:4: Error: The method TestClass7.testMethod overrides the method Stub.testMethod which is not annotated with @EnforcePermission. \
- The same annotation must be used on Stub.testMethod. Did you forget to annotate the AIDL definition? [MissingEnforcePermissionAnnotation]
- public void testMethod() {}
+ src/test/pkg/TestClass7.java:4: Error: The method TestClass7.testMethod overrides the method IBar.testMethod which is not annotated with @EnforcePermission. \
+ The same annotation must be used on IBar.testMethod. Did you forget to annotate the AIDL definition? [MissingEnforcePermissionAnnotation]
+ public void testMethod() {
~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -304,7 +377,9 @@
"""
package test.pkg;
public class Default extends IFooMethod.Stub {
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -313,23 +388,74 @@
.expect(
"""
src/test/pkg/Default.java:3: Error: The method Default.testMethod \
- overrides the method Stub.testMethod which is annotated with @EnforcePermission. The same annotation must be used on Default.testMethod [MissingEnforcePermissionAnnotation]
- public void testMethod() {}
+ overrides the method IFooMethod.testMethod which is annotated with @EnforcePermission. The same annotation must be used on Default.testMethod [MissingEnforcePermissionAnnotation]
+ public void testMethod() {
~~~~~~~~~~
- 1 errors, 0 warnings
+ 1 errors, 0 warnings
""".addLineContinuation()
)
}
- fun testDoesDetectIssuesShortStringsNotAllowed() {
+ fun testDoesNotDetectIssuesShortStringsAllowedInChildAndParent() {
lint().files(java(
"""
package test.pkg;
import android.annotation.EnforcePermission;
public class TestClass121 extends IFooMethod.Stub {
@Override
+ @EnforcePermission("READ_PHONE_STATE")
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethodParentShortPermission() {
+ testMethodParentShortPermission_enforcePermission();
+ }
+ @Override
@EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
- public void testMethodAnyLiteral() {}
+ public void testMethodAnyLiteral() {
+ testMethodAnyLiteral_enforcePermission();
+ }
+ @Override
+ @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
+ public void testMethodAnyLiteralParentsShortPermission() {
+ testMethodAnyLiteralParentsShortPermission_enforcePermission();
+ }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDoesDetectIssuesWrongShortStringsInChildAndParent() {
+ lint().files(java(
+ """
+ package test.pkg;
+ import android.annotation.EnforcePermission;
+ public class TestClass121 extends IFooMethod.Stub {
+ @Override
+ @EnforcePermission("READ_WRONG_PHONE_STATE")
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_WRONG_PHONE_STATE)
+ public void testMethodParentShortPermission() {
+ testMethodParentShortPermission_enforcePermission();
+ }
+ @Override
+ @EnforcePermission(anyOf={"WRONG_INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteral() {
+ testMethodAnyLiteral_enforcePermission();
+ }
+ @Override
+ @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_WRONG_PHONE_STATE})
+ public void testMethodAnyLiteralParentsShortPermission() {
+ testMethodAnyLiteralParentsShortPermission_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -337,14 +463,19 @@
.run()
.expect(
"""
- src/test/pkg/TestClass121.java:6: Error: The method \
- TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}) \
- which differs from the overridden method Stub.testMethodAnyLiteral: \
- @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). \
- The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAnyLiteral() {}
- ~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
+ src/test/pkg/TestClass121.java:6: Error: The method TestClass121.testMethod is annotated with @EnforcePermission("READ_WRONG_PHONE_STATE") which differs from the overridden method IFooMethod.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethod() {
+ ~~~~~~~~~~
+ src/test/pkg/TestClass121.java:11: Error: The method TestClass121.testMethodParentShortPermission is annotated with @EnforcePermission(android.Manifest.permission.READ_WRONG_PHONE_STATE) which differs from the overridden method IFooMethod.testMethodParentShortPermission: @android.annotation.EnforcePermission("READ_PHONE_STATE"). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodParentShortPermission() {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass121.java:16: Error: The method TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"WRONG_INTERNET", "READ_PHONE_STATE"}) which differs from the overridden method IFooMethod.testMethodAnyLiteral: @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodAnyLiteral() {
+ ~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass121.java:21: Error: The method TestClass121.testMethodAnyLiteralParentsShortPermission is annotated with @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_WRONG_PHONE_STATE}) which differs from the overridden method IFooMethod.testMethodAnyLiteralParentsShortPermission: @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodAnyLiteralParentsShortPermission() {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 4 errors, 0 warnings
""".addLineContinuation()
)
}
@@ -356,28 +487,17 @@
"""
public interface IFooMethod extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements IFooMethod {
- @Override
- @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
- public void testMethod() {}
- @Override
- @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAny() {}
- @Override
- @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
- public void testMethodAnyLiteral() {}
- @Override
- @android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAll() {}
- @Override
- @android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
- public void testMethodAllLiteral() {}
}
@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
public void testMethod();
+ @android.annotation.EnforcePermission("READ_PHONE_STATE")
+ public void testMethodParentShortPermission();
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAny() {}
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
public void testMethodAnyLiteral() {}
+ @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteralParentsShortPermission() {}
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAll() {}
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
@@ -391,8 +511,6 @@
"""
public interface IBar extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements IBar {
- @Override
- public void testMethod() {}
}
public void testMethod();
}
@@ -404,6 +522,7 @@
package android.Manifest;
class permission {
public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+ public static final String READ_WRONG_PHONE_STATE = "android.permission.READ_WRONG_PHONE_STATE";
public static final String NFC = "android.permission.NFC";
public static final String INTERNET = "android.permission.INTERNET";
}
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt
index 5a63bb4..a4b0bc3 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt
@@ -25,10 +25,11 @@
@Suppress("UnstableApiUsage")
class EnforcePermissionHelperDetectorCodegenTest : LintDetectorTest() {
- override fun getDetector(): Detector = EnforcePermissionHelperDetector()
+ override fun getDetector(): Detector = EnforcePermissionDetector()
override fun getIssues(): List<Issue> = listOf(
- EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION
)
override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt
index 10a6e1d..64e2bfb 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt
@@ -20,10 +20,10 @@
import com.android.tools.lint.checks.infrastructure.TestLintTask
class EnforcePermissionHelperDetectorTest : LintDetectorTest() {
- override fun getDetector() = EnforcePermissionHelperDetector()
+ override fun getDetector() = EnforcePermissionDetector()
override fun getIssues() = listOf(
- EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
- EnforcePermissionHelperDetector.ISSUE_MISUSING_ENFORCE_PERMISSION
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION
)
override fun lint(): TestLintTask = super.lint().allowMissingSdk()