Merge "Update owners for android.tracing" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 45f6570..fdd0505 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -121,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",
@@ -149,7 +161,7 @@
name: "android.security.flags-aconfig-java-host",
aconfig_declarations: "android.security.flags-aconfig",
host_supported: true,
- test: true,
+ mode: "test",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
diff --git a/Android.bp b/Android.bp
index 1fb50b6..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",
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/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/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg b/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg
new file mode 100644
index 0000000..d8b2d75
--- /dev/null
+++ b/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 15b10a6..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",
@@ -399,7 +399,10 @@
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"],
+ defaults_visibility: [
+ "//frameworks/base/location",
+ "//frameworks/base/nfc",
+ ],
plugins: ["error_prone_android_framework"],
errorprone: {
javacflags: [
@@ -415,3 +418,49 @@
"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 f6f6929..28b2d4b 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -635,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/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 c9cba00..9bcbd72 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29105,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();
@@ -29220,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);
@@ -29239,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";
}
@@ -33221,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);
@@ -39603,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("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);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 3d01011..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";
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/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/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/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 19ba6a1..dbb3127 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -23,7 +23,6 @@
import android.os.IBinder;
import android.os.ServiceManager;
-import com.android.net.flags.Flags;
import com.android.net.module.util.PermissionUtils;
/**
* Constants and utilities for client code communicating with the network stack service.
@@ -104,16 +103,4 @@
final @NonNull String... otherPermissions) {
PermissionUtils.enforceNetworkStackPermissionOr(context, otherPermissions);
}
-
- /**
- * Get setting of the "set_data_saver_via_cm" flag.
- *
- * @hide
- */
- // A workaround for aconfig. Currently, aconfig value read from platform and mainline code can
- // be inconsistent. To avoid the problem, CTS for mainline code can get the flag value by this
- // method.
- public static boolean getDataSaverViaCmFlag() {
- return Flags.setDataSaverViaCm();
- }
}
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 c7b3b2c..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);
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index c897595..98a980f 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -1081,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 597c948..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;
@@ -134,8 +135,9 @@
/**
* @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,
@@ -147,7 +149,7 @@
* @hide
*/
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,
boolean isEnabled) {
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index d048b59..58b6179 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -328,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
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/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/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..f2f1bd98 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -83,3 +83,7 @@
# PerformanceHintManager
per-file PerformanceHintManager.java = file:/ADPF_OWNERS
+
+# IThermal interfaces
+per-file IThermal* = file:/THERMAL_OWNERS
+
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/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/security/OWNERS b/core/java/android/security/OWNERS
index 33a67ae..533d459 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -8,4 +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
+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..e94f23a 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -8,7 +8,7 @@
}
flag {
- name: "fix_unlocked_device_required_keys"
+ 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/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/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/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 a3e0016..28fd2b4 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1936,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 0e753e5..f7b6a7b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2278,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/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/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 709646b..3a2e50a 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
<!-- Arab Emirates -->
- <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253" />
+ <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214" />
<!-- Albania: 5 digits, known short codes listed -->
<shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -155,7 +155,7 @@
<shortcode country="ie" pattern="\\d{5}" premium="5[3-9]\\d{3}" free="50\\d{3}|116\\d{3}" standard="5[12]\\d{3}" />
<!-- Israel: 4 digits, known premium codes listed -->
- <shortcode country="il" pattern="\\d{1,5}" premium="4422|4545" free="37477" />
+ <shortcode country="il" pattern="\\d{4}" premium="4422|4545" />
<!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU:
https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf -->
@@ -198,9 +198,6 @@
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
<shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" />
- <!-- Namibia: 5 digits -->
- <shortcode country="na" pattern="\\d{1,5}" free="40005" />
-
<!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
<shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
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/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 b714035..231fa48 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -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) {
+ public Builder setMgf1Digests(@NonNull @KeyProperties.DigestEnum String... mgf1Digests) {
mMgf1Digests = Set.of(mgf1Digests);
return this;
}
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/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/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/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/jni/Android.bp b/media/jni/Android.bp
index 6031ef7..94fce79 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -122,11 +122,6 @@
"-Wunused",
"-Wunreachable-code",
],
-
- // Workaround Clang LTO crash.
- lto: {
- never: true,
- },
}
cc_library_shared {
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/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/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/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/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/services/Android.bp b/services/Android.bp
index 0f6b984..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,6 +58,7 @@
enabled: true,
// TODO(b/210510433): Enable optimizations after improving
// retracing infra.
+ // See also FULL_SYSTEM_OPTIMIZE_JAVA.
optimize: false,
shrink: true,
ignore_warnings: false,
@@ -78,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 5295ec8..4fe0592 100644
--- a/services/companion/java/com/android/server/companion/virtual/OWNERS
+++ b/services/companion/java/com/android/server/companion/virtual/OWNERS
@@ -2,7 +2,7 @@
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/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/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/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d56448d..469582d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -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/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 18f24db..f465867 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -965,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) {
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 a12243b..c517058 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -56,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;
@@ -67,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;
@@ -109,7 +106,6 @@
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
-import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -120,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;
@@ -160,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;
@@ -190,8 +182,6 @@
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;
@@ -451,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;
@@ -483,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);
}
@@ -511,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)
*/
@@ -2549,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);
}
@@ -2616,13 +2483,13 @@
}
/**
- * 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))) {
@@ -3385,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
@@ -3905,6 +3779,7 @@
updateState(DetailedState.FAILED, exception.getMessage());
}
+ clearVpnNetworkPreference(mSessionKey);
disconnectVpnRunner();
}
@@ -4039,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);
+ }
}
/**
@@ -4085,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();
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/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index fa95a34..b4c3997 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,6 +1414,20 @@
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);
}
@@ -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 550ad5d..681d1a0 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -74,7 +74,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.HexDump;
import com.android.modules.utils.build.SdkLevel;
-import com.android.net.flags.Flags;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.PermissionUtils;
import com.android.server.FgThread;
@@ -328,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.
@@ -1062,7 +1061,7 @@
}
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setDataSaverModeEnabled");
try {
- if (Flags.setDataSaverViaCm()) {
+ if (SdkLevel.isAtLeastV()) {
// setDataSaverEnabled throws if it fails to set data saver.
mContext.getSystemService(ConnectivityManager.class)
.setDataSaverEnabled(enable);
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/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/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 e11e226..6814848 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -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);
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/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index 1970ee4..94340ec 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -2,6 +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
\ No newline at end of file
+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/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 76ee845..cc892a0 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1421,6 +1421,7 @@
final NetworkStats nonTaggedStats =
NetworkStatsUtils.fromPublicNetworkStats(queryNonTaggedStats);
+ queryNonTaggedStats.close();
if (!includeTags) return nonTaggedStats;
final android.app.usage.NetworkStats queryTaggedStats =
@@ -1429,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..24c971c 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);
@@ -1647,8 +1647,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) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6d560e4..f2346e6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -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;
}
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/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/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/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/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 2cdfbff..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,7 +57,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
-import com.android.net.flags.Flags;
+import com.android.modules.utils.build.SdkLevel;
import org.junit.After;
import org.junit.Before;
@@ -264,7 +264,7 @@
verify(mCm).addUidToMeteredNetworkDenyList(TEST_UID);
mNMService.setDataSaverModeEnabled(true);
- if (Flags.setDataSaverViaCm()) {
+ if (SdkLevel.isAtLeastV()) {
verify(mCm).setDataSaverEnabled(true);
} else {
verify(mNetdService).bandwidthEnableDataSaver(true);
@@ -284,7 +284,7 @@
mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, false);
verify(mCm).removeUidFromMeteredNetworkAllowList(TEST_UID);
mNMService.setDataSaverModeEnabled(false);
- if (Flags.setDataSaverViaCm()) {
+ if (SdkLevel.isAtLeastV()) {
verify(mCm).setDataSaverEnabled(false);
} else {
verify(mNetdService).bandwidthEnableDataSaver(false);
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/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/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/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 80c7a21..db3a992 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/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/res/drawable/robot_repeated.xml b/tests/HwAccelerationTest/res/drawable/robot_repeated.xml
new file mode 100644
index 0000000..bbb15b7
--- /dev/null
+++ b/tests/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/src/com/android/test/hwui/BackdropBlurActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java
new file mode 100644
index 0000000..8086b29
--- /dev/null
+++ b/tests/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/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/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/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index ed3e1ac..40cba3e 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -119,6 +119,7 @@
"io/Util.cpp",
"io/ZipArchive.cpp",
"link/AutoVersioner.cpp",
+ "link/FeatureFlagsFilter.cpp",
"link/ManifestFixer.cpp",
"link/NoDefaultResourceRemover.cpp",
"link/PrivateAttributeMover.cpp",
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index f00be36..1fb5cc0 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -2513,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 a08f385..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;
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/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/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/aidl/EnforcePermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index 83b8f16..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
@@ -168,7 +168,7 @@
annotationInfo.origin == AnnotationOrigin.METHOD) {
/* Ignore implementations that are not a sub-class of Stub (i.e., Proxy). */
val uMethod = element as? UMethod ?: return
- if (!isContainedInSubclassOfStub(context, uMethod)) {
+ if (getContainingAidlInterface(context, uMethod) == null) {
return
}
val overridingMethod = element.sourcePsi as PsiMethod
@@ -184,7 +184,8 @@
if (context.evaluator.isAbstract(node)) return
if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return
- if (!isContainedInSubclassOfStub(context, node)) {
+ val stubClass = containingStub(context, node)
+ if (stubClass == null) {
context.report(
ISSUE_MISUSING_ENFORCE_PERMISSION,
node,
@@ -196,7 +197,7 @@
/* Check that we are connected to the super class */
val overridingMethod = node as PsiMethod
- val parents = overridingMethod.findSuperMethods()
+ val parents = overridingMethod.findSuperMethods(stubClass)
if (parents.isEmpty()) {
context.report(
ISSUE_MISUSING_ENFORCE_PERMISSION,
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 d8afcb9..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
@@ -176,6 +176,29 @@
""".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(
"""