diff options
108 files changed, 1925 insertions, 1199 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 45f65709533f..fdd050538ecc 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -121,6 +121,18 @@ aconfig_declarations { 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 @@ java_aconfig_library { 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 1fb50b6c24a6..1aa297faa7e8 100644 --- a/Android.bp +++ b/Android.bp @@ -396,6 +396,7 @@ java_defaults { "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", diff --git a/INPUT_OWNERS b/INPUT_OWNERS index e02ba770cdf8..44b2f3805495 100644 --- a/INPUT_OWNERS +++ b/INPUT_OWNERS @@ -5,3 +5,5 @@ michaelwr@google.com 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 000000000000..b95b7e84191c --- /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 4c57e640c141..8ff3f9bc6620 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/res/drawable-nodpi/fountain_night.jpg b/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg Binary files differnew file mode 100644 index 000000000000..d8b2d759e4c0 --- /dev/null +++ b/apct-tests/perftests/core/res/drawable-nodpi/fountain_night.jpg 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 f84a0d037ca5..e5a06c9bd146 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 @@ public class CanvasPerfTest { 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 a4a0b4b29200..83ad736023b0 100644 --- a/apex/jobscheduler/service/Android.bp +++ b/apex/jobscheduler/service/Android.bp @@ -26,6 +26,10 @@ java_library { "unsupportedappusage", ], + static_libs: [ + "modules-utils-fastxmlserializer", + ], + // Rename classes shared with the framework jarjar_rules: "jarjar-rules.txt", diff --git a/api/Android.bp b/api/Android.bp index d2e0849ed946..a89e1f268e58 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -83,7 +83,6 @@ combined_apis { "framework-configinfrastructure", "framework-connectivity", "framework-connectivity-t", - "framework-crashrecovery", "framework-devicelock", "framework-graphics", "framework-healthfitness", @@ -91,6 +90,7 @@ combined_apis { "framework-media", "framework-mediaprovider", "framework-ondevicepersonalization", + "framework-pdf", "framework-permission", "framework-permission-s", "framework-scheduling", @@ -106,7 +106,6 @@ combined_apis { system_server_classpath: [ "service-art", "service-configinfrastructure", - "service-crashrecovery", "service-healthfitness", "service-media-s", "service-permission", @@ -350,6 +349,16 @@ stubs_defaults { visibility: ["//frameworks/base/api"], } +// We resolve dependencies on APIs in modules by depending on a prebuilt of the whole +// platform (sdk_system_current_android). That prebuilt does not include module-lib APIs, +// so use the prebuilt module-lib stubs for modules that export module-lib stubs that the +// non-updatable part depends on. +non_updatable_api_deps_on_modules = [ + "sdk_module-lib_current_framework-tethering", + "sdk_module-lib_current_framework-connectivity-t", + "sdk_system_current_android", +] + // Defaults with module APIs in the classpath (mostly from prebuilts). // Suitable for compiling android-non-updatable. stubs_defaults { @@ -361,19 +370,7 @@ stubs_defaults { "packages/modules/Media/apex/aidl/stable", ], }, - libs: [ - "art.module.public.api", - "sdk_module-lib_current_framework-tethering", - "sdk_module-lib_current_framework-connectivity-t", - "sdk_public_current_framework-bluetooth", - // There are a few classes from modules used by the core that - // need to be resolved by metalava. We use a prebuilt stub of the - // full sdk to ensure we can resolve them. If a new class gets added, - // the prebuilts/sdk/current needs to be updated. - "sdk_system_current_android", - // NOTE: The below can be removed once the prebuilt stub contains IKE. - "sdk_system_current_android.net.ipsec.ike", - ], + libs: non_updatable_api_deps_on_modules, } // Defaults for the java_sdk_libraries of unbundled jars from framework. diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp index 5688b968db87..f6f69291ce0e 100644 --- a/api/StubLibraries.bp +++ b/api/StubLibraries.bp @@ -351,17 +351,7 @@ java_library { "android-non-updatable_from_source_defaults", ], srcs: [":module-lib-api-stubs-docs-non-updatable"], - libs: [ - // We cannot depend on all-modules-module-lib-stubs, because the module-lib stubs - // depend on this stub. We resolve dependencies on APIs in modules by depending - // on a prebuilt of the whole platform (sdk_system_current_android). - // That prebuilt does not include module-lib APIs, so use the prebuilt module-lib - // stubs for modules that export module-lib stubs that the non-updatable part - // depends on. - "sdk_module-lib_current_framework-tethering", - "sdk_module-lib_current_framework-connectivity-t", - "sdk_system_current_android", - ], + libs: non_updatable_api_deps_on_modules, dist: { dir: "apistubs/android/module-lib", }, diff --git a/api/coverage/tools/Android.bp b/api/coverage/tools/Android.bp new file mode 100644 index 000000000000..3e169120dc48 --- /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 000000000000..948e64f22f20 --- /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() + .allTopLevelClasses() + .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 000000000000..a858108a27b2 --- /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 b33fab6e1a9f..8a3d35e2d0eb 100644 --- a/boot/Android.bp +++ b/boot/Android.bp @@ -84,10 +84,6 @@ custom_platform_bootclasspath { 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 af58174ff9a2..958b2f9f4e26 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4456,6 +4456,7 @@ package android.app { method public boolean onPreparePanel(int, @Nullable android.view.View, @NonNull android.view.Menu); method public void onProvideAssistContent(android.app.assist.AssistContent); method public void onProvideAssistData(android.os.Bundle); + method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu, int); method public android.net.Uri onProvideReferrer(); method public void onRequestPermissionsResult(int, @NonNull String[], @NonNull int[]); method @CallSuper protected void onRestart(); @@ -13770,6 +13771,7 @@ package android.database { method public String getColumnName(int); method public android.os.Bundle getExtras(); method public android.net.Uri getNotificationUri(); + method public java.util.List<android.net.Uri> getNotificationUris(); method public final int getPosition(); method public int getType(int); method @Deprecated protected Object getUpdatedField(int); @@ -13795,6 +13797,7 @@ package android.database { method public android.os.Bundle respond(android.os.Bundle); method public void setExtras(android.os.Bundle); method public void setNotificationUri(android.content.ContentResolver, android.net.Uri); + method public void setNotificationUris(@NonNull android.content.ContentResolver, @NonNull java.util.List<android.net.Uri>); method public void unregisterContentObserver(android.database.ContentObserver); method public void unregisterDataSetObserver(android.database.DataSetObserver); field @Deprecated protected boolean mClosed; @@ -13926,6 +13929,7 @@ package android.database { method public boolean hasNext(); method public java.util.Iterator<android.database.CursorJoiner.Result> iterator(); method public android.database.CursorJoiner.Result next(); + method public void remove(); } public enum CursorJoiner.Result { @@ -13993,6 +13997,7 @@ package android.database { method public int getInt(int); method public long getLong(int); method public android.net.Uri getNotificationUri(); + method public java.util.List<android.net.Uri> getNotificationUris(); method public int getPosition(); method public short getShort(int); method public String getString(int); @@ -14017,6 +14022,7 @@ package android.database { method public android.os.Bundle respond(android.os.Bundle); method public void setExtras(android.os.Bundle); method public void setNotificationUri(android.content.ContentResolver, android.net.Uri); + method public void setNotificationUris(android.content.ContentResolver, java.util.List<android.net.Uri>); method public void unregisterContentObserver(android.database.ContentObserver); method public void unregisterDataSetObserver(android.database.DataSetObserver); } @@ -22644,6 +22650,9 @@ package android.media { ctor public MediaCodec.CryptoException(int, @Nullable String); method @Nullable public android.media.MediaCodec.CryptoInfo getCryptoInfo(); method public int getErrorCode(); + method public int getErrorContext(); + method public int getOemError(); + method public int getVendorError(); field @Deprecated public static final int ERROR_FRAME_TOO_LARGE = 8; // 0x8 field @Deprecated public static final int ERROR_INSUFFICIENT_OUTPUT_PROTECTION = 4; // 0x4 field @Deprecated public static final int ERROR_INSUFFICIENT_SECURITY = 7; // 0x7 @@ -23156,6 +23165,9 @@ package android.media { public final class MediaCryptoException extends java.lang.Exception implements android.media.MediaDrmThrowable { ctor public MediaCryptoException(@Nullable String); + method public int getErrorContext(); + method public int getOemError(); + method public int getVendorError(); } public abstract class MediaDataSource implements java.io.Closeable { @@ -23380,6 +23392,9 @@ package android.media { public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable { method @NonNull public String getDiagnosticInfo(); method public int getErrorCode(); + method public int getErrorContext(); + method public int getOemError(); + method public int getVendorError(); method public boolean isTransient(); } @@ -23453,6 +23468,9 @@ package android.media { public static final class MediaDrm.SessionException extends java.lang.RuntimeException implements android.media.MediaDrmThrowable { ctor public MediaDrm.SessionException(int, @Nullable String); method @Deprecated public int getErrorCode(); + method public int getErrorContext(); + method public int getOemError(); + method public int getVendorError(); method public boolean isTransient(); field @Deprecated public static final int ERROR_RESOURCE_CONTENTION = 1; // 0x1 field @Deprecated public static final int ERROR_UNKNOWN = 0; // 0x0 @@ -23460,6 +23478,9 @@ package android.media { public class MediaDrmException extends java.lang.Exception implements android.media.MediaDrmThrowable { ctor public MediaDrmException(String); + method public int getErrorContext(); + method public int getOemError(); + method public int getVendorError(); } public class MediaDrmResetException extends java.lang.IllegalStateException implements android.media.MediaDrmThrowable { @@ -29084,14 +29105,17 @@ package android.nfc { } 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(); @@ -29199,6 +29223,7 @@ package android.nfc.cardemulation { 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); @@ -29218,9 +29243,20 @@ package android.nfc.cardemulation { 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"; } @@ -32327,6 +32363,7 @@ package android.opengl { method public void surfaceCreated(android.view.SurfaceHolder); method public void surfaceDestroyed(android.view.SurfaceHolder); method @Deprecated public void surfaceRedrawNeeded(android.view.SurfaceHolder); + method public void surfaceRedrawNeededAsync(android.view.SurfaceHolder, Runnable); field public static final int DEBUG_CHECK_GL_ERROR = 1; // 0x1 field public static final int DEBUG_LOG_GL_CALLS = 2; // 0x2 field public static final int RENDERMODE_CONTINUOUSLY = 1; // 0x1 @@ -39581,7 +39618,7 @@ package android.security.keystore { 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); @@ -47786,6 +47823,7 @@ package android.text { method public boolean hasNext(); method public java.util.Iterator<java.lang.String> iterator(); method public String next(); + method public void remove(); method public void setString(String); } @@ -49240,6 +49278,7 @@ package android.util { method public void ensureCapacity(int); method public java.util.Set<java.util.Map.Entry<K,V>> entrySet(); method public boolean equals(@Nullable Object); + method public void forEach(java.util.function.BiConsumer<? super K,? super V>); method public V get(Object); method public int hashCode(); method public int indexOfKey(Object); @@ -49253,6 +49292,7 @@ package android.util { method public V remove(Object); method public boolean removeAll(java.util.Collection<?>); method public V removeAt(int); + method public void replaceAll(java.util.function.BiFunction<? super K,? super V,? extends V>); method public boolean retainAll(java.util.Collection<?>); method public V setValueAt(int, V); method public int size(); @@ -49283,6 +49323,7 @@ package android.util { method public boolean removeAll(android.util.ArraySet<? extends E>); method public boolean removeAll(java.util.Collection<?>); method public E removeAt(int); + method public boolean removeIf(java.util.function.Predicate<? super E>); method public boolean retainAll(java.util.Collection<?>); method public int size(); method public Object[] toArray(); @@ -53137,6 +53178,7 @@ package android.view { method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>); method protected boolean drawChild(@NonNull android.graphics.Canvas, android.view.View, long); method public void endViewTransition(android.view.View); + method @Nullable public android.window.OnBackInvokedDispatcher findOnBackInvokedDispatcherForChild(@NonNull android.view.View, @NonNull android.view.View); method public android.view.View focusSearch(android.view.View, int); method public void focusableViewAvailable(android.view.View); method protected android.view.ViewGroup.LayoutParams generateDefaultLayoutParams(); @@ -53178,6 +53220,7 @@ package android.view { method public void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int); method public final void offsetDescendantRectToMyCoords(android.view.View, android.graphics.Rect); method public final void offsetRectIntoDescendantCoords(android.view.View, android.graphics.Rect); + method @CallSuper public void onDescendantInvalidated(@NonNull android.view.View, @NonNull android.view.View); method public boolean onInterceptHoverEvent(android.view.MotionEvent); method public boolean onInterceptTouchEvent(android.view.MotionEvent); method protected abstract void onLayout(boolean, int, int, int, int); @@ -55337,12 +55380,14 @@ package android.view.inputmethod { method @Nullable public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); method @Nullable public android.os.Handler getHandler(); method @Nullable public CharSequence getSelectedText(int); + method @Nullable public android.view.inputmethod.SurroundingText getSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int); method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); method public boolean performPrivateCommand(String, android.os.Bundle); method public static final void removeComposingSpans(@NonNull android.text.Spannable); + method public boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute); method public boolean reportFullscreenMode(boolean); method public boolean requestCursorUpdates(int); method public boolean sendKeyEvent(android.view.KeyEvent); @@ -55350,6 +55395,7 @@ package android.view.inputmethod { method public static void setComposingSpans(@NonNull android.text.Spannable); method public boolean setComposingText(CharSequence, int); method public boolean setSelection(int, int); + method @Nullable public android.view.inputmethod.TextSnapshot takeSnapshot(); } public final class CompletionInfo implements android.os.Parcelable { @@ -55684,6 +55730,7 @@ package android.view.inputmethod { method public boolean commitContent(android.view.inputmethod.InputContentInfo, int, android.os.Bundle); method public boolean commitCorrection(android.view.inputmethod.CorrectionInfo); method public boolean commitText(CharSequence, int); + method public boolean commitText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute); method public boolean deleteSurroundingText(int, int); method public boolean deleteSurroundingTextInCodePoints(int, int); method public boolean endBatchEdit(); @@ -55692,18 +55739,29 @@ package android.view.inputmethod { method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int); method public android.os.Handler getHandler(); method public CharSequence getSelectedText(int); + method @Nullable public android.view.inputmethod.SurroundingText getSurroundingText(int, int, int); method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int); method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int); method public boolean performContextMenuAction(int); method public boolean performEditorAction(int); + method public void performHandwritingGesture(@NonNull android.view.inputmethod.HandwritingGesture, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.IntConsumer); method public boolean performPrivateCommand(String, android.os.Bundle); + method public boolean performSpellCheck(); + method public boolean previewHandwritingGesture(@NonNull android.view.inputmethod.PreviewableHandwritingGesture, @Nullable android.os.CancellationSignal); + method public boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute); method public boolean reportFullscreenMode(boolean); method public boolean requestCursorUpdates(int); + method public boolean requestCursorUpdates(int, int); + method public void requestTextBoundsInfo(@NonNull android.graphics.RectF, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.inputmethod.TextBoundsInfoResult>); method public boolean sendKeyEvent(android.view.KeyEvent); method public boolean setComposingRegion(int, int); + method public boolean setComposingRegion(int, int, @Nullable android.view.inputmethod.TextAttribute); method public boolean setComposingText(CharSequence, int); + method public boolean setComposingText(@NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute); + method public boolean setImeConsumesInput(boolean); method public boolean setSelection(int, int); method public void setTarget(android.view.inputmethod.InputConnection); + method @Nullable public android.view.inputmethod.TextSnapshot takeSnapshot(); } public final class InputContentInfo implements android.os.Parcelable { @@ -58270,6 +58328,7 @@ package android.widget { public abstract class BaseAdapter implements android.widget.ListAdapter android.widget.SpinnerAdapter { ctor public BaseAdapter(); method public boolean areAllItemsEnabled(); + method public CharSequence[] getAutofillOptions(); method public android.view.View getDropDownView(int, android.view.View, android.view.ViewGroup); method public int getItemViewType(int); method public int getViewTypeCount(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c1b70cb0b526..3d0101167782 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -357,6 +357,7 @@ package android { field public static final String SYSTEM_APPLICATION_OVERLAY = "android.permission.SYSTEM_APPLICATION_OVERLAY"; field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA"; field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED"; + field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED"; field public static final String TIS_EXTENSION_INTERFACE = "android.permission.TIS_EXTENSION_INTERFACE"; field public static final String TOGGLE_AUTOMOTIVE_PROJECTION = "android.permission.TOGGLE_AUTOMOTIVE_PROJECTION"; field public static final String TRIGGER_LOST_MODE = "android.permission.TRIGGER_LOST_MODE"; @@ -10303,6 +10304,7 @@ package android.nfc.cardemulation { method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid(); method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean hasCategory(@NonNull String); method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOnHost(); + method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOtherServiceEnabled(); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager); @@ -10313,6 +10315,7 @@ package android.nfc.cardemulation { method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement(); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String); + method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOtherServiceEnabled(boolean); method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int); field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR; } @@ -10795,6 +10798,7 @@ package android.os { ctor public ParcelableHolder(int); method public int describeContents(); method @Nullable public <T extends android.os.Parcelable> T getParcelable(@NonNull Class<T>); + method public int getStability(); method public void readFromParcel(@NonNull android.os.Parcel); method public void setParcelable(@Nullable android.os.Parcelable); method public void writeToParcel(@NonNull android.os.Parcel, int); diff --git a/core/java/Android.bp b/core/java/Android.bp index 26b758189996..d5acf4228a6b 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -226,7 +226,6 @@ filegroup { "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 @@ aidl_interface { backend: { rust: { enabled: true, + apex_available: [ + "//apex_available:platform", + "com.android.virt", + ], }, }, } @@ -455,7 +458,6 @@ filegroup { "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 @@ filegroup { "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 4f5da99d539a..325758b58a3b 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -113,6 +113,7 @@ import android.hardware.iris.IrisManager; 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 @@ public final class SystemServiceRegistry { new CachedServiceFetcher<ContextHubManager>() { @Override public ContextHubManager createService(ContextImpl ctx) throws ServiceNotFoundException { - return new ContextHubManager(ctx.getOuterContext(), - ctx.mMainThread.getHandler().getLooper()); + IBinder b = ServiceManager.getService(Context.CONTEXTHUB_SERVICE); + if (b == null) { + return null; + } + return new ContextHubManager(IContextHubService.Stub.asInterface(b), + ctx.mMainThread.getHandler().getLooper()); }}); registerService(Context.INCIDENT_SERVICE, IncidentManager.class, diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index cd45f4df3d50..b4f4a7efad98 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -212,6 +212,11 @@ public final class AttributionSource implements Parcelable { } /** @hide */ + public AttributionSource withDefaultToken() { + return withToken(sDefaultToken); + } + + /** @hide */ public AttributionSource withPid(int pid) { return new AttributionSource(getUid(), pid, getPackageName(), getAttributionTag(), getToken(), mAttributionSourceState.renouncedPermissions, getNext()); @@ -520,16 +525,28 @@ public final class AttributionSource implements Parcelable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AttributionSource that = (AttributionSource) o; - return mAttributionSourceState.uid == that.mAttributionSourceState.uid + return equalsExceptToken(that) && Objects.equals( + mAttributionSourceState.token, that.mAttributionSourceState.token); + } + + /** + * We store trusted attribution sources without their token (the token is the key to the map) + * to avoid having a strong reference to the token. This means, when checking the equality of a + * supplied AttributionSource in PermissionManagerService.isTrustedAttributionSource, we want to + * compare everything except the token. + * + * @hide + */ + public boolean equalsExceptToken(@Nullable AttributionSource o) { + if (o == null) return false; + return mAttributionSourceState.uid == o.mAttributionSourceState.uid && Objects.equals(mAttributionSourceState.packageName, - that.mAttributionSourceState.packageName) + o.mAttributionSourceState.packageName) && Objects.equals(mAttributionSourceState.attributionTag, - that.mAttributionSourceState.attributionTag) - && Objects.equals(mAttributionSourceState.token, - that.mAttributionSourceState.token) + o.mAttributionSourceState.attributionTag) && Arrays.equals(mAttributionSourceState.renouncedPermissions, - that.mAttributionSourceState.renouncedPermissions) - && Objects.equals(getNext(), that.getNext()); + o.mAttributionSourceState.renouncedPermissions) + && Objects.equals(getNext(), o.getNext()); } @Override diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 01ce7b9c0731..481ec7207c8a 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 @@ public final class ContextHubManager { } }; - /** @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 0a6be20226b8..eda80c861698 100644 --- a/core/java/android/net/INetworkManagementEventObserver.aidl +++ b/core/java/android/net/INetworkManagementEventObserver.aidl @@ -85,14 +85,14 @@ oneway interface INetworkManagementEventObserver { /** * 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/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index 0c95c2ec7a7a..f6beec179d57 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -84,4 +84,6 @@ interface INfcAdapter 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 c7b3b2c03f65..191385a3c13d 100644 --- a/core/java/android/nfc/INfcCardEmulation.aidl +++ b/core/java/android/nfc/INfcCardEmulation.aidl @@ -30,6 +30,7 @@ interface INfcCardEmulation 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 c89759553810..98a980f5e7f8 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -1081,6 +1081,61 @@ public final class NfcAdapter { } } + + /** + * 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 9cf8c4ddc53b..e331c95288d9 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; @@ -129,13 +130,14 @@ public final class ApduServiceInfo implements Parcelable { /** * State of the service for CATEGORY_OTHER selection */ - private boolean mOtherServiceSelectionState; + private boolean mOtherServiceEnabled; /** * @hide */ + @UnsupportedAppUsage public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, - List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups, + ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, boolean requiresUnlock, int bannerResource, int uid, String settingsActivityName, String offHost, String staticOffHost) { this(info, onHost, description, staticAidGroups, dynamicAidGroups, @@ -147,13 +149,13 @@ public final class ApduServiceInfo implements Parcelable { * @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 isSelected) { + boolean isEnabled) { this(info, onHost, description, staticAidGroups, dynamicAidGroups, requiresUnlock, onHost ? true : false, bannerResource, uid, - settingsActivityName, offHost, staticOffHost, isSelected); + settingsActivityName, offHost, staticOffHost, isEnabled); } /** @@ -162,7 +164,7 @@ public final class ApduServiceInfo implements Parcelable { public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups, boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid, - String settingsActivityName, String offHost, String staticOffHost, boolean isSelected) { + String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled) { this.mService = info; this.mDescription = description; this.mStaticAidGroups = new HashMap<String, AidGroup>(); @@ -181,7 +183,7 @@ public final class ApduServiceInfo implements Parcelable { this.mBannerResourceId = bannerResource; this.mUid = uid; this.mSettingsActivityName = settingsActivityName; - this.mOtherServiceSelectionState = isSelected; + this.mOtherServiceEnabled = isEnabled; } @@ -372,7 +374,7 @@ public final class ApduServiceInfo implements Parcelable { // Set uid mUid = si.applicationInfo.uid; - mOtherServiceSelectionState = false; // support other category + mOtherServiceEnabled = false; // support other category } @@ -744,7 +746,7 @@ public final class ApduServiceInfo implements Parcelable { dest.writeInt(mUid); dest.writeString(mSettingsActivityName); - dest.writeInt(mOtherServiceSelectionState ? 1 : 0); + dest.writeInt(mOtherServiceEnabled ? 1 : 0); }; @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @@ -772,11 +774,11 @@ public final class ApduServiceInfo implements Parcelable { int bannerResource = source.readInt(); int uid = source.readInt(); String settingsActivityName = source.readString(); - boolean isSelected = source.readInt() != 0; + boolean isEnabled = source.readInt() != 0; return new ApduServiceInfo(info, onHost, description, staticAidGroups, dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid, settingsActivityName, offHostName, staticOffHostName, - isSelected); + isEnabled); } @Override @@ -807,7 +809,7 @@ public final class ApduServiceInfo implements Parcelable { pw.println(" Static AID groups:"); for (AidGroup group : mStaticAidGroups.values()) { pw.println(" Category: " + group.getCategory() - + "(selected: " + mOtherServiceSelectionState + ")"); + + "(enabled: " + mOtherServiceEnabled + ")"); for (String aid : group.getAids()) { pw.println(" AID: " + aid); } @@ -815,7 +817,7 @@ public final class ApduServiceInfo implements Parcelable { pw.println(" Dynamic AID groups:"); for (AidGroup group : mDynamicAidGroups.values()) { pw.println(" Category: " + group.getCategory() - + "(selected: " + mOtherServiceSelectionState + ")"); + + "(enabled: " + mOtherServiceEnabled + ")"); for (String aid : group.getAids()) { pw.println(" AID: " + aid); } @@ -827,18 +829,24 @@ public final class ApduServiceInfo implements Parcelable { /** - * @hide + * Enable or disable this CATEGORY_OTHER service. + * + * @param enabled true to indicate if user has enabled this service */ - public void setOtherServiceState(boolean selected) { - mOtherServiceSelectionState = selected; + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public void setOtherServiceEnabled(boolean enabled) { + mOtherServiceEnabled = enabled; } /** - * @hide + * Returns whether this CATEGORY_OTHER service is enabled or not. + * + * @return true to indicate if user has enabled this service */ - public boolean isSelectedOtherService() { - return mOtherServiceSelectionState; + @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) + public boolean isOtherServiceEnabled() { + return mOtherServiceEnabled; } /** diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index d048b595ad1e..58b6179691e9 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -328,6 +328,24 @@ public final class CardEmulation { 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 55d0e73780a2..7cd2533a7dbf 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.Messenger; 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 @@ public abstract class HostApduService extends Service { /** * @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 @@ public abstract class HostApduService extends Service { 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 @@ public abstract class HostApduService extends Service { 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 @@ public abstract class HostApduService extends Service { } } + /** + * 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 cd50ace036de..17e042761dbe 100644 --- a/core/java/android/nfc/flags.aconfig +++ b/core/java/android/nfc/flags.aconfig @@ -20,3 +20,31 @@ flag { 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 e06b0f9f4bc2..61aa222ef482 100644 --- a/core/java/android/os/DeadObjectException.java +++ b/core/java/android/os/DeadObjectException.java @@ -19,7 +19,29 @@ import android.os.RemoteException; /** * 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 1e869249eb9d..3b107984cebf 100644 --- a/core/java/android/os/DeadSystemRuntimeException.java +++ b/core/java/android/os/DeadSystemRuntimeException.java @@ -18,9 +18,12 @@ package android.os; /** * 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/OWNERS b/core/java/android/os/OWNERS index d9a9266cfddc..f2f1bd98ff5d 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -83,3 +83,7 @@ per-file ArtModuleServiceManager.java = file:platform/art:/OWNERS # PerformanceHintManager per-file PerformanceHintManager.java = file:/ADPF_OWNERS + +# IThermal interfaces +per-file IThermal* = file:/THERMAL_OWNERS + diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 90a40717eada..d12e3b2431d5 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -2023,9 +2023,13 @@ public final class StrictMode { return; } + // Temporarily disable checks so that explicit GC is allowed. + final int oldMask = getThreadPolicyMask(); + setThreadPolicyMask(0); System.gc(); System.runFinalization(); System.gc(); + setThreadPolicyMask(oldMask); // Note: classInstanceLimit is immutable, so this is lock-free // Create the classes array. diff --git a/core/java/android/preference/OWNERS b/core/java/android/preference/OWNERS index 827134e8fc9d..b4cb9ec7ceda 100644 --- a/core/java/android/preference/OWNERS +++ b/core/java/android/preference/OWNERS @@ -1,3 +1,5 @@ lpf@google.com pavlis@google.com clarabayarri@google.com + +per-file SeekBarVolumizer.java = jmtrivi@google.com
\ No newline at end of file diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS index 7f0a651c6420..e20357fa2dd9 100644 --- a/core/java/android/widget/OWNERS +++ b/core/java/android/widget/OWNERS @@ -12,6 +12,6 @@ per-file TextView*,Edit*,Selection* = file:../text/OWNERS per-file SpellChecker.java = file:../view/inputmethod/OWNERS -per-file RemoteViews* = file:../appwidget/OWNERS +per-file Remote* = file:../appwidget/OWNERS per-file Toast.java = juliacr@google.com, jeffdq@google.com diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java index 0947ec178c77..f62094d231ef 100644 --- a/core/java/com/android/internal/net/VpnProfile.java +++ b/core/java/com/android/internal/net/VpnProfile.java @@ -618,4 +618,14 @@ public final class VpnProfile implements Cloneable, Parcelable { 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 70514c30d90d..01c91bae72cd 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -337,6 +337,12 @@ public class ProcessCpuTracker { @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/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java deleted file mode 100644 index 929c9e8bb8c1..000000000000 --- 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 a3e0016f9174..28fd2b488426 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1936,7 +1936,8 @@ public class LockPatternUtils { * 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 139b88b108c5..61e017d3443f 100644 --- a/core/java/com/android/server/net/BaseNetworkObserver.java +++ b/core/java/com/android/server/net/BaseNetworkObserver.java @@ -64,7 +64,7 @@ public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub { } @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/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp index b1dab85d2e27..f9d00edce3fa 100644 --- a/core/jni/android_media_AudioRecord.cpp +++ b/core/jni/android_media_AudioRecord.cpp @@ -212,14 +212,11 @@ static jint android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject w 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 @@ static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject th 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 18c60a793166..91dfc6023e42 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -1245,7 +1245,7 @@ jint android_os_Process_sendSignalToProcessGroup(JNIEnv* env, jobject clazz, jin 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/res/Android.bp b/core/res/Android.bp index b71995f899c5..6fa70d840d07 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -151,6 +151,8 @@ android_app { "simulated_device_launcher", ], }, + + generate_product_characteristics_rro: true, } java_genrule { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7d9d99113663..0e753e51f597 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2225,6 +2225,13 @@ <permission android:name="android.permission.MANAGE_LOWPAN_INTERFACES" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi @hide Allows changing Thread network state and access to Thread network + credentials such as Network Key and PSKc. + <p>Not for use by third-party applications. + @FlaggedApi("com.android.net.thread.flags.thread_enabled") --> + <permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED" + android:protectionLevel="signature|privileged" /> + <!-- #SystemApi @hide Allows an app to bypass Private DNS. <p>Not for use by third-party applications. TODO: publish as system API in next API release. --> diff --git a/core/res/OWNERS b/core/res/OWNERS index 0df7c2047bc1..f24c3f59155a 100644 --- a/core/res/OWNERS +++ b/core/res/OWNERS @@ -1,5 +1,6 @@ adamp@google.com asc@google.com +austindelgado@google.com cinek@google.com dsandler@android.com dsandler@google.com @@ -8,6 +9,7 @@ hackbod@android.com hackbod@google.com ilyamaty@google.com jaggies@google.com +jbolinger@google.com jsharkey@android.com jsharkey@google.com juliacr@google.com diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 6f7bc53e891c..3413282351ff 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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index b05507e7e128..c1018f59ffb5 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -438,6 +438,8 @@ applications that come with the platform <permission name="android.permission.MANAGE_WIFI_NETWORK_SELECTION" /> <!-- Permission needed for CTS test - ConcurrencyTest#testP2pSetWfdInfo --> <permission name="android.permission.CONFIGURE_WIFI_DISPLAY" /> + <!-- Permission required for CTS test - CtsThreadNetworkTestCases --> + <permission name="android.permission.THREAD_NETWORK_PRIVILEGED"/> <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest --> <permission name="android.permission.BIND_CARRIER_SERVICES"/> <!-- Permission required for CTS test - MusicRecognitionManagerTest --> diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java index b7ea04fdfe07..2beb434566e5 100644 --- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java +++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java @@ -51,7 +51,7 @@ public class AndroidKeyStoreMaintenance { * @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 @@ public class AndroidKeyStoreMaintenance { } /** + * 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 @@ public class AndroidKeyStoreMaintenance { } /** + * 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/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index b7140355e489..231fa4837441 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -1282,15 +1282,18 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * 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/Properties.cpp b/libs/hwui/Properties.cpp index 06aed63d8def..09d3f05935b9 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -35,7 +35,7 @@ namespace uirenderer { #ifndef __ANDROID__ // Layoutlib does not compile HWUIProperties.sysprop as it depends on cutils properties std::optional<bool> use_vulkan() { - return base::GetBoolProperty("ro.hwui.use_vulkan", false); + return base::GetBoolProperty("ro.hwui.use_vulkan", true); } std::optional<std::int32_t> render_ahead() { diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 8394c3cd4175..ea9b6c90a65f 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -588,10 +588,40 @@ void SkiaCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Pain // 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 @@ void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float s 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 b785989f35cb..9cb50ed5b081 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -223,6 +223,8 @@ private: 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/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 504dfaa2a1f5..cedfaed3260a 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -919,6 +919,7 @@ void CanvasContext::buildLayer(RenderNode* node) { // buildLayer() will leave the tree in an unknown state, so we must stop drawing stopDrawing(); + ScopedActiveContext activeContext(this); TreeInfo info(TreeInfo::MODE_FULL, *this); info.damageAccumulator = &mDamageAccumulator; info.layerUpdateQueue = &mLayerUpdateQueue; diff --git a/media/Android.bp b/media/Android.bp index f69dd3cc3206..349340804f1e 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -23,6 +23,10 @@ aidl_interface { name: "soundtrigger_middleware-aidl", unstable: true, local_include_dir: "aidl", + defaults: [ + "latest_android_media_audio_common_types_import_interface", + "latest_android_media_soundtrigger_types_import_interface", + ], backend: { java: { sdk_version: "module_current", @@ -32,8 +36,6 @@ aidl_interface { "aidl/android/media/soundtrigger_middleware/*.aidl", ], imports: [ - "android.media.audio.common.types-V2", - "android.media.soundtrigger.types-V1", "media_permission-aidl", ], } diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 7faa13c80c62..447d3bbddceb 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -1176,6 +1176,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, 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 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, // 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/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index ee9883b0b0af..1edb89c82065 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -588,6 +588,9 @@ <!-- Permission needed for CTS test - ConcurrencyTest#testP2pSetWfdInfo --> <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" /> + <!-- Permission required for CTS test - CtsThreadNetworkTestCases --> + <uses-permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED"/> + <!-- Permission required for CTS tests to enable/disable rate limiting toasts. --> <uses-permission android:name="android.permission.MANAGE_TOAST_RATE_LIMITING" /> diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index 34545cf190f3..967a36b38090 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -27,6 +27,8 @@ cameronyee@google.com chandruis@google.com chrisgollner@google.com cinek@google.com +cocod@google.com +darrellshi@google.com dupin@google.com ethibodeau@google.com evanlaird@google.com @@ -80,6 +82,7 @@ petrcermak@google.com pinyaoting@google.com pixel@google.com pomini@google.com +princedonkor@google.com rahulbanerjee@google.com roosa@google.com saff@google.com @@ -102,6 +105,7 @@ vanjan@google.com victortulias@google.com winsonc@google.com wleshner@google.com +wxyz@google.com xilei@google.com xuqiu@google.com yeinj@google.com diff --git a/services/Android.bp b/services/Android.bp index 3a0428e0a9f8..0f6b9845b4aa 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -58,6 +58,7 @@ system_optimized_java_defaults { optimize: false, shrink: true, ignore_warnings: false, + proguard_compatibility: false, proguard_flags_files: [ "proguard.flags", // Ensure classes referenced in the framework-res manifest diff --git a/services/companion/java/com/android/server/companion/virtual/OWNERS b/services/companion/java/com/android/server/companion/virtual/OWNERS index 83143a431406..5295ec82e3c3 100644 --- a/services/companion/java/com/android/server/companion/virtual/OWNERS +++ b/services/companion/java/com/android/server/companion/virtual/OWNERS @@ -1,3 +1,5 @@ +# Bug component: 1171888 + set noparent ogunwale@google.com diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index a71966b916c9..e92f043457f5 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -180,14 +180,7 @@ option java_package com.android.server # Snapshot rebuild instance 3131 pm_snapshot_rebuild (build_time|1|3),(lifetime|1|3) # Caller information to clear application data -1003160 pm_clear_app_data_caller (pid|1),(uid|1),(package|3) -# --------------------------- -# Installer.java -# --------------------------- -# Caller Information to clear application data -1003200 installer_clear_app_data_caller (pid|1),(uid|1),(package|3),(flags|1) -# Call stack to clear application data -1003201 installer_clear_app_data_call_stack (method|3),(class|3),(file|3),(line|1) +3132 pm_clear_app_data_caller (pid|1),(uid|1),(package|3) # --------------------------- # InputMethodManagerService.java @@ -227,6 +220,14 @@ option java_package com.android.server 35000 auto_brightness_adj (old_lux|5),(old_brightness|5),(new_lux|5),(new_brightness|5) # --------------------------- +# Installer.java +# --------------------------- +# Caller Information to clear application data +39000 installer_clear_app_data_caller (pid|1),(uid|1),(package|3),(flags|1) +# Call stack to clear application data +39001 installer_clear_app_data_call_stack (method|3),(class|3),(file|3),(line|1) + +# --------------------------- # ConnectivityService.java # --------------------------- # Connectivity state changed diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java index 0d423d8a0a62..2ba3a1d751d0 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.content.pm.UserInfo; 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 @@ public class VpnManagerService extends IVpnManager.Stub { 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/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index d56448d43e43..469582d06018 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 dc6f8584006e..7dac24104029 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.DumpUtils; 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub 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/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index 931914f17729..2aed8476d031 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -131,4 +131,4 @@ option java_package com.android.server.am 30110 am_intent_sender_redirect_user (userId|1|5) # Caller information to clear application data -1030002 am_clear_app_data_caller (pid|1),(uid|1),(package|3) +30120 am_clear_app_data_caller (pid|1),(uid|1),(package|3) diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index 969dd60a8012..18f24db8b307 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -37,10 +37,9 @@ import android.media.ISpatializerHeadTrackingCallback; import android.media.ISpatializerHeadTrackingModeCallback; import android.media.ISpatializerOutputCallback; import android.media.MediaMetrics; -import android.media.SpatializationLevel; -import android.media.SpatializationMode; import android.media.Spatializer; -import android.media.SpatializerHeadTrackingMode; +import android.media.audio.common.HeadTracking; +import android.media.audio.common.Spatialization; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.text.TextUtils; @@ -82,22 +81,22 @@ public class SpatializerHelper { /*package*/ static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(14) { { - append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL); - append(AudioDeviceInfo.TYPE_WIRED_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL); - append(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, SpatializationMode.SPATIALIZER_BINAURAL); + append(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, Spatialization.Mode.TRANSAURAL); + append(AudioDeviceInfo.TYPE_WIRED_HEADSET, Spatialization.Mode.BINAURAL); + append(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, Spatialization.Mode.BINAURAL); // assumption for A2DP: mostly headsets - append(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, SpatializationMode.SPATIALIZER_BINAURAL); - append(AudioDeviceInfo.TYPE_DOCK, SpatializationMode.SPATIALIZER_TRANSAURAL); - append(AudioDeviceInfo.TYPE_USB_ACCESSORY, SpatializationMode.SPATIALIZER_TRANSAURAL); - append(AudioDeviceInfo.TYPE_USB_DEVICE, SpatializationMode.SPATIALIZER_TRANSAURAL); - append(AudioDeviceInfo.TYPE_USB_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL); - append(AudioDeviceInfo.TYPE_LINE_ANALOG, SpatializationMode.SPATIALIZER_TRANSAURAL); - append(AudioDeviceInfo.TYPE_LINE_DIGITAL, SpatializationMode.SPATIALIZER_TRANSAURAL); - append(AudioDeviceInfo.TYPE_AUX_LINE, SpatializationMode.SPATIALIZER_TRANSAURAL); - append(AudioDeviceInfo.TYPE_BLE_HEADSET, SpatializationMode.SPATIALIZER_BINAURAL); - append(AudioDeviceInfo.TYPE_BLE_SPEAKER, SpatializationMode.SPATIALIZER_TRANSAURAL); + append(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, Spatialization.Mode.BINAURAL); + append(AudioDeviceInfo.TYPE_DOCK, Spatialization.Mode.TRANSAURAL); + append(AudioDeviceInfo.TYPE_USB_ACCESSORY, Spatialization.Mode.TRANSAURAL); + append(AudioDeviceInfo.TYPE_USB_DEVICE, Spatialization.Mode.TRANSAURAL); + append(AudioDeviceInfo.TYPE_USB_HEADSET, Spatialization.Mode.BINAURAL); + append(AudioDeviceInfo.TYPE_LINE_ANALOG, Spatialization.Mode.TRANSAURAL); + append(AudioDeviceInfo.TYPE_LINE_DIGITAL, Spatialization.Mode.TRANSAURAL); + append(AudioDeviceInfo.TYPE_AUX_LINE, Spatialization.Mode.TRANSAURAL); + append(AudioDeviceInfo.TYPE_BLE_HEADSET, Spatialization.Mode.BINAURAL); + append(AudioDeviceInfo.TYPE_BLE_SPEAKER, Spatialization.Mode.TRANSAURAL); // assumption that BLE broadcast would be mostly consumed on headsets - append(AudioDeviceInfo.TYPE_BLE_BROADCAST, SpatializationMode.SPATIALIZER_BINAURAL); + append(AudioDeviceInfo.TYPE_BLE_BROADCAST, Spatialization.Mode.BINAURAL); } }; @@ -224,12 +223,12 @@ public class SpatializerHelper { ArrayList<Integer> list = new ArrayList<>(0); for (byte value : values) { switch (value) { - case SpatializerHeadTrackingMode.OTHER: - case SpatializerHeadTrackingMode.DISABLED: + case HeadTracking.Mode.OTHER: + case HeadTracking.Mode.DISABLED: // not expected here, skip break; - case SpatializerHeadTrackingMode.RELATIVE_WORLD: - case SpatializerHeadTrackingMode.RELATIVE_SCREEN: + case HeadTracking.Mode.RELATIVE_WORLD: + case HeadTracking.Mode.RELATIVE_SCREEN: list.add(headTrackingModeTypeToSpatializerInt(value)); break; default: @@ -252,10 +251,10 @@ public class SpatializerHelper { byte[] spatModes = spat.getSupportedModes(); for (byte mode : spatModes) { switch (mode) { - case SpatializationMode.SPATIALIZER_BINAURAL: + case Spatialization.Mode.BINAURAL: mBinauralSupported = true; break; - case SpatializationMode.SPATIALIZER_TRANSAURAL: + case Spatialization.Mode.TRANSAURAL: mTransauralSupported = true; break; default: @@ -272,8 +271,8 @@ public class SpatializerHelper { // initialize list of compatible devices for (int i = 0; i < SPAT_MODE_FOR_DEVICE_TYPE.size(); i++) { int mode = SPAT_MODE_FOR_DEVICE_TYPE.valueAt(i); - if ((mode == (int) SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported) - || (mode == (int) SpatializationMode.SPATIALIZER_TRANSAURAL + if ((mode == (int) Spatialization.Mode.BINAURAL && mBinauralSupported) + || (mode == (int) Spatialization.Mode.TRANSAURAL && mTransauralSupported)) { mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i)); } @@ -576,9 +575,9 @@ public class SpatializerHelper { int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(device.getDeviceType(), Integer.MIN_VALUE); - device.setSAEnabled(spatMode == SpatializationMode.SPATIALIZER_BINAURAL + device.setSAEnabled(spatMode == Spatialization.Mode.BINAURAL ? mBinauralEnabledDefault - : spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL + : spatMode == Spatialization.Mode.TRANSAURAL ? mTransauralEnabledDefault : false); device.setHeadTrackerEnabled(mHeadTrackingEnabledDefault); @@ -628,9 +627,9 @@ public class SpatializerHelper { if (isBluetoothDevice(internalDeviceType)) return deviceType; final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE); - if (spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL) { + if (spatMode == Spatialization.Mode.TRANSAURAL) { return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; - } else if (spatMode == SpatializationMode.SPATIALIZER_BINAURAL) { + } else if (spatMode == Spatialization.Mode.BINAURAL) { return AudioDeviceInfo.TYPE_WIRED_HEADPHONES; } return AudioDeviceInfo.TYPE_UNKNOWN; @@ -755,8 +754,8 @@ public class SpatializerHelper { // not be included. final byte modeForDevice = (byte) SPAT_MODE_FOR_DEVICE_TYPE.get(ada.getType(), /*default when type not found*/ -1); - if ((modeForDevice == SpatializationMode.SPATIALIZER_BINAURAL && mBinauralSupported) - || (modeForDevice == SpatializationMode.SPATIALIZER_TRANSAURAL + if ((modeForDevice == Spatialization.Mode.BINAURAL && mBinauralSupported) + || (modeForDevice == Spatialization.Mode.TRANSAURAL && mTransauralSupported)) { return true; } @@ -1430,7 +1429,7 @@ public class SpatializerHelper { } synchronized void onInitSensors() { - final boolean init = mFeatureEnabled && (mSpatLevel != SpatializationLevel.NONE); + final boolean init = mFeatureEnabled && (mSpatLevel != Spatialization.Level.NONE); final String action = init ? "initializing" : "releasing"; if (mSpat == null) { logloge("not " + action + " sensors, null spatializer"); @@ -1496,13 +1495,13 @@ public class SpatializerHelper { // SDK <-> AIDL converters private static int headTrackingModeTypeToSpatializerInt(byte mode) { switch (mode) { - case SpatializerHeadTrackingMode.OTHER: + case HeadTracking.Mode.OTHER: return Spatializer.HEAD_TRACKING_MODE_OTHER; - case SpatializerHeadTrackingMode.DISABLED: + case HeadTracking.Mode.DISABLED: return Spatializer.HEAD_TRACKING_MODE_DISABLED; - case SpatializerHeadTrackingMode.RELATIVE_WORLD: + case HeadTracking.Mode.RELATIVE_WORLD: return Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD; - case SpatializerHeadTrackingMode.RELATIVE_SCREEN: + case HeadTracking.Mode.RELATIVE_SCREEN: return Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE; default: throw (new IllegalArgumentException("Unexpected head tracking mode:" + mode)); @@ -1512,13 +1511,13 @@ public class SpatializerHelper { private static byte spatializerIntToHeadTrackingModeType(int sdkMode) { switch (sdkMode) { case Spatializer.HEAD_TRACKING_MODE_OTHER: - return SpatializerHeadTrackingMode.OTHER; + return HeadTracking.Mode.OTHER; case Spatializer.HEAD_TRACKING_MODE_DISABLED: - return SpatializerHeadTrackingMode.DISABLED; + return HeadTracking.Mode.DISABLED; case Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD: - return SpatializerHeadTrackingMode.RELATIVE_WORLD; + return HeadTracking.Mode.RELATIVE_WORLD; case Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE: - return SpatializerHeadTrackingMode.RELATIVE_SCREEN; + return HeadTracking.Mode.RELATIVE_SCREEN; default: throw (new IllegalArgumentException("Unexpected head tracking mode:" + sdkMode)); } @@ -1526,11 +1525,11 @@ public class SpatializerHelper { private static int spatializationLevelToSpatializerInt(byte level) { switch (level) { - case SpatializationLevel.NONE: + case Spatialization.Level.NONE: return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; - case SpatializationLevel.SPATIALIZER_MULTICHANNEL: + case Spatialization.Level.MULTICHANNEL: return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL; - case SpatializationLevel.SPATIALIZER_MCHAN_BED_PLUS_OBJECTS: + case Spatialization.Level.BED_PLUS_OBJECTS: return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MCHAN_BED_PLUS_OBJECTS; default: throw (new IllegalArgumentException("Unexpected spatializer level:" + level)); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 53fbe8f37046..aef224843b2f 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -22,7 +22,6 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNREACHABLE; import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN; import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_AUTO; @@ -45,12 +44,10 @@ import android.app.AppOpsManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -113,7 +110,6 @@ import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.CancellationSignal; -import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; import android.os.INetworkManagementService; @@ -152,7 +148,6 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; -import com.android.modules.utils.build.SdkLevel; import com.android.net.module.util.BinderUtils; import com.android.net.module.util.LinkPropertiesUtils; import com.android.net.module.util.NetdUtils; @@ -202,7 +197,6 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; /** * @hide @@ -1063,8 +1057,6 @@ public class Vpn { // Store mPackage since it might be reset or might be replaced with the other VPN app. final String oldPackage = mPackage; final boolean isPackageChanged = !Objects.equals(packageName, oldPackage); - // TODO: Remove "SdkLevel.isAtLeastT()" check once VpnManagerService is decoupled from - // ConnectivityServiceTest. // Only notify VPN apps that were already always-on, and only if the always-on provider // changed, or the lockdown mode changed. final boolean shouldNotifyOldPkg = isVpnApp(oldPackage) && mAlwaysOn @@ -1078,12 +1070,6 @@ public class Vpn { saveAlwaysOnPackage(); - // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from - // ConnectivityServiceTest. - if (!SdkLevel.isAtLeastT()) { - return true; - } - if (shouldNotifyOldPkg) { // If both of shouldNotifyOldPkg & isPackageChanged are true, that means the // always-on of old package is disabled or the old package is replaced with the new @@ -1984,9 +1970,7 @@ public class Vpn { for (String app : packageNames) { int uid = getAppUid(mContext, app, userId); if (uid != -1) uids.add(uid); - // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from - // ConnectivityServiceTest. - if (Process.isApplicationUid(uid) && SdkLevel.isAtLeastT()) { + if (Process.isApplicationUid(uid)) { uids.add(Process.toSdkSandboxUid(uid)); } } @@ -2297,15 +2281,6 @@ public class Vpn { private INetworkManagementEventObserver mObserver = new BaseNetworkObserver() { @Override - public void interfaceStatusChanged(String interfaze, boolean up) { - synchronized (Vpn.this) { - if (!up && mVpnRunner != null && mVpnRunner instanceof LegacyVpnRunner) { - ((LegacyVpnRunner) mVpnRunner).exitIfOuterInterfaceIs(interfaze); - } - } - } - - @Override public void interfaceRemoved(String interfaze) { synchronized (Vpn.this) { if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) { @@ -2556,17 +2531,6 @@ public class Vpn { private native boolean jniAddAddress(String interfaze, String address, int prefixLen); private native boolean jniDelAddress(String interfaze, String address, int prefixLen); - private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) { - for (RouteInfo route : prop.getAllRoutes()) { - // Currently legacy VPN only works on IPv4. - if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) { - return route; - } - } - - throw new IllegalStateException("Unable to find IPv4 default gateway"); - } - private void enforceNotRestrictedUser() { final long token = Binder.clearCallingIdentity(); try { @@ -2585,15 +2549,14 @@ public class Vpn { * secondary thread to perform connection work, returning quickly. * * Should only be called to respond to Binder requests as this enforces caller permission. Use - * {@link #startLegacyVpnPrivileged(VpnProfile, Network, LinkProperties)} to skip the + * {@link #startLegacyVpnPrivileged(VpnProfile)} to skip the * permission check only when the caller is trusted (or the call is initiated by the system). */ - public void startLegacyVpn(VpnProfile profile, @Nullable Network underlying, - LinkProperties egress) { + public void startLegacyVpn(VpnProfile profile) { enforceControlPermission(); final long token = Binder.clearCallingIdentity(); try { - startLegacyVpnPrivileged(profile, underlying, egress); + startLegacyVpnPrivileged(profile); } finally { Binder.restoreCallingIdentity(token); } @@ -2652,23 +2615,19 @@ public class Vpn { } /** - * Like {@link #startLegacyVpn(VpnProfile, Network, LinkProperties)}, but does not - * check permissions under the assumption that the caller is the system. + * Like {@link #startLegacyVpn(VpnProfile)}, but does not check permissions under + * the assumption that the caller is the system. * * Callers are responsible for checking permissions if needed. */ - public void startLegacyVpnPrivileged(VpnProfile profile, - @Nullable Network underlying, @NonNull LinkProperties egress) { + public void startLegacyVpnPrivileged(VpnProfile profileToStart) { + final VpnProfile profile = profileToStart.clone(); UserInfo user = mUserManager.getUserInfo(mUserId); if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, new UserHandle(mUserId))) { throw new SecurityException("Restricted users cannot establish VPNs"); } - final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress); - final String gateway = ipv4DefaultRoute.getGateway().getHostAddress(); - final String iface = ipv4DefaultRoute.getInterface(); - // Load certificates. String privateKey = ""; String userCert = ""; @@ -2700,8 +2659,6 @@ public class Vpn { throw new IllegalStateException("Cannot load credentials"); } - // Prepare arguments for racoon. - String[] racoon = null; switch (profile.type) { case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // Secret key is still just the alias (not the actual private key). The private key @@ -2731,109 +2688,9 @@ public class Vpn { // profile. startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN); return; - case VpnProfile.TYPE_L2TP_IPSEC_PSK: - racoon = new String[] { - iface, profile.server, "udppsk", profile.ipsecIdentifier, - profile.ipsecSecret, "1701", - }; - break; - case VpnProfile.TYPE_L2TP_IPSEC_RSA: - racoon = new String[] { - iface, profile.server, "udprsa", makeKeystoreEngineGrantString(privateKey), - userCert, caCert, serverCert, "1701", - }; - break; - case VpnProfile.TYPE_IPSEC_XAUTH_PSK: - racoon = new String[] { - iface, profile.server, "xauthpsk", profile.ipsecIdentifier, - profile.ipsecSecret, profile.username, profile.password, "", gateway, - }; - break; - case VpnProfile.TYPE_IPSEC_XAUTH_RSA: - racoon = new String[] { - iface, profile.server, "xauthrsa", makeKeystoreEngineGrantString(privateKey), - userCert, caCert, serverCert, profile.username, profile.password, "", gateway, - }; - break; - case VpnProfile.TYPE_IPSEC_HYBRID_RSA: - racoon = new String[] { - iface, profile.server, "hybridrsa", - caCert, serverCert, profile.username, profile.password, "", gateway, - }; - break; } - // Prepare arguments for mtpd. MTU/MRU calculated conservatively. Only IPv4 supported - // because LegacyVpn. - // 1500 - 60 (Carrier-internal IPv6 + UDP + GTP) - 10 (PPP) - 16 (L2TP) - 8 (UDP) - // - 77 (IPsec w/ SHA-2 512, 256b trunc-len, AES-CBC) - 8 (UDP encap) - 20 (IPv4) - // - 28 (464xlat) - String[] mtpd = null; - switch (profile.type) { - case VpnProfile.TYPE_PPTP: - mtpd = new String[] { - iface, "pptp", profile.server, "1723", - "name", profile.username, "password", profile.password, - "linkname", "vpn", "refuse-eap", "nodefaultroute", - "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270", - (profile.mppe ? "+mppe" : "nomppe"), - }; - if (profile.mppe) { - // Disallow PAP authentication when MPPE is requested, as MPPE cannot work - // with PAP anyway, and users may not expect PAP (plain text) to be used when - // MPPE was requested. - mtpd = Arrays.copyOf(mtpd, mtpd.length + 1); - mtpd[mtpd.length - 1] = "-pap"; - } - break; - case VpnProfile.TYPE_L2TP_IPSEC_PSK: - case VpnProfile.TYPE_L2TP_IPSEC_RSA: - mtpd = new String[] { - iface, "l2tp", profile.server, "1701", profile.l2tpSecret, - "name", profile.username, "password", profile.password, - "linkname", "vpn", "refuse-eap", "nodefaultroute", - "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270", - }; - break; - } - - VpnConfig config = new VpnConfig(); - config.legacy = true; - config.user = profile.key; - config.interfaze = iface; - config.session = profile.name; - config.isMetered = false; - config.proxyInfo = profile.proxy; - if (underlying != null) { - config.underlyingNetworks = new Network[] { underlying }; - } - - config.addLegacyRoutes(profile.routes); - if (!profile.dnsServers.isEmpty()) { - config.dnsServers = Arrays.asList(profile.dnsServers.split(" +")); - } - if (!profile.searchDomains.isEmpty()) { - config.searchDomains = Arrays.asList(profile.searchDomains.split(" +")); - } - startLegacyVpn(config, racoon, mtpd, profile); - } - - private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd, - VpnProfile profile) { - stopVpnRunnerPrivileged(); - - // Prepare for the new request. - prepareInternal(VpnConfig.LEGACY_VPN); - updateState(DetailedState.CONNECTING, "startLegacyVpn"); - - // Start a new LegacyVpnRunner and we are done! - mVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile); - startLegacyVpnRunner(); - } - - @VisibleForTesting - protected void startLegacyVpnRunner() { - mVpnRunner.start(); + throw new UnsupportedOperationException("Legacy VPN is deprecated"); } /** @@ -2851,17 +2708,7 @@ public class Vpn { return; } - final boolean isLegacyVpn = mVpnRunner instanceof LegacyVpnRunner; mVpnRunner.exit(); - - // LegacyVpn uses daemons that must be shut down before new ones are brought up. - // The same limitation does not apply to Platform VPNs. - if (isLegacyVpn) { - synchronized (LegacyVpnRunner.TAG) { - // wait for old thread to completely finish before spinning up - // new instance, otherwise state updates can be out of order. - } - } } /** @@ -3537,6 +3384,13 @@ public class Vpn { * given network to start a new IKE session. */ private void startOrMigrateIkeSession(@Nullable Network underlyingNetwork) { + synchronized (Vpn.this) { + // Ignore stale runner. + if (mVpnRunner != this) return; + setVpnNetworkPreference(mSessionKey, + createUserAndRestrictedProfilesRanges(mUserId, + mConfig.allowedApplications, mConfig.disallowedApplications)); + } if (underlyingNetwork == null) { // For null underlyingNetwork case, there will not be a NetworkAgent available so // no underlying network update is necessary here. Note that updating @@ -4057,6 +3911,7 @@ public class Vpn { updateState(DetailedState.FAILED, exception.getMessage()); } + clearVpnNetworkPreference(mSessionKey); disconnectVpnRunner(); } @@ -4143,9 +3998,7 @@ public class Vpn { // Ignore stale runner. if (mVpnRunner != this) return; - // TODO(b/230548427): Remove SDK check once VPN related stuff are - // decoupled from ConnectivityServiceTest. - if (SdkLevel.isAtLeastT() && category != null && isVpnApp(mPackage)) { + if (category != null && isVpnApp(mPackage)) { sendEventToVpnManagerApp(category, errorClass, errorCode, getPackage(), mSessionKey, makeVpnProfileStateLocked(), mActiveNetwork, @@ -4193,6 +4046,13 @@ public class Vpn { } resetIkeState(); + if (errorCode != VpnManager.ERROR_CODE_NETWORK_LOST + // Clear the VPN network preference when the retry delay is higher than 5s. + // mRetryCount was increased when scheduleRetryNewIkeSession() is called, + // therefore use mRetryCount - 1 here. + && mDeps.getNextRetryDelayMs(mRetryCount - 1) > 5_000L) { + clearVpnNetworkPreference(mSessionKey); + } } /** @@ -4239,13 +4099,17 @@ public class Vpn { mCarrierConfigManager.unregisterCarrierConfigChangeListener( mCarrierConfigChangeListener); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); - clearVpnNetworkPreference(mSessionKey); mExecutor.shutdown(); } @Override public void exitVpnRunner() { + // mSessionKey won't be changed since the Ikev2VpnRunner is created, so it's ok to use + // it outside the mExecutor. And clearing the VPN network preference here can prevent + // the case that the VPN network preference isn't cleared when Ikev2VpnRunner became + // stale. + clearVpnNetworkPreference(mSessionKey); try { mExecutor.execute(() -> { disconnectVpnRunner(); @@ -4256,343 +4120,6 @@ public class Vpn { } } - /** - * Bringing up a VPN connection takes time, and that is all this thread - * does. Here we have plenty of time. The only thing we need to take - * care of is responding to interruptions as soon as possible. Otherwise - * requests will pile up. This could be done in a Handler as a state - * machine, but it is much easier to read in the current form. - */ - private class LegacyVpnRunner extends VpnRunner { - private static final String TAG = "LegacyVpnRunner"; - - private final String[] mDaemons; - private final String[][] mArguments; - private final LocalSocket[] mSockets; - private final String mOuterInterface; - private final AtomicInteger mOuterConnection = - new AtomicInteger(ConnectivityManager.TYPE_NONE); - private final VpnProfile mProfile; - - private long mBringupStartTime = -1; - - /** - * Watch for the outer connection (passing in the constructor) going away. - */ - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (!mEnableTeardown) return; - - if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, - ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) { - NetworkInfo info = (NetworkInfo)intent.getExtra( - ConnectivityManager.EXTRA_NETWORK_INFO); - if (info != null && !info.isConnectedOrConnecting()) { - try { - mObserver.interfaceStatusChanged(mOuterInterface, false); - } catch (RemoteException e) {} - } - } - } - } - }; - - // GuardedBy("Vpn.this") (annotation can't be applied to constructor) - LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) { - super(TAG); - if (racoon == null && mtpd == null) { - throw new IllegalArgumentException( - "Arguments to racoon and mtpd must not both be null"); - } - mConfig = config; - mDaemons = new String[] {"racoon", "mtpd"}; - // TODO: clear arguments from memory once launched - mArguments = new String[][] {racoon, mtpd}; - mSockets = new LocalSocket[mDaemons.length]; - - // This is the interface which VPN is running on, - // mConfig.interfaze will change to point to OUR - // internal interface soon. TODO - add inner/outer to mconfig - // TODO - we have a race - if the outer iface goes away/disconnects before we hit this - // we will leave the VPN up. We should check that it's still there/connected after - // registering - mOuterInterface = mConfig.interfaze; - - mProfile = profile; - - if (!TextUtils.isEmpty(mOuterInterface)) { - for (Network network : mConnectivityManager.getAllNetworks()) { - final LinkProperties lp = mConnectivityManager.getLinkProperties(network); - if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) { - final NetworkInfo netInfo = mConnectivityManager.getNetworkInfo(network); - if (netInfo != null) { - mOuterConnection.set(netInfo.getType()); - break; - } - } - } - } - - IntentFilter filter = new IntentFilter(); - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - mContext.registerReceiver(mBroadcastReceiver, filter); - } - - /** - * Checks if the parameter matches the underlying interface - * - * <p>If the underlying interface is torn down, the LegacyVpnRunner also should be. It has - * no ability to migrate between interfaces (or Networks). - */ - public void exitIfOuterInterfaceIs(String interfaze) { - if (interfaze.equals(mOuterInterface)) { - Log.i(TAG, "Legacy VPN is going down with " + interfaze); - exitVpnRunner(); - } - } - - /** Tears down this LegacyVpn connection */ - @Override - public void exitVpnRunner() { - // We assume that everything is reset after stopping the daemons. - interrupt(); - - // Always disconnect. This may be called again in cleanupVpnStateLocked() if - // exitVpnRunner() was called from exit(), but it will be a no-op. - agentDisconnect(); - try { - mContext.unregisterReceiver(mBroadcastReceiver); - } catch (IllegalArgumentException e) {} - } - - @Override - public void run() { - // Wait for the previous thread since it has been interrupted. - Log.v(TAG, "Waiting"); - synchronized (TAG) { - Log.v(TAG, "Executing"); - try { - bringup(); - waitForDaemonsToStop(); - interrupted(); // Clear interrupt flag if execute called exit. - } catch (InterruptedException e) { - } finally { - for (LocalSocket socket : mSockets) { - IoUtils.closeQuietly(socket); - } - // This sleep is necessary for racoon to successfully complete sending delete - // message to server. - try { - Thread.sleep(50); - } catch (InterruptedException e) { - } - for (String daemon : mDaemons) { - mDeps.stopService(daemon); - } - } - agentDisconnect(); - } - } - - private void checkInterruptAndDelay(boolean sleepLonger) throws InterruptedException { - long now = SystemClock.elapsedRealtime(); - if (now - mBringupStartTime <= 60000) { - Thread.sleep(sleepLonger ? 200 : 1); - } else { - updateState(DetailedState.FAILED, "checkpoint"); - throw new IllegalStateException("VPN bringup took too long"); - } - } - - private void checkAndFixupArguments(@NonNull final InetAddress endpointAddress) { - final String endpointAddressString = endpointAddress.getHostAddress(); - // Perform some safety checks before inserting the address in place. - // Position 0 in mDaemons and mArguments must be racoon, and position 1 must be mtpd. - if (!"racoon".equals(mDaemons[0]) || !"mtpd".equals(mDaemons[1])) { - throw new IllegalStateException("Unexpected daemons order"); - } - - // Respectively, the positions at which racoon and mtpd take the server address - // argument are 1 and 2. Not all types of VPN require both daemons however, and - // in that case the corresponding argument array is null. - if (mArguments[0] != null) { - if (!mProfile.server.equals(mArguments[0][1])) { - throw new IllegalStateException("Invalid server argument for racoon"); - } - mArguments[0][1] = endpointAddressString; - } - - if (mArguments[1] != null) { - if (!mProfile.server.equals(mArguments[1][2])) { - throw new IllegalStateException("Invalid server argument for mtpd"); - } - mArguments[1][2] = endpointAddressString; - } - } - - private void bringup() { - // Catch all exceptions so we can clean up a few things. - try { - // resolve never returns null. If it does because of some bug, it will be - // caught by the catch() block below and cleanup gracefully. - final InetAddress endpointAddress = mDeps.resolve(mProfile.server); - - // Big hack : dynamically replace the address of the server in the arguments - // with the resolved address. - checkAndFixupArguments(endpointAddress); - - // Initialize the timer. - mBringupStartTime = SystemClock.elapsedRealtime(); - - // Wait for the daemons to stop. - for (String daemon : mDaemons) { - while (!mDeps.isServiceStopped(daemon)) { - checkInterruptAndDelay(true); - } - } - - // Clear the previous state. - final File state = mDeps.getStateFile(); - state.delete(); - if (state.exists()) { - throw new IllegalStateException("Cannot delete the state"); - } - new File("/data/misc/vpn/abort").delete(); - - updateState(DetailedState.CONNECTING, "execute"); - - // Start the daemon with arguments. - for (int i = 0; i < mDaemons.length; ++i) { - String[] arguments = mArguments[i]; - if (arguments == null) { - continue; - } - - // Start the daemon. - String daemon = mDaemons[i]; - mDeps.startService(daemon); - - // Wait for the daemon to start. - while (!mDeps.isServiceRunning(daemon)) { - checkInterruptAndDelay(true); - } - - // Create the control socket. - mSockets[i] = new LocalSocket(); - - // Wait for the socket to connect and send over the arguments. - mDeps.sendArgumentsToDaemon(daemon, mSockets[i], arguments, - this::checkInterruptAndDelay); - } - - // Wait for the daemons to create the new state. - while (!state.exists()) { - // Check if a running daemon is dead. - for (int i = 0; i < mDaemons.length; ++i) { - String daemon = mDaemons[i]; - if (mArguments[i] != null && !mDeps.isServiceRunning(daemon)) { - throw new IllegalStateException(daemon + " is dead"); - } - } - checkInterruptAndDelay(true); - } - - // Now we are connected. Read and parse the new state. - String[] parameters = FileUtils.readTextFile(state, 0, null).split("\n", -1); - if (parameters.length != 7) { - throw new IllegalStateException("Cannot parse the state: '" - + String.join("', '", parameters) + "'"); - } - - // Set the interface and the addresses in the config. - synchronized (Vpn.this) { - mConfig.interfaze = parameters[0].trim(); - - mConfig.addLegacyAddresses(parameters[1]); - // Set the routes if they are not set in the config. - if (mConfig.routes == null || mConfig.routes.isEmpty()) { - mConfig.addLegacyRoutes(parameters[2]); - } - - // Set the DNS servers if they are not set in the config. - if (mConfig.dnsServers == null || mConfig.dnsServers.size() == 0) { - String dnsServers = parameters[3].trim(); - if (!dnsServers.isEmpty()) { - mConfig.dnsServers = Arrays.asList(dnsServers.split(" ")); - } - } - - // Set the search domains if they are not set in the config. - if (mConfig.searchDomains == null || mConfig.searchDomains.size() == 0) { - String searchDomains = parameters[4].trim(); - if (!searchDomains.isEmpty()) { - mConfig.searchDomains = Arrays.asList(searchDomains.split(" ")); - } - } - - // Add a throw route for the VPN server endpoint, if one was specified. - if (endpointAddress instanceof Inet4Address) { - mConfig.routes.add(new RouteInfo( - new IpPrefix(endpointAddress, 32), null /*gateway*/, - null /*iface*/, RTN_THROW)); - } else if (endpointAddress instanceof Inet6Address) { - mConfig.routes.add(new RouteInfo( - new IpPrefix(endpointAddress, 128), null /*gateway*/, - null /*iface*/, RTN_THROW)); - } else { - Log.e(TAG, "Unknown IP address family for VPN endpoint: " - + endpointAddress); - } - - // Here is the last step and it must be done synchronously. - // Set the start time - mConfig.startTime = SystemClock.elapsedRealtime(); - - // Check if the thread was interrupted while we were waiting on the lock. - checkInterruptAndDelay(false); - - // Check if the interface is gone while we are waiting. - if (!mDeps.isInterfacePresent(Vpn.this, mConfig.interfaze)) { - throw new IllegalStateException(mConfig.interfaze + " is gone"); - } - - // Now INetworkManagementEventObserver is watching our back. - mInterface = mConfig.interfaze; - prepareStatusIntent(); - - agentConnect(); - - Log.i(TAG, "Connected!"); - } - } catch (Exception e) { - Log.i(TAG, "Aborting", e); - updateState(DetailedState.FAILED, e.getMessage()); - exitVpnRunner(); - } - } - - /** - * Check all daemons every two seconds. Return when one of them is stopped. - * The caller will move to the disconnected state when this function returns, - * which can happen if a daemon failed or if the VPN was torn down. - */ - private void waitForDaemonsToStop() throws InterruptedException { - if (!mNetworkInfo.isConnected()) { - return; - } - while (true) { - Thread.sleep(2000); - for (int i = 0; i < mDaemons.length; i++) { - if (mArguments[i] != null && mDeps.isServiceStopped(mDaemons[i])) { - return; - } - } - } - } - } - private void verifyCallingUidAndPackage(String packageName) { mDeps.verifyCallingUidAndPackage(mContext, packageName, mUserId); } @@ -4839,11 +4366,9 @@ public class Vpn { // Build intent first because the sessionKey will be reset after performing // VpnRunner.exit(). Also, cache mOwnerUID even if ownerUID will not be changed in // VpnRunner.exit() to prevent design being changed in the future. - // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from - // ConnectivityServiceTest. final int ownerUid = mOwnerUID; Intent intent = null; - if (SdkLevel.isAtLeastT() && isVpnApp(mPackage)) { + if (isVpnApp(mPackage)) { intent = buildVpnManagerEventIntent( VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER, -1 /* errorClass */, -1 /* errorCode*/, mPackage, @@ -4884,12 +4409,8 @@ public class Vpn { // The underlying network, NetworkCapabilities and LinkProperties are not // necessary to send to VPN app since the purpose of this event is to notify // VPN app that VPN is deactivated by the user. - // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from - // ConnectivityServiceTest. - if (SdkLevel.isAtLeastT()) { - mEventChanges.log("[VMEvent] " + packageName + " stopped"); - sendEventToVpnManagerApp(intent, packageName); - } + mEventChanges.log("[VMEvent] " + packageName + " stopped"); + sendEventToVpnManagerApp(intent, packageName); } private boolean storeAppExclusionList(@NonNull String packageName, diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java index 7045e65a8936..d994849611f8 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 @@ public class HdmiCecNetwork { * 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/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index fa95a348d8d3..ec7f561bd9da 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 @@ public class LockSettingsService extends ILockSettings.Stub { 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.fixUnlockedDeviceRequiredKeys(); // 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 @@ public class LockSettingsService extends ILockSettings.Stub { @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 @@ public class LockSettingsService extends ILockSettings.Stub { } 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. + // + // 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. // - // 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); + // - 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); + } + } 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); } - setString(MIGRATED_SP_CE_ONLY, "true", 0); } mThirdPartyAppsStarted = true; @@ -1067,6 +1102,37 @@ public class LockSettingsService extends ILockSettings.Stub { } } + @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 @@ public class LockSettingsService extends ILockSettings.Stub { 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 @@ public class LockSettingsService extends ILockSettings.Stub { return; } onSyntheticPasswordUnlocked(userId, result.syntheticPassword); + if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) { + unlockKeystore(userId, result.syntheticPassword); + } unlockCeStorage(userId, result.syntheticPassword); } } @@ -2350,6 +2433,16 @@ public class LockSettingsService extends ILockSettings.Stub { } 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 @@ public class LockSettingsService extends ILockSettings.Stub { /** * 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 @@ public class LockSettingsService extends ILockSettings.Stub { 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 @@ public class LockSettingsService extends ILockSettings.Stub { /** * 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 @@ public class LockSettingsService extends ILockSettings.Stub { 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 @@ public class LockSettingsService extends ILockSettings.Stub { 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/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index 8e9c21f5f35f..cc205d4a53bd 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 @@ import java.util.Set; * * - 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 1b7d1ba59b06..9a0b3914122c 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 @@ public class LockdownVpnTracker { // 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 550ad5d610da..681d1a0ee10a 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.app.IBatteryStats; 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub { /** * 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub { } 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/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 8080e4074a17..988a32f3ffc7 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -486,20 +486,30 @@ final class InstallPackageHelper { pkgSetting.setLoadingProgress(1f); } + // TODO: passes the package name as an argument in a message to the handler for V+ + // so we don't need to rely on creating lambda objects so frequently. + if (UpdateOwnershipHelper.hasValidOwnershipDenyList(pkgSetting)) { + mPm.mHandler.post(() -> handleUpdateOwnerDenyList(pkgSetting)); + } + return pkg; + } + + private void handleUpdateOwnerDenyList(PackageSetting pkgSetting) { ArraySet<String> listItems = mUpdateOwnershipHelper.readUpdateOwnerDenyList(pkgSetting); if (listItems != null && !listItems.isEmpty()) { - mUpdateOwnershipHelper.addToUpdateOwnerDenyList(pkgSetting.getPackageName(), listItems); - for (String unownedPackage : listItems) { - PackageSetting unownedSetting = mPm.mSettings.getPackageLPr(unownedPackage); - SystemConfig config = SystemConfig.getInstance(); - if (unownedSetting != null - && config.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) { - unownedSetting.setUpdateOwnerPackage(null); + mUpdateOwnershipHelper.addToUpdateOwnerDenyList(pkgSetting.getPackageName(), + listItems); + SystemConfig config = SystemConfig.getInstance(); + synchronized (mPm.mLock) { + for (String unownedPackage : listItems) { + PackageSetting unownedSetting = mPm.mSettings.getPackageLPr(unownedPackage); + if (unownedSetting != null + && config.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) { + unownedSetting.setUpdateOwnerPackage(null); + } } } } - - return pkg; } /** diff --git a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java index 43752f31a1a2..adac68b25749 100644 --- a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java +++ b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java @@ -48,7 +48,7 @@ public class UpdateOwnershipHelper { private final Object mLock = new Object(); - private static boolean hasValidOwnershipDenyList(PackageSetting pkgSetting) { + static boolean hasValidOwnershipDenyList(PackageSetting pkgSetting) { AndroidPackage pkg = pkgSetting.getPkg(); // we're checking for uses-permission for these priv permissions instead of grant as we're // only considering system apps to begin with, so presumed to be granted. diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 297ad73e054b..c24d5236f4f7 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -1001,7 +1001,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { } synchronized (mLock) { - mAttributions.put(source.getToken(), source); + // Change the token for the AttributionSource we're storing, so that we don't store + // a strong reference to the original token inside the map itself. + mAttributions.put(source.getToken(), source.withDefaultToken()); } } @@ -1009,7 +1011,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { synchronized (mLock) { final AttributionSource cachedSource = mAttributions.get(source.getToken()); if (cachedSource != null) { - return cachedSource.equals(source); + return cachedSource.equalsExceptToken(source); } return false; } diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 0e99e7ee1daa..bd9738e43d41 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -1315,7 +1315,8 @@ public final class PermissionPolicyService extends SystemService { } private boolean isTaskStartedFromLauncher(String currPkg, TaskInfo taskInfo) { - return currPkg.equals(taskInfo.baseActivity.getPackageName()) + return taskInfo.baseActivity != null + && currPkg.equals(taskInfo.baseActivity.getPackageName()) && isLauncherIntent(taskInfo.baseIntent); } diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS index a0e91ad7cf45..94340ec26cba 100644 --- a/services/core/java/com/android/server/power/OWNERS +++ b/services/core/java/com/android/server/power/OWNERS @@ -2,4 +2,6 @@ michaelwr@google.com santoscordon@google.com philipjunker@google.com -per-file ThermalManagerService.java=wvw@google.com +per-file ThermalManagerService.java=file:/THERMAL_OWNERS +per-file LowPowerStandbyController.java=qingxun@google.com +per-file LowPowerStandbyControllerInternal.java=qingxun@google.com diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 76ee84571b04..cc892a03a141 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 @@ public class StatsPullAtomService extends SystemService { final NetworkStats nonTaggedStats = NetworkStatsUtils.fromPublicNetworkStats(queryNonTaggedStats); + queryNonTaggedStats.close(); if (!includeTags) return nonTaggedStats; final android.app.usage.NetworkStats queryTaggedStats = @@ -1429,6 +1430,7 @@ public class StatsPullAtomService extends SystemService { currentTimeInMillis); final NetworkStats taggedStats = NetworkStatsUtils.fromPublicNetworkStats(queryTaggedStats); + queryTaggedStats.close(); return nonTaggedStats.add(taggedStats); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 18a6254ca115..82a954d4178f 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4214,6 +4214,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A Slog.v(TAG_APP, "Keeping entry during removeHistory for activity " + this); } } + if (task != null && task.mKillProcessesOnDestroyed) { + mTaskSupervisor.removeTimeoutOfKillProcessesOnProcessDied(this, task); + } // upgrade transition trigger to task if this is the last activity since it means we are // closing the task. final WindowContainer trigger = remove && task != null && task.getChildCount() == 1 diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index b34ae1930048..ecdca93ccaaa 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -1891,7 +1891,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // DestroyActivityItem may be called first. final ActivityRecord top = task.getTopMostActivity(); if (top != null && top.finishing && !top.mAppStopped && top.lastVisibleTime > 0 - && !task.mKillProcessesOnDestroyed) { + && !task.mKillProcessesOnDestroyed && top.hasProcess()) { task.mKillProcessesOnDestroyed = true; mHandler.sendMessageDelayed( mHandler.obtainMessage(KILL_TASK_PROCESSES_TIMEOUT_MSG, task), @@ -1901,8 +1901,26 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { killTaskProcessesIfPossible(task); } + void removeTimeoutOfKillProcessesOnProcessDied(@NonNull ActivityRecord r, @NonNull Task task) { + if (r.packageName.equals(task.getBasePackageName())) { + task.mKillProcessesOnDestroyed = false; + mHandler.removeMessages(KILL_TASK_PROCESSES_TIMEOUT_MSG, task); + } + } + void killTaskProcessesOnDestroyedIfNeeded(Task task) { if (task == null || !task.mKillProcessesOnDestroyed) return; + final int[] numDestroyingActivities = new int[1]; + task.forAllActivities(r -> { + if (r.finishing && r.lastVisibleTime > 0 && r.attachedToProcess()) { + numDestroyingActivities[0]++; + } + }); + if (numDestroyingActivities[0] > 1) { + // Skip if there are still destroying activities. When the last activity reports + // destroyed, the number will be 1 to proceed the kill. + return; + } mHandler.removeMessages(KILL_TASK_PROCESSES_TIMEOUT_MSG, task); killTaskProcessesIfPossible(task); } @@ -2763,7 +2781,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } break; case KILL_TASK_PROCESSES_TIMEOUT_MSG: { final Task task = (Task) msg.obj; - if (task.mKillProcessesOnDestroyed) { + if (task.mKillProcessesOnDestroyed && task.hasActivity()) { Slog.i(TAG, "Destroy timeout of remove-task, attempt to kill " + task); killTaskProcessesIfPossible(task); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 46ca4455f93c..6d560e4b762b 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4626,7 +4626,7 @@ class Task extends TaskFragment { // Expanding pip into new rotation, so create a rotation leash // until the display is rotated. topActivity.getOrCreateFixedRotationLeash( - topActivity.getSyncTransaction()); + topActivity.getPendingTransaction()); } lastParentBeforePip.moveToFront("movePinnedActivityToOriginalTask"); } diff --git a/services/proguard.flags b/services/proguard.flags index e11e613adb5c..84f2a52a2b22 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,10 +45,6 @@ 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.* { *; } diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java index dc1c6d57dfdb..74d664fe7977 100644 --- a/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java @@ -75,7 +75,7 @@ public class UserBackupManagerServiceTest { private static final String TEST_PACKAGE = "package1"; private static final String[] TEST_PACKAGES = new String[] { TEST_PACKAGE }; private static final String TEST_TRANSPORT = "transport"; - private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 1; + private static final int WORKER_THREAD_TIMEOUT_MILLISECONDS = 100; @Mock Context mContext; @Mock IBackupManagerMonitor mBackupManagerMonitor; diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java index 7cd88196bf1b..d68791589282 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -320,11 +320,20 @@ public final class UserManagerServiceTest { @Test public void testGetBootUser_Headless_ThrowsIfOnlySystemUserExists() throws Exception { setSystemUserHeadless(true); + removeNonSystemUsers(); assertThrows(UserManager.CheckedUserOperationException.class, () -> mUmi.getBootUser(/* waitUntilSet= */ false)); } + private void removeNonSystemUsers() { + for (UserInfo user : mUms.getUsers(true)) { + if (!user.getUserHandle().isSystem()) { + mUms.removeUserInfo(user.id); + } + } + } + private void mockCurrentUser(@UserIdInt int userId) { mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal); diff --git a/services/tests/mockingservicestests/src/com/android/server/power/OWNERS b/services/tests/mockingservicestests/src/com/android/server/power/OWNERS index fb62520ff57b..37396f392551 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 1c33d0de4568..18961c0feef9 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 android.service.gatekeeper.IGateKeeperService; 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 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @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 949f8e7a6ab0..0e881efd4cdf 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 @@ public class LockdownVpnTrackerTest { 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 @@ public class LockdownVpnTrackerTest { // 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 @@ public class LockdownVpnTrackerTest { // 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 2cdfbffda407..13dc12032e7d 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 android.util.ArrayMap; 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 @@ public class NetworkManagementServiceTest { 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 @@ public class NetworkManagementServiceTest { 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 ef4c0bf71cd7..fe93ebbf3d8c 100644 --- a/services/tests/servicestests/src/com/android/server/power/OWNERS +++ b/services/tests/servicestests/src/com/android/server/power/OWNERS @@ -1,3 +1,3 @@ include /services/core/java/com/android/server/power/OWNERS -per-file ThermalManagerServiceTest.java=wvw@google.com, xwxw@google.com
\ No newline at end of file +per-file ThermalManagerServiceTest.java=file:/THERMAL_OWNERS diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 0ccb0d0b2ef5..9e0d69bfdf6e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -1040,14 +1040,25 @@ public class RecentTasksTest extends WindowTestsBase { // If the task has a non-stopped activity, the removal will wait for its onDestroy. final Task task = tasks.get(0); + final ActivityRecord bottom = new ActivityBuilder(mAtm).setTask(task).build(); final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).build(); - top.lastVisibleTime = 123; + bottom.lastVisibleTime = top.lastVisibleTime = 123; top.setState(ActivityRecord.State.RESUMED, "test"); mRecentTasks.removeTasksByPackageName(task.getBasePackageName(), TEST_USER_0_ID); assertTrue(task.mKillProcessesOnDestroyed); top.setState(ActivityRecord.State.DESTROYING, "test"); + bottom.destroyed("test"); + assertTrue("Wait for all destroyed", task.mKillProcessesOnDestroyed); top.destroyed("test"); - assertFalse(task.mKillProcessesOnDestroyed); + assertFalse("Consume kill", task.mKillProcessesOnDestroyed); + + // If the process is died, the state should be cleared. + final Task lastTask = tasks.get(0); + lastTask.intent.setComponent(top.mActivityComponent); + lastTask.addChild(top); + lastTask.mKillProcessesOnDestroyed = true; + top.handleAppDied(); + assertFalse(lastTask.mKillProcessesOnDestroyed); } @Test diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java index 216f45acd5bd..d722f2f1aa0c 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java @@ -279,6 +279,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection, mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection, Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY | Context.BIND_SCHEDULE_LIKE_TOP_APP + | Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser)); } diff --git a/telephony/OWNERS b/telephony/OWNERS index 3158ad8fc58e..287aa653ef9a 100644 --- a/telephony/OWNERS +++ b/telephony/OWNERS @@ -7,10 +7,12 @@ rgreenwalt@google.com tgunn@google.com huiwang@google.com jayachandranc@google.com -chinmayd@google.com amruthr@google.com sasindran@google.com # Requiring TL ownership for new carrier config keys. per-file CarrierConfigManager.java=set noparent per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com + +#Domain Selection is jointly owned, add additional owners for domain selection specific files +per-file TransportSelectorCallback.java,WwanSelectorCallback.java,DomainSelectionService.java,DomainSelectionService.aidl,DomainSelector.java,EmergencyRegResult.java,EmergencyRegResult.aidl,IDomainSelectionServiceController.aidl,IDomainSelector.aidl,ITransportSelectorCallback.aidl,ITransportSelectorResultCallback.aidl,IWwanSelectorCallback.aidl,IWwanSelectorResultCallback.aidl=hwangoo@google.com,forestchoi@google.com,avinashmp@google.com,mkoon@google.com,seheele@google.com,radhikaagrawal@google.com diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt index f61cce666cca..daaab3314679 100644 --- a/test-mock/api/current.txt +++ b/test-mock/api/current.txt @@ -156,6 +156,7 @@ package android.test.mock { method @Deprecated public int getInt(int); method @Deprecated public long getLong(int); method @Deprecated public android.net.Uri getNotificationUri(); + method @Deprecated public java.util.List<android.net.Uri> getNotificationUris(); method @Deprecated public int getPosition(); method @Deprecated public short getShort(int); method @Deprecated public String getString(int); @@ -179,6 +180,7 @@ package android.test.mock { method @Deprecated public android.os.Bundle respond(android.os.Bundle); method @Deprecated public void setExtras(android.os.Bundle); method @Deprecated public void setNotificationUri(android.content.ContentResolver, android.net.Uri); + method @Deprecated public void setNotificationUris(android.content.ContentResolver, java.util.List<android.net.Uri>); method @Deprecated public void unregisterContentObserver(android.database.ContentObserver); method @Deprecated public void unregisterDataSetObserver(android.database.DataSetObserver); } diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index ed3e1ac5c39f..40cba3ed316f 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -119,6 +119,7 @@ cc_library_host_static { "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 f00be3688597..1fb5cc0919a3 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -2513,6 +2513,28 @@ int LinkCommand::Action(const std::vector<std::string>& args) { } } + // 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 a08f385b2270..26713fd92264 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 @@ struct LinkOptions { 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 a92f24b82547..678d84628015 100644 --- a/tools/aapt2/cmd/Util.cpp +++ b/tools/aapt2/cmd/Util.cpp @@ -113,6 +113,56 @@ std::unique_ptr<IConfigFilter> ParseConfigFilterParameters(const std::vector<std 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 712c07b71695..9ece5dd4d720 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 @@ bool ParseSplitParameter(android::StringPiece arg, android::IDiagnostics* diag, 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 139bfbcd0f41..723d87ed0af3 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 @@ TEST (UtilTest, ParseSplitParameters) { 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 000000000000..fdf3f74d4e18 --- /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 000000000000..1d342a71b996 --- /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 000000000000..53086cc30f18 --- /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/fonts/update_font_metadata.py b/tools/fonts/update_font_metadata.py index c07a98a1e3d2..04a552886d42 100755 --- a/tools/fonts/update_font_metadata.py +++ b/tools/fonts/update_font_metadata.py @@ -19,7 +19,7 @@ def main(): args_parser.add_argument('--revision', help='Updated font revision. Use + to update revision based on the current revision') args = args_parser.parse_args() - font = ttLib.TTFont(args.input) + font = ttLib.TTFont(args.input, recalcTimestamp=False) update_font_revision(font, args.revision) font.save(args.output) diff --git a/tools/lint/README.md b/tools/lint/README.md index b235ad60c799..ff8e44229189 100644 --- a/tools/lint/README.md +++ b/tools/lint/README.md @@ -103,10 +103,15 @@ out/soong/.intermediates/frameworks/base/services/autofill/services.autofill/and 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 d41fee3fc0dc..24d203fd1116 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 com.intellij.psi.PsiReferenceList 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 - } - } - } - return null + val containingStub = containingStub(context, node) ?: return null + val superMethod = node.findSuperMethods(containingStub) + if (superMethod.isEmpty()) return null + return containingStub.containingClass?.name } -fun isContainedInSubclassOfStub(context: JavaContext, node: UMethod?): Boolean { +/* 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 true + if (isStub(context, superClass)) return superClass superClass = superClass.superClass } - return false + return null } -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 935badecf8d5..624a1987638e 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.IssueRegistry 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 @@ class AndroidFrameworkIssueRegistry : IssueRegistry() { 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 000000000000..6b50cfd9e5ab --- /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 000000000000..bce848a2e3a7 --- /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 83b8f163abee..4455a9cda3a8 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 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { 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 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { 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 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner { /* 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 d8afcb977594..2afca05f8130 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 @@ class EnforcePermissionDetectorTest : LintDetectorTest() { """.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( """ |