summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp43
-rw-r--r--Android.bp205
-rw-r--r--BAL_OWNERS5
-rw-r--r--INPUT_OWNERS2
-rw-r--r--ProtoLibraries.bp36
-rw-r--r--apex/jobscheduler/service/Android.bp4
-rw-r--r--api/Android.bp150
-rw-r--r--api/ApiDocs.bp150
-rw-r--r--api/StubLibraries.bp89
-rw-r--r--api/api.go8
-rwxr-xr-xapi/gen_combined_removed_dex.sh2
-rw-r--r--cmds/svc/src/com/android/commands/svc/NfcCommand.java36
-rw-r--r--cmds/svc/src/com/android/commands/svc/OWNERS2
-rw-r--r--core/api/Android.bp5
-rw-r--r--core/api/current.txt105
-rw-r--r--core/api/lint-baseline.txt7
-rw-r--r--core/api/module-lib-current.txt4
-rw-r--r--core/api/module-lib-lint-baseline.txt8
-rw-r--r--core/api/module-lib-removed.txt2
-rw-r--r--core/api/removed.txt2
-rw-r--r--core/api/system-current.txt59
-rw-r--r--core/api/system-lint-baseline.txt9
-rw-r--r--core/api/system-removed.txt2
-rw-r--r--core/api/test-current.txt10
-rw-r--r--core/api/test-lint-baseline.txt148
-rw-r--r--core/api/test-removed.txt2
-rw-r--r--core/java/Android.bp12
-rw-r--r--core/java/android/app/Activity.java3
-rw-r--r--core/java/android/app/KeyguardManager.java20
-rw-r--r--core/java/android/app/LoadedApk.java11
-rw-r--r--core/java/android/app/OWNERS1
-rw-r--r--core/java/android/app/SystemServiceRegistry.java9
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java5
-rw-r--r--core/java/android/app/contentsuggestions/OWNERS7
-rw-r--r--core/java/android/content/AttributionSource.java31
-rw-r--r--core/java/android/content/Context.java2
-rw-r--r--core/java/android/content/Intent.java5
-rw-r--r--core/java/android/content/pm/PackageManager.java15
-rw-r--r--core/java/android/hardware/HardwareBuffer.aidl2
-rw-r--r--core/java/android/hardware/biometrics/BiometricConstants.java12
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java67
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java6
-rw-r--r--core/java/android/hardware/biometrics/CryptoObject.java44
-rw-r--r--core/java/android/hardware/biometrics/IAuthService.aidl3
-rw-r--r--core/java/android/hardware/biometrics/IBiometricService.aidl4
-rw-r--r--core/java/android/hardware/biometrics/flags.aconfig7
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java1
-rw-r--r--core/java/android/hardware/hdmi/OWNERS3
-rw-r--r--core/java/android/hardware/location/ContextHubManager.java12
-rw-r--r--core/java/android/net/NetworkStack.java13
-rw-r--r--core/java/android/net/vcn/VcnGatewayConnectionConfig.java44
-rw-r--r--core/java/android/net/vcn/flags.aconfig8
-rw-r--r--core/java/android/nfc/Constants.java29
-rw-r--r--core/java/android/nfc/INfcCardEmulation.aidl1
-rw-r--r--core/java/android/nfc/NfcAdapter.java41
-rw-r--r--core/java/android/nfc/cardemulation/AidGroup.java33
-rw-r--r--core/java/android/nfc/cardemulation/ApduServiceInfo.java160
-rw-r--r--core/java/android/nfc/cardemulation/CardEmulation.java49
-rw-r--r--core/java/android/nfc/cardemulation/NfcFServiceInfo.java58
-rw-r--r--core/java/android/nfc/flags.aconfig7
-rw-r--r--core/java/android/os/BatteryManager.java2
-rwxr-xr-xcore/java/android/os/Build.java2
-rw-r--r--core/java/android/os/DeadObjectException.java3
-rw-r--r--core/java/android/os/DeadSystemRuntimeException.java7
-rw-r--r--core/java/android/os/GraphicsEnvironment.java168
-rw-r--r--core/java/android/os/INetworkManagementService.aidl10
-rw-r--r--core/java/android/os/StrictMode.java18
-rw-r--r--core/java/android/os/UserManager.java3
-rw-r--r--core/java/android/os/flags.aconfig7
-rw-r--r--core/java/android/os/storage/IStorageManager.aidl12
-rw-r--r--core/java/android/os/storage/StorageManager.java67
-rw-r--r--core/java/android/preference/OWNERS2
-rw-r--r--core/java/android/provider/Settings.java16
-rw-r--r--core/java/android/security/OWNERS1
-rw-r--r--core/java/android/security/flags.aconfig1
-rw-r--r--core/java/android/service/rotationresolver/OWNERS2
-rw-r--r--core/java/android/view/OWNERS1
-rw-r--r--core/java/android/widget/OWNERS2
-rw-r--r--core/java/android/window/WindowInfosListenerForTest.java25
-rw-r--r--core/java/android/window/flags/OWNERS1
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java4
-rw-r--r--core/java/com/android/internal/foldables/OWNERS1
-rw-r--r--core/java/com/android/internal/os/Zygote.java5
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java1
-rw-r--r--core/java/com/android/internal/util/FastXmlSerializer.java424
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl1
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java122
-rw-r--r--core/java/com/android/internal/widget/LockSettingsInternal.java11
-rw-r--r--core/jni/android_media_AudioRecord.cpp2
-rw-r--r--core/jni/android_os_GraphicsEnvironment.cpp26
-rw-r--r--core/proto/android/nfc/Android.bp43
-rw-r--r--core/proto/android/service/OWNERS1
-rw-r--r--core/res/AndroidManifest.xml7
-rw-r--r--core/res/OWNERS2
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/res/res/xml/sms_short_codes.xml9
-rw-r--r--core/tests/GameManagerTests/src/android/app/GameManagerTests.java10
-rw-r--r--core/tests/coretests/src/android/app/OWNERS4
-rw-r--r--core/tests/coretests/src/android/content/BrickDeniedTest.java33
-rw-r--r--core/tests/coretests/src/android/view/OWNERS5
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java13
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--data/keyboards/Generic.kl6
-rw-r--r--keystore/java/android/security/AndroidKeyStoreMaintenance.java19
-rw-r--r--keystore/java/android/security/Authorization.java27
-rw-r--r--keystore/java/android/security/GateKeeper.java13
-rw-r--r--keystore/java/android/security/KeyStore.java33
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java38
-rw-r--r--libs/hwui/Properties.cpp2
-rw-r--r--libs/hwui/api/current.txt2
-rw-r--r--libs/hwui/api/module-lib-current.txt2
-rw-r--r--libs/hwui/api/module-lib-removed.txt2
-rw-r--r--libs/hwui/api/removed.txt2
-rw-r--r--libs/hwui/api/system-current.txt2
-rw-r--r--libs/hwui/api/system-removed.txt2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp1
-rw-r--r--location/Android.bp23
-rw-r--r--location/api/current.txt1
-rw-r--r--location/api/module-lib-current.txt1
-rw-r--r--location/api/module-lib-removed.txt1
-rw-r--r--location/api/removed.txt1
-rw-r--r--location/api/system-current.txt1
-rw-r--r--location/api/system-removed.txt1
-rw-r--r--location/api/test-current.txt1
-rw-r--r--location/api/test-removed.txt1
-rw-r--r--media/Android.bp6
-rw-r--r--media/java/android/media/session/MediaSession.java1
-rw-r--r--media/jni/Android.bp2
-rw-r--r--media/jni/android_media_MediaMetadataRetriever.cpp11
-rw-r--r--media/jni/playback_flags.aconfig8
-rw-r--r--media/tests/MediaFrameworkTest/Android.bp4
-rw-r--r--media/tests/MediaFrameworkTest/AndroidManifest.xml1
-rw-r--r--media/tests/MediaFrameworkTest/AndroidTest.xml1
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java6
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java68
-rw-r--r--native/android/system_fonts.cpp18
-rw-r--r--nfc-extras/OWNERS2
-rw-r--r--nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java49
-rw-r--r--omapi/aidl/Android.bp5
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java12
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java4
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java16
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java8
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java69
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java2
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/OWNERS8
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS3
-rw-r--r--rs/java/android/renderscript/ScriptC.java27
-rw-r--r--services/Android.bp13
-rw-r--r--services/companion/java/com/android/server/companion/virtual/OWNERS2
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/EventLogTags.logtags10
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java104
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java2
-rw-r--r--services/core/java/com/android/server/am/EventLogTags.logtags3
-rw-r--r--services/core/java/com/android/server/am/LmkdStatsReporter.java3
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java3
-rw-r--r--services/core/java/com/android/server/am/ProcessProviderRecord.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java2
-rw-r--r--services/core/java/com/android/server/am/UserController.java30
-rw-r--r--services/core/java/com/android/server/app/GameManagerService.java67
-rw-r--r--services/core/java/com/android/server/app/GameManagerShellCommand.java10
-rw-r--r--services/core/java/com/android/server/audio/AdiDeviceState.java204
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java104
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java89
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java56
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java436
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java28
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricCameraManager.java (renamed from services/core/java/com/android/server/BrickReceiver.java)27
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java68
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java100
-rw-r--r--services/core/java/com/android/server/biometrics/PreAuthInfo.java41
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java35
-rw-r--r--services/core/java/com/android/server/compat/overrides/OWNERS2
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java509
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java142
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java2
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsStorage.java57
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java91
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java8
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java49
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java13
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java2
-rw-r--r--services/core/java/com/android/server/net/NetworkManagementService.java35
-rw-r--r--services/core/java/com/android/server/notification/OWNERS5
-rw-r--r--services/core/java/com/android/server/pm/AppDataHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java4
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java41
-rw-r--r--services/core/java/com/android/server/pm/Installer.java23
-rw-r--r--services/core/java/com/android/server/pm/MovePackageHelper.java24
-rw-r--r--services/core/java/com/android/server/pm/OtaDexoptService.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java12
-rw-r--r--services/core/java/com/android/server/pm/StorageEventHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/UpdateOwnershipHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java23
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java6
-rw-r--r--services/core/java/com/android/server/policy/PermissionPolicyService.java3
-rw-r--r--services/core/java/com/android/server/power/OWNERS2
-rw-r--r--services/core/java/com/android/server/rollback/AppDataRollbackHelper.java4
-rw-r--r--services/core/java/com/android/server/stats/OWNERS5
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java9
-rw-r--r--services/core/java/com/android/server/vcn/VcnContext.java11
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java8
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java14
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java22
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java11
-rw-r--r--services/core/java/com/android/server/wm/OWNERS8
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java6
-rw-r--r--services/midi/OWNERS2
-rw-r--r--services/proguard.flags13
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java9
-rw-r--r--services/tests/servicestests/Android.bp1
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java86
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java63
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java167
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java206
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java65
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java1
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java1
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java5
-rw-r--r--telephony/OWNERS4
-rw-r--r--telephony/java/android/telephony/BarringInfo.java18
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java7
-rw-r--r--test-mock/api/current.txt23
-rw-r--r--test-mock/api/removed.txt4
-rw-r--r--test-mock/api/system-current.txt46
-rw-r--r--test-mock/api/system-removed.txt2
-rw-r--r--test-mock/api/test-current.txt16
-rw-r--r--test-mock/api/test-removed.txt2
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java52
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java71
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java13
-rw-r--r--tools/aapt2/Android.bp3
-rw-r--r--tools/aapt2/cmd/Compile.cpp10
-rw-r--r--tools/aapt2/cmd/Compile.h5
-rw-r--r--tools/aapt2/cmd/Link.cpp25
-rw-r--r--tools/aapt2/cmd/Link.h9
-rw-r--r--tools/aapt2/cmd/Util.cpp50
-rw-r--r--tools/aapt2/cmd/Util.h14
-rw-r--r--tools/aapt2/cmd/Util_test.cpp46
-rw-r--r--tools/aapt2/link/FeatureFlagsFilter.cpp104
-rw-r--r--tools/aapt2/link/FeatureFlagsFilter.h79
-rw-r--r--tools/aapt2/link/FeatureFlagsFilter_test.cpp236
-rw-r--r--tools/aapt2/link/Linkers.h28
-rw-r--r--tools/aapt2/process/ProductFilter.cpp (renamed from tools/aapt2/link/ProductFilter.cpp)70
-rw-r--r--tools/aapt2/process/ProductFilter.h65
-rw-r--r--tools/aapt2/process/ProductFilter_test.cpp (renamed from tools/aapt2/link/ProductFilter_test.cpp)95
-rwxr-xr-xtools/fonts/update_font_metadata.py2
-rw-r--r--tools/lint/README.md13
-rw-r--r--tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt1
-rw-r--r--tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt32
-rw-r--r--tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt2
-rw-r--r--tools/lint/framework/checks/src/main/java/com/google/android/lint/PermissionAnnotationDetector.kt87
-rw-r--r--tools/lint/framework/checks/src/test/java/com/google/android/lint/PermissionAnnotationDetectorTest.kt134
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt6
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt198
-rw-r--r--tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt149
-rw-r--r--tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt243
-rw-r--r--tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt5
-rw-r--r--tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt6
284 files changed, 5912 insertions, 2937 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index f0e0c30fd031..45f65709533f 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -15,6 +15,7 @@
aconfig_srcjars = [
":android.content.pm.flags-aconfig-java{.generated_srcjars}",
":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
+ ":android.net.vcn.flags-aconfig-java{.generated_srcjars}",
":android.nfc.flags-aconfig-java{.generated_srcjars}",
":android.os.flags-aconfig-java{.generated_srcjars}",
":android.security.flags-aconfig-java{.generated_srcjars}",
@@ -123,6 +124,11 @@ aconfig_declarations {
java_aconfig_library {
name: "android.nfc.flags-aconfig-java",
aconfig_declarations: "android.nfc.flags-aconfig",
+ min_sdk_version: "VanillaIceCream",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.nfcservices",
+ ],
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -147,6 +153,12 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+rust_aconfig_library {
+ name: "libandroid_security_flags_rust",
+ crate_name: "android_security_flags",
+ aconfig_declarations: "android.security.flags-aconfig",
+}
+
// Package Manager
aconfig_declarations {
name: "android.content.pm.flags-aconfig",
@@ -186,3 +198,34 @@ java_aconfig_library {
aconfig_declarations: "com.android.net.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Media
+aconfig_declarations {
+ name: "android.media.playback.flags-aconfig",
+ package: "com.android.media.playback.flags",
+ srcs: ["media/jni/playback_flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "android.media.playback.flags-aconfig-cc",
+ aconfig_declarations: "android.media.playback.flags-aconfig",
+}
+
+java_aconfig_library {
+ name: "android.media.playback.flags-aconfig-java",
+ aconfig_declarations: "android.media.playback.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// VCN
+aconfig_declarations {
+ name: "android.net.vcn.flags-aconfig",
+ package: "android.net.vcn",
+ srcs: ["core/java/android/net/vcn/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.net.vcn.flags-aconfig-java",
+ aconfig_declarations: "android.net.vcn.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index ccff26fb70d2..1aa297faa7e8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -321,7 +321,6 @@ java_defaults {
":installd_aidl",
":libaudioclient_aidl",
":libbinder_aidl",
- ":libbluetooth-binder-aidl",
":libcamera_client_aidl",
":libcamera_client_framework_aidl",
":libupdate_engine_aidl",
@@ -370,17 +369,12 @@ java_defaults {
// TODO(b/120066492): remove default_television.xml when the build system
// propagates "required" properly.
"default_television.xml",
- "framework-platform-compat-config",
// TODO(b/120066492): remove gps_debug and protolog.conf.json when the build
// system propagates "required" properly.
"gps_debug.conf",
- "icu4j-platform-compat-config",
"protolog.conf.json.gz",
- "services-platform-compat-config",
- "TeleService-platform-compat-config",
- "documents-ui-compat-config",
- "calendar-provider-compat-config",
- "contacts-provider-platform-compat-config",
+ // any install dependencies should go into framework-minus-apex-install-dependencies
+ // rather than here to avoid bloating incremental build time
],
libs: [
"androidx.annotation_annotation",
@@ -402,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",
@@ -415,21 +410,18 @@ java_defaults {
],
}
-java_library {
- name: "framework-minus-apex",
+// Separated so framework-minus-apex-defaults can be used without the libs dependency
+java_defaults {
+ name: "framework-minus-apex-with-libs-defaults",
defaults: ["framework-minus-apex-defaults"],
- installable: true,
- // For backwards compatibility.
- stem: "framework",
- apex_available: ["//apex_available:platform"],
- visibility: [
- "//frameworks/base",
- // TODO(b/147128803) remove the below lines
- "//frameworks/base/apex/blobstore/framework",
- "//frameworks/base/apex/jobscheduler/framework",
- "//frameworks/base/packages/Tethering/tests/unit",
- "//packages/modules/Connectivity/Tethering/tests/unit",
+ libs: [
+ "framework-virtualization.stubs.module_lib",
+ "framework-location.impl",
],
+}
+
+java_defaults {
+ name: "framework-non-updatable-lint-defaults",
lint: {
extra_check_modules: ["AndroidFrameworkLintChecker"],
disabled_checks: ["ApiMightLeakAppVisibility"],
@@ -443,6 +435,43 @@ java_library {
"UseOfCallerAwareMethodsWithClearedIdentity",
],
},
+}
+
+// we are unfortunately building the turbine jar twice, but more efficient and less complex
+// than generating a similar set of stubs with metalava
+java_library {
+ name: "framework-minus-apex-headers",
+ defaults: ["framework-minus-apex-defaults"],
+ installable: false,
+ // For backwards compatibility.
+ stem: "framework",
+ apex_available: ["//apex_available:platform"],
+ visibility: [
+ "//frameworks/base/location",
+ ],
+ compile_dex: false,
+ headers_only: true,
+}
+
+java_library {
+ name: "framework-minus-apex",
+ defaults: [
+ "framework-minus-apex-with-libs-defaults",
+ "framework-non-updatable-lint-defaults",
+ ],
+ installable: true,
+ // For backwards compatibility.
+ stem: "framework",
+ apex_available: ["//apex_available:platform"],
+ visibility: [
+ "//frameworks/base",
+ "//frameworks/base/location",
+ // TODO(b/147128803) remove the below lines
+ "//frameworks/base/apex/blobstore/framework",
+ "//frameworks/base/apex/jobscheduler/framework",
+ "//frameworks/base/packages/Tethering/tests/unit",
+ "//packages/modules/Connectivity/Tethering/tests/unit",
+ ],
errorprone: {
javacflags: [
"-Xep:AndroidFrameworkCompatChange:ERROR",
@@ -453,7 +482,7 @@ java_library {
java_library {
name: "framework-minus-apex-intdefs",
- defaults: ["framework-minus-apex-defaults"],
+ defaults: ["framework-minus-apex-with-libs-defaults"],
plugins: ["intdef-annotation-processor"],
// Errorprone and android lint will already run on framework-minus-apex, don't rerun them on
@@ -481,6 +510,7 @@ java_library {
installable: false, // this lib is a build-only library
static_libs: [
"app-compat-annotations",
+ "framework-location.impl",
"framework-minus-apex",
"framework-updatable-stubs-module_libs_api",
],
@@ -488,6 +518,20 @@ java_library {
apex_available: ["//apex_available:platform"],
}
+java_library {
+ name: "framework-minus-apex-install-dependencies",
+ required: [
+ "framework-minus-apex",
+ "framework-platform-compat-config",
+ "services-platform-compat-config",
+ "icu4j-platform-compat-config",
+ "TeleService-platform-compat-config",
+ "documents-ui-compat-config",
+ "calendar-provider-compat-config",
+ "contacts-provider-platform-compat-config",
+ ],
+}
+
platform_compat_config {
name: "framework-platform-compat-config",
src: ":framework-minus-apex",
@@ -560,46 +604,11 @@ java_library {
],
}
-// TODO(b/145644363): move this to under StubLibraries.bp or ApiDocs.bp
-metalava_framework_docs_args = "" +
- "--api-lint-ignore-prefix android.icu. " +
- "--api-lint-ignore-prefix java. " +
- "--api-lint-ignore-prefix junit. " +
- "--api-lint-ignore-prefix org. " +
- "--error NoSettingsProvider " +
- "--error UnhiddenSystemApi " +
- "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " +
- "--hide BroadcastBehavior " +
- "--hide CallbackInterface " +
- "--hide DeprecationMismatch " +
- "--hide HiddenSuperclass " +
- "--hide HiddenTypeParameter " +
- "--hide MissingPermission " +
- "--hide-package android.audio.policy.configuration.V7_0 " +
- "--hide-package com.android.server " +
- "--hide RequiresPermission " +
- "--hide SdkConstant " +
- "--hide Todo " +
- "--hide UnavailableSymbol " +
- "--manifest $(location :frameworks-base-core-AndroidManifest.xml) "
-
-packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
filegroup {
name: "android-non-updatable-stub-sources",
srcs: [
":framework-mime-sources", // mimemap builds separately but has no separate droidstubs.
+ ":framework-minus-apex-aconfig-srcjars",
":framework-non-updatable-sources",
":opt-telephony-srcs",
":opt-net-voip-srcs",
@@ -609,86 +618,6 @@ filegroup {
visibility: ["//frameworks/base/api"],
}
-// Defaults for all stubs that include the non-updatable framework. These defaults do not include
-// module symbols, so will not compile correctly on their own. Users must add module APIs to the
-// classpath (or sources) somehow.
-stubs_defaults {
- name: "android-non-updatable-stubs-defaults",
- srcs: [":android-non-updatable-stub-sources"],
- sdk_version: "none",
- system_modules: "none",
- java_version: "1.8",
- arg_files: [":frameworks-base-core-AndroidManifest.xml"],
- aidl: {
- include_dirs: [
- "frameworks/av/aidl",
- "frameworks/base/media/aidl",
- "frameworks/base/telephony/java",
- "frameworks/native/libs/permission/aidl",
- "packages/modules/Bluetooth/framework/aidl-export",
- "packages/modules/Connectivity/framework/aidl-export",
- "packages/modules/Media/apex/aidl/stable",
- "hardware/interfaces/biometrics/common/aidl",
- "hardware/interfaces/biometrics/fingerprint/aidl",
- "hardware/interfaces/graphics/common/aidl",
- "hardware/interfaces/keymaster/aidl",
- "system/hardware/interfaces/media/aidl",
- ],
- },
- // These are libs from framework-internal-utils that are required (i.e. being referenced)
- // from framework-non-updatable-sources. Add more here when there's a need.
- // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
- // dependencies gets bigger.
- libs: [
- "android.hardware.cas-V1.2-java",
- "android.hardware.health-V1.0-java-constants",
- "android.hardware.radio-V1.5-java",
- "android.hardware.radio-V1.6-java",
- "android.hardware.thermal-V1.0-java-constants",
- "android.hardware.thermal-V2.0-java",
- "android.hardware.tv.input-V1.0-java-constants",
- "android.hardware.usb-V1.0-java-constants",
- "android.hardware.usb-V1.1-java-constants",
- "android.hardware.usb.gadget-V1.0-java",
- "android.hardware.vibrator-V1.3-java",
- "framework-protos",
- ],
- filter_packages: packages_to_document,
- high_mem: true, // Lots of sources => high memory use, see b/170701554
- installable: false,
- annotations_enabled: true,
- previous_api: ":android.api.public.latest",
- merge_annotations_dirs: ["metalava-manual"],
- defaults_visibility: ["//frameworks/base/api"],
- visibility: ["//frameworks/base/api"],
-}
-
-// Defaults with module APIs in the classpath (mostly from prebuilts).
-// Suitable for compiling android-non-updatable.
-stubs_defaults {
- name: "module-classpath-stubs-defaults",
- aidl: {
- include_dirs: [
- "packages/modules/Bluetooth/framework/aidl-export",
- "packages/modules/Connectivity/framework/aidl-export",
- "packages/modules/Media/apex/aidl/stable",
- ],
- },
- libs: [
- "art.module.public.api",
- "sdk_module-lib_current_framework-tethering",
- "sdk_module-lib_current_framework-connectivity-t",
- "sdk_public_current_framework-bluetooth",
- // There are a few classes from modules used by the core that
- // need to be resolved by metalava. We use a prebuilt stub of the
- // full sdk to ensure we can resolve them. If a new class gets added,
- // the prebuilts/sdk/current needs to be updated.
- "sdk_system_current_android",
- // NOTE: The below can be removed once the prebuilt stub contains IKE.
- "sdk_system_current_android.net.ipsec.ike",
- ],
-}
-
build = [
"AconfigFlags.bp",
"ProtoLibraries.bp",
diff --git a/BAL_OWNERS b/BAL_OWNERS
new file mode 100644
index 000000000000..d56a1d4634df
--- /dev/null
+++ b/BAL_OWNERS
@@ -0,0 +1,5 @@
+brufino@google.com
+achim@google.com
+topjohnwu@google.com
+lus@google.com
+
diff --git a/INPUT_OWNERS b/INPUT_OWNERS
index 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/ProtoLibraries.bp b/ProtoLibraries.bp
index 45bb16184069..e7adf203334e 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -77,6 +77,42 @@ gensrcs {
output_extension: "proto.h",
}
+// ==== nfc framework java library ==============================
+gensrcs {
+ name: "framework-nfc-javastream-protos",
+
+ tools: [
+ "aprotoc",
+ "protoc-gen-javastream",
+ "soong_zip",
+ ],
+
+ cmd: "mkdir -p $(genDir)/$(in) " +
+ "&& $(location aprotoc) " +
+ " --plugin=$(location protoc-gen-javastream) " +
+ " --javastream_out=$(genDir)/$(in) " +
+ " -Iexternal/protobuf/src " +
+ " -I . " +
+ " $(in) " +
+ "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
+
+ srcs: [
+ "core/proto/android/app/pendingintent.proto",
+ "core/proto/android/content/component_name.proto",
+ "core/proto/android/content/intent.proto",
+ "core/proto/android/nfc/*.proto",
+ "core/proto/android/os/patternmatcher.proto",
+ "core/proto/android/os/persistablebundle.proto",
+ "core/proto/android/privacy.proto",
+ ],
+
+ data: [
+ ":libprotobuf-internal-protos",
+ ],
+
+ output_extension: "srcjar",
+}
+
// ==== java proto host library ==============================
java_library_host {
name: "platformprotos",
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 c16bce5a1aea..bd2b11a0e69f 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -86,6 +86,7 @@ combined_apis {
"framework-devicelock",
"framework-graphics",
"framework-healthfitness",
+ "framework-location",
"framework-media",
"framework-mediaprovider",
"framework-ondevicepersonalization",
@@ -259,6 +260,155 @@ java_genrule {
cmd: "cat $(in) | md5sum | cut -d' ' -f1 > $(out)",
}
+packages_to_document = [
+ "android",
+ "dalvik",
+ "java",
+ "javax",
+ "junit",
+ "org.apache.http",
+ "org.json",
+ "org.w3c.dom",
+ "org.xml.sax",
+ "org.xmlpull",
+]
+
+// Defaults for all stubs that include the non-updatable framework. These defaults do not include
+// module symbols, so will not compile correctly on their own. Users must add module APIs to the
+// classpath (or sources) somehow.
+stubs_defaults {
+ name: "android-non-updatable-stubs-defaults",
+ srcs: [":android-non-updatable-stub-sources"],
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
+ arg_files: [":frameworks-base-core-AndroidManifest.xml"],
+ aidl: {
+ include_dirs: [
+ "frameworks/av/aidl",
+ "frameworks/base/media/aidl",
+ "frameworks/base/telephony/java",
+ "frameworks/native/libs/permission/aidl",
+ "packages/modules/Bluetooth/framework/aidl-export",
+ "packages/modules/Connectivity/framework/aidl-export",
+ "packages/modules/Media/apex/aidl/stable",
+ "hardware/interfaces/biometrics/common/aidl",
+ "hardware/interfaces/biometrics/fingerprint/aidl",
+ "hardware/interfaces/graphics/common/aidl",
+ "hardware/interfaces/keymaster/aidl",
+ "system/hardware/interfaces/media/aidl",
+ ],
+ },
+ // These are libs from framework-internal-utils that are required (i.e. being referenced)
+ // from framework-non-updatable-sources. Add more here when there's a need.
+ // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
+ // dependencies gets bigger.
+ libs: [
+ "android.hardware.cas-V1.2-java",
+ "android.hardware.health-V1.0-java-constants",
+ "android.hardware.radio-V1.5-java",
+ "android.hardware.radio-V1.6-java",
+ "android.hardware.thermal-V1.0-java-constants",
+ "android.hardware.thermal-V2.0-java",
+ "android.hardware.tv.input-V1.0-java-constants",
+ "android.hardware.usb-V1.0-java-constants",
+ "android.hardware.usb-V1.1-java-constants",
+ "android.hardware.usb.gadget-V1.0-java",
+ "android.hardware.vibrator-V1.3-java",
+ "framework-protos",
+ ],
+ flags: [
+ "--api-lint-ignore-prefix android.icu.",
+ "--api-lint-ignore-prefix java.",
+ "--api-lint-ignore-prefix junit.",
+ "--api-lint-ignore-prefix org.",
+ "--error NoSettingsProvider",
+ "--error UnhiddenSystemApi",
+ "--error UnflaggedApi",
+ "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*",
+ "--hide BroadcastBehavior",
+ "--hide CallbackInterface",
+ "--hide DeprecationMismatch",
+ "--hide HiddenSuperclass",
+ "--hide MissingPermission",
+ "--hide RequiresPermission",
+ "--hide SdkConstant",
+ "--hide Todo",
+ "--hide-package android.audio.policy.configuration.V7_0",
+ "--hide-package com.android.server",
+ "--manifest $(location :frameworks-base-core-AndroidManifest.xml)",
+ ],
+ filter_packages: packages_to_document,
+ high_mem: true, // Lots of sources => high memory use, see b/170701554
+ installable: false,
+ annotations_enabled: true,
+ previous_api: ":android.api.public.latest",
+ merge_annotations_dirs: ["metalava-manual"],
+ defaults_visibility: ["//frameworks/base/api"],
+ visibility: ["//frameworks/base/api"],
+}
+
+// We resolve dependencies on APIs in modules by depending on a prebuilt of the whole
+// platform (sdk_system_current_android). That prebuilt does not include module-lib APIs,
+// so use the prebuilt module-lib stubs for modules that export module-lib stubs that the
+// non-updatable part depends on.
+non_updatable_api_deps_on_modules = [
+ "sdk_module-lib_current_framework-tethering",
+ "sdk_module-lib_current_framework-connectivity-t",
+ "sdk_system_current_android",
+]
+
+// Defaults with module APIs in the classpath (mostly from prebuilts).
+// Suitable for compiling android-non-updatable.
+stubs_defaults {
+ name: "module-classpath-stubs-defaults",
+ aidl: {
+ include_dirs: [
+ "packages/modules/Bluetooth/framework/aidl-export",
+ "packages/modules/Connectivity/framework/aidl-export",
+ "packages/modules/Media/apex/aidl/stable",
+ ],
+ },
+ libs: non_updatable_api_deps_on_modules,
+}
+
+// Defaults for the java_sdk_libraries of unbundled jars from framework.
+// java_sdk_libraries using these defaults should also add themselves to the
+// non_updatable_modules list in frameworks/base/api/api.go
+java_defaults {
+ name: "framework-non-updatable-unbundled-defaults",
+ defaults: [
+ "framework-non-updatable-lint-defaults",
+ "non-updatable-framework-module-defaults",
+ ],
+ public: {
+ libs: ["android_module_lib_stubs_current"],
+ },
+ system: {
+ libs: ["android_module_lib_stubs_current"],
+ },
+ module_lib: {
+ libs: ["android_module_lib_stubs_current"],
+ },
+ test: {
+ libs: ["android_test_frameworks_core_stubs_current"],
+ },
+ sdk_version: "core_platform",
+ stub_only_libs: ["framework-protos"],
+ impl_only_libs: ["framework-minus-apex-headers"], // the framework, including hidden API
+ impl_library_visibility: ["//frameworks/base"],
+ defaults_visibility: ["//frameworks/base/location"],
+ plugins: ["error_prone_android_framework"],
+ errorprone: {
+ javacflags: [
+ "-Xep:AndroidFrameworkCompatChange:ERROR",
+ "-Xep:AndroidFrameworkUid:ERROR",
+ ],
+ },
+ // Include manual annotations in API txt files
+ merge_annotations_dirs: ["metalava-manual"],
+}
+
build = [
"ApiDocs.bp",
"StubLibraries.bp",
diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp
index 30b442336148..5744bdfd4b28 100644
--- a/api/ApiDocs.bp
+++ b/api/ApiDocs.bp
@@ -72,7 +72,6 @@ droidstubs {
"android-non-updatable-doc-stubs-defaults",
"module-classpath-stubs-defaults",
],
- args: metalava_framework_docs_args,
}
droidstubs {
@@ -81,8 +80,7 @@ droidstubs {
"android-non-updatable-doc-stubs-defaults",
"module-classpath-stubs-defaults",
],
- args: metalava_framework_docs_args +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+ flags: ["--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)"],
}
droidstubs {
@@ -91,9 +89,10 @@ droidstubs {
"android-non-updatable-doc-stubs-defaults",
"module-classpath-stubs-defaults",
],
- args: metalava_framework_docs_args +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) " +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\) ",
+ flags: [
+ "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
+ "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)",
+ ],
generate_stubs: false, // We're only using this module for the annotations.zip output, disable doc-stubs.
write_sdk_values: false,
}
@@ -104,10 +103,11 @@ droidstubs {
"android-non-updatable-doc-stubs-defaults",
"module-classpath-stubs-defaults",
],
- args: metalava_framework_docs_args +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) " +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\) " +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\) ",
+ flags: [
+ "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)",
+ "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES\\)",
+ "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)",
+ ],
generate_stubs: false, // We're only using this module for the annotations.zip output, disable doc-stubs.
write_sdk_values: false,
}
@@ -116,7 +116,6 @@ droidstubs {
name: "framework-doc-stubs",
defaults: ["android-non-updatable-doc-stubs-defaults"],
srcs: [":all-modules-public-stubs-source"],
- args: metalava_framework_docs_args,
api_levels_module: "api_versions_public",
aidl: {
include_dirs: [
@@ -124,21 +123,13 @@ droidstubs {
"packages/modules/Media/apex/aidl/stable",
],
},
- extensions_info_file: ":sdk-extensions-info",
}
droidstubs {
name: "framework-doc-system-stubs",
defaults: ["framework-doc-stubs-sources-default"],
- args: metalava_framework_docs_args +
- " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
- api_levels_annotations_enabled: true,
- api_levels_annotations_dirs: [
- "sdk-dir",
- "api-versions-jars-dir",
- ],
- api_levels_sdk_type: "system",
- extensions_info_file: ":sdk-extensions-info",
+ flags: ["--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)"],
+ api_levels_module: "api_versions_system",
}
/////////////////////////////////////////////////////////////////////
@@ -146,17 +137,6 @@ droidstubs {
// using droiddoc
/////////////////////////////////////////////////////////////////////
-framework_docs_only_args = " -android -manifest $(location :frameworks-base-core-AndroidManifest.xml) " +
- "-metalavaApiSince " +
- "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
- "-overview $(location :frameworks-base-java-overview) " +
- // Federate Support Library references against local API file.
- "-federate SupportLib https://developer.android.com " +
- "-federationapi SupportLib $(location :current-support-api) " +
- // Federate Support Library references against local API file.
- "-federate AndroidX https://developer.android.com " +
- "-federationapi AndroidX $(location :current-androidx-api) "
-
doc_defaults {
name: "framework-docs-default",
sdk_version: "none",
@@ -176,6 +156,28 @@ doc_defaults {
resourcesdir: "docs/html/reference/images/",
resourcesoutdir: "reference/android/images/",
lint_baseline: "javadoc-lint-baseline",
+ flags: [
+ "-android",
+ "-manifest $(location :frameworks-base-core-AndroidManifest.xml)",
+ "-metalavaApiSince",
+ "-werror",
+ "-lerror",
+ "-overview $(location :frameworks-base-java-overview)",
+ // Federate Support Library references against local API file.
+ "-federate SupportLib https://developer.android.com",
+ "-federationapi SupportLib $(location :current-support-api)",
+ // Federate Support Library references against local API file.
+ "-federate AndroidX https://developer.android.com",
+ "-federationapi AndroidX $(location :current-androidx-api)",
+ // doclava contains checks for a few issues that are have been migrated to metalava.
+ // disable them in doclava, to avoid mistriggering or double triggering.
+ "-hide 111", // HIDDEN_SUPERCLASS
+ "-hide 113", // DEPRECATION_MISMATCH
+ "-hide 125", // REQUIRES_PERMISSION
+ "-hide 126", // BROADCAST_BEHAVIOR
+ "-hide 127", // SDK_CONSTANT
+ "-hide 128", // TODO
+ ],
hdf: [
"dac true",
"sdk.codename O",
@@ -211,7 +213,10 @@ droiddoc {
],
compat_config: ":global-compat-config",
proofread_file: "offline-sdk-docs-proofread.txt",
- args: framework_docs_only_args + " -offlinemode -title \"Android SDK\"",
+ flags: [
+ "-offlinemode",
+ "-title \"Android SDK\"",
+ ],
static_doc_index_redirect: "docs/docs-preview-index.html",
}
@@ -228,7 +233,11 @@ droiddoc {
"android.whichdoc offline",
],
proofread_file: "offline-sdk-referenceonly-docs-proofread.txt",
- args: framework_docs_only_args + " -offlinemode -title \"Android SDK\" -referenceonly",
+ flags: [
+ "-offlinemode",
+ "-title \"Android SDK\"",
+ "-referenceonly",
+ ],
static_doc_index_redirect: "docs/docs-documentation-redirect.html",
static_doc_properties: "docs/source.properties",
}
@@ -246,8 +255,14 @@ droiddoc {
"android.whichdoc offline",
],
proofread_file: "offline-system-sdk-referenceonly-docs-proofread.txt",
- args: framework_docs_only_args + " -hide 101 -hide 104 -hide 108" +
- " -offlinemode -title \"Android System SDK\" -referenceonly",
+ flags: [
+ "-hide 101",
+ "-hide 104",
+ "-hide 108",
+ "-offlinemode",
+ "-title \"Android System SDK\"",
+ "-referenceonly",
+ ],
static_doc_index_redirect: "docs/docs-documentation-redirect.html",
static_doc_properties: "docs/source.properties",
}
@@ -263,22 +278,28 @@ droiddoc {
"android.hasSamples true",
],
proofread_file: "ds-docs-proofread.txt",
- args: framework_docs_only_args +
- " -toroot / -yamlV2 -samplegroup Admin " +
- " -samplegroup Background " +
- " -samplegroup Connectivity " +
- " -samplegroup Content " +
- " -samplegroup Input " +
- " -samplegroup Media " +
- " -samplegroup Notification " +
- " -samplegroup RenderScript " +
- " -samplegroup Security " +
- " -samplegroup Sensors " +
- " -samplegroup System " +
- " -samplegroup Testing " +
- " -samplegroup UI " +
- " -samplegroup Views " +
- " -samplegroup Wearable -devsite -samplesdir development/samples/browseable ",
+ flags: [
+ " -toroot /",
+ "-yamlV2",
+ "-samplegroup Admin",
+ "-samplegroup Background",
+ "-samplegroup Connectivity",
+ "-samplegroup Content",
+ "-samplegroup Input",
+ "-samplegroup Media",
+ "-samplegroup Notification",
+ "-samplegroup RenderScript",
+ "-samplegroup Security",
+ "-samplegroup Sensors",
+ "-samplegroup System",
+ "-samplegroup Testing",
+ "-samplegroup UI",
+ "-samplegroup Views",
+ "-samplegroup Wearable",
+ "-devsite",
+ "-samplesdir",
+ "development/samples/browseable",
+ ],
}
droiddoc {
@@ -286,8 +307,11 @@ droiddoc {
srcs: [
":framework-doc-stubs",
],
- args: "-noJdkLink -links https://kotlinlang.org/api/latest/jvm/stdlib/^external/dokka/package-list " +
+ flags: [
+ "-noJdkLink",
+ "-links https://kotlinlang.org/api/latest/jvm/stdlib/^external/dokka/package-list",
"-noStdlibLink",
+ ],
proofread_file: "ds-dokka-proofread.txt",
dokka_enabled: true,
}
@@ -340,11 +364,12 @@ droiddoc {
hdf: [
"android.whichdoc online",
],
- args: framework_docs_only_args +
- " -staticonly " +
- " -toroot / " +
- " -devsite " +
- " -ignoreJdLinks ",
+ flags: [
+ "-staticonly",
+ "-toroot /",
+ "-devsite",
+ "-ignoreJdLinks",
+ ],
}
droiddoc {
@@ -356,8 +381,9 @@ droiddoc {
hdf: [
"android.whichdoc online",
],
- args: framework_docs_only_args +
- " -toroot / " +
- " -atLinksNavtree " +
- " -navtreeonly ",
+ flags: [
+ "-toroot /",
+ "-atLinksNavtree",
+ "-navtreeonly",
+ ],
}
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 79efaced998a..f6f69291ce0e 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -29,14 +29,10 @@
droidstubs {
name: "api-stubs-docs-non-updatable",
- srcs: [
- ":framework-minus-apex-aconfig-srcjars",
- ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
],
- args: metalava_framework_docs_args,
check_api: {
current: {
api_file: ":non-updatable-current.txt",
@@ -50,6 +46,7 @@ droidstubs {
api_lint: {
enabled: true,
new_since: ":android.api.public.latest",
+ baseline_file: ":non-updatable-lint-baseline.txt",
},
},
dists: [
@@ -69,30 +66,33 @@ droidstubs {
api_surface: "public",
}
-priv_apps = " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\)"
+priv_apps = [
+ "--show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
+ "\\)",
+]
-priv_apps_in_stubs = " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
- "\\)"
+priv_apps_in_stubs = [
+ "--show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
+ "\\)",
+]
-test = " --show-annotation android.annotation.TestApi"
+test = ["--show-annotation android.annotation.TestApi"]
-module_libs = " --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
- "\\)"
+module_libs = [
+ "--show-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+ "\\)",
+]
droidstubs {
name: "system-api-stubs-docs-non-updatable",
- srcs: [
- ":framework-minus-apex-aconfig-srcjars",
- ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
],
- args: metalava_framework_docs_args + priv_apps,
+ flags: priv_apps,
check_api: {
current: {
api_file: ":non-updatable-system-current.txt",
@@ -128,14 +128,11 @@ droidstubs {
droidstubs {
name: "test-api-stubs-docs-non-updatable",
- srcs: [
- ":framework-minus-apex-aconfig-srcjars",
- ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
],
- args: metalava_framework_docs_args + test + priv_apps_in_stubs,
+ flags: test + priv_apps_in_stubs,
check_api: {
current: {
api_file: ":non-updatable-test-current.txt",
@@ -143,6 +140,7 @@ droidstubs {
},
api_lint: {
enabled: true,
+ new_since: ":android.api.test.latest",
baseline_file: ":non-updatable-test-lint-baseline.txt",
},
},
@@ -177,14 +175,11 @@ droidstubs {
droidstubs {
name: "module-lib-api-stubs-docs-non-updatable",
- srcs: [
- ":framework-minus-apex-aconfig-srcjars",
- ],
defaults: [
"android-non-updatable-stubs-defaults",
"module-classpath-stubs-defaults",
],
- args: metalava_framework_docs_args + priv_apps_in_stubs + module_libs,
+ flags: priv_apps_in_stubs + module_libs,
check_api: {
current: {
api_file: ":non-updatable-module-lib-current.txt",
@@ -356,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",
},
@@ -521,6 +506,21 @@ java_library {
}
java_library {
+ name: "android_test_frameworks_core_stubs_current.from-source",
+ static_libs: [
+ "all-updatable-modules-system-stubs",
+ "android-non-updatable.stubs.test",
+ ],
+ defaults: [
+ "android.jar_defaults",
+ "android_stubs_dists_default",
+ ],
+ dist: {
+ dir: "apistubs/android/test-core",
+ },
+}
+
+java_library {
name: "android_module_lib_stubs_current.from-source",
defaults: [
"android.jar_defaults",
@@ -634,6 +634,7 @@ java_defaults {
api_surface: "test",
api_contributions: [
"framework-virtualization.stubs.source.test.api.contribution",
+ "framework-location.stubs.source.test.api.contribution",
],
}
@@ -677,6 +678,7 @@ java_api_library {
"api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -692,6 +694,7 @@ java_api_library {
"system-api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -709,6 +712,7 @@ java_api_library {
"test-api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -718,12 +722,15 @@ java_api_library {
"android_stubs_current_contributions",
"android_system_stubs_current_contributions",
"android_test_frameworks_core_stubs_current_contributions",
- "stub-annotation-defaults",
+ ],
+ libs: [
+ "stub-annotations",
],
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
],
+ enable_validation: false,
}
java_api_library {
@@ -743,6 +750,7 @@ java_api_library {
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -756,6 +764,7 @@ java_api_library {
"stub-annotations",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
java_api_library {
@@ -780,6 +789,7 @@ java_api_library {
visibility: [
"//visibility:private",
],
+ enable_validation: false,
}
java_api_library {
@@ -796,6 +806,7 @@ java_api_library {
"android_module_lib_stubs_current.from-text",
],
visibility: ["//visibility:public"],
+ enable_validation: false,
}
////////////////////////////////////////////////////////////////////////
@@ -944,7 +955,7 @@ droidstubs {
merge_annotations_dirs: [
"metalava-manual",
],
- args: priv_apps,
+ flags: priv_apps,
}
java_library {
diff --git a/api/api.go b/api/api.go
index 692d38fe21a2..8df6dab715ef 100644
--- a/api/api.go
+++ b/api/api.go
@@ -31,6 +31,7 @@ const art = "art.module.public.api"
const conscrypt = "conscrypt.module.public.api"
const i18n = "i18n.module.public.api"
const virtualization = "framework-virtualization"
+const location = "framework-location"
var core_libraries_modules = []string{art, conscrypt, i18n}
@@ -42,7 +43,7 @@ var core_libraries_modules = []string{art, conscrypt, i18n}
// APIs.
// In addition, the modules in this list are allowed to contribute to test APIs
// stubs.
-var non_updatable_modules = []string{virtualization}
+var non_updatable_modules = []string{virtualization, location}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
@@ -296,8 +297,10 @@ func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) {
}
func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) {
- // The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes.
+ // The user of this module compiles against the "core" SDK and against non-updatable modules,
+ // so remove to avoid dupes.
modules = removeAll(modules, core_libraries_modules)
+ modules = removeAll(modules, non_updatable_modules)
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
@@ -393,6 +396,7 @@ func createFullApiLibraries(ctx android.LoadHookContext) {
"android_stubs_current",
"android_system_stubs_current",
"android_test_stubs_current",
+ "android_test_frameworks_core_stubs_current",
"android_module_lib_stubs_current",
"android_system_server_stubs_current",
}
diff --git a/api/gen_combined_removed_dex.sh b/api/gen_combined_removed_dex.sh
index 71f366a6aae2..e0153f7c1091 100755
--- a/api/gen_combined_removed_dex.sh
+++ b/api/gen_combined_removed_dex.sh
@@ -6,6 +6,6 @@ shift 2
# Convert each removed.txt to the "dex format" equivalent, and print all output.
for f in "$@"; do
- "$metalava_path" "$f" --dex-api "${tmp_dir}/tmp"
+ "$metalava_path" signature-to-dex "$f" "${tmp_dir}/tmp"
cat "${tmp_dir}/tmp"
done
diff --git a/cmds/svc/src/com/android/commands/svc/NfcCommand.java b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
index 020ca3387555..ee2af129d400 100644
--- a/cmds/svc/src/com/android/commands/svc/NfcCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
@@ -16,10 +16,11 @@
package com.android.commands.svc;
+import android.app.ActivityThread;
import android.content.Context;
-import android.nfc.INfcAdapter;
-import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcManager;
+import android.os.Looper;
public class NfcCommand extends Svc.Command {
@@ -42,27 +43,26 @@ public class NfcCommand extends Svc.Command {
@Override
public void run(String[] args) {
- INfcAdapter adapter = INfcAdapter.Stub.asInterface(
- ServiceManager.getService(Context.NFC_SERVICE));
-
+ Looper.prepareMainLooper();
+ ActivityThread.initializeMainlineModules();
+ Context context = ActivityThread.systemMain().getSystemContext();
+ NfcManager nfcManager = context.getSystemService(NfcManager.class);
+ if (nfcManager == null) {
+ System.err.println("Got a null NfcManager, is the system running?");
+ return;
+ }
+ NfcAdapter adapter = nfcManager.getDefaultAdapter();
if (adapter == null) {
System.err.println("Got a null NfcAdapter, is the system running?");
return;
}
-
- try {
- if (args.length == 2 && "enable".equals(args[1])) {
- adapter.enable();
- return;
- } else if (args.length == 2 && "disable".equals(args[1])) {
- adapter.disable(true);
- return;
- }
- } catch (RemoteException e) {
- System.err.println("NFC operation failed: " + e);
+ if (args.length == 2 && "enable".equals(args[1])) {
+ adapter.enable();
+ return;
+ } else if (args.length == 2 && "disable".equals(args[1])) {
+ adapter.disable(true);
return;
}
-
System.err.println(longHelp());
}
diff --git a/cmds/svc/src/com/android/commands/svc/OWNERS b/cmds/svc/src/com/android/commands/svc/OWNERS
new file mode 100644
index 000000000000..d5a5d7b3b858
--- /dev/null
+++ b/cmds/svc/src/com/android/commands/svc/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48448
+per-file NfcCommand.java = file:platform/packages/apps/Nfc:/OWNERS
diff --git a/core/api/Android.bp b/core/api/Android.bp
index 71a2ca2903f6..907916a125da 100644
--- a/core/api/Android.bp
+++ b/core/api/Android.bp
@@ -38,6 +38,11 @@ filegroup {
}
filegroup {
+ name: "non-updatable-lint-baseline.txt",
+ srcs: ["lint-baseline.txt"],
+}
+
+filegroup {
name: "non-updatable-system-current.txt",
srcs: ["system-current.txt"],
}
diff --git a/core/api/current.txt b/core/api/current.txt
index e8988dcf3f17..c9cba0065dee 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android {
public final class Manifest {
@@ -4458,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();
@@ -5677,6 +5676,7 @@ package android.app {
ctor @Deprecated public FragmentBreadCrumbs(android.content.Context, android.util.AttributeSet);
ctor @Deprecated public FragmentBreadCrumbs(android.content.Context, android.util.AttributeSet, int);
method @Deprecated public void onBackStackChanged();
+ method @Deprecated protected void onLayout(boolean, int, int, int, int);
method @Deprecated public void setActivity(android.app.Activity);
method @Deprecated public void setMaxVisible(int);
method @Deprecated public void setOnBreadCrumbClickListener(android.app.FragmentBreadCrumbs.OnBreadCrumbClickListener);
@@ -12738,7 +12738,7 @@ package android.content.pm {
field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio.access";
field public static final String FEATURE_TELEPHONY_SUBSCRIPTION = "android.hardware.telephony.subscription";
field @Deprecated public static final String FEATURE_TELEVISION = "android.hardware.type.television";
- field public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
+ field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
field public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
@@ -13542,6 +13542,7 @@ package android.content.res {
public interface XmlResourceParser extends org.xmlpull.v1.XmlPullParser android.util.AttributeSet java.lang.AutoCloseable {
method public void close();
+ method public String getAttributeNamespace(int);
}
}
@@ -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);
}
@@ -18291,11 +18297,13 @@ package android.hardware.biometrics {
public class BiometricManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate();
method @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public int canAuthenticate(int);
+ method @FlaggedApi("android.hardware.biometrics.last_authentication_time") @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public long getLastAuthenticationTime(int);
method @NonNull @RequiresPermission(android.Manifest.permission.USE_BIOMETRIC) public android.hardware.biometrics.BiometricManager.Strings getStrings(int);
field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
+ field @FlaggedApi("android.hardware.biometrics.last_authentication_time") public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
}
@@ -18341,6 +18349,7 @@ package android.hardware.biometrics {
field public static final int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
field public static final int BIOMETRIC_ERROR_USER_CANCELED = 10; // 0xa
field public static final int BIOMETRIC_ERROR_VENDOR = 8; // 0x8
+ field @FlaggedApi("android.hardware.biometrics.last_authentication_time") public static final long BIOMETRIC_NO_AUTHENTICATION = -1L; // 0xffffffffffffffffL
}
public abstract static class BiometricPrompt.AuthenticationCallback {
@@ -18375,12 +18384,12 @@ package android.hardware.biometrics {
ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
ctor @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") public BiometricPrompt.CryptoObject(@NonNull javax.crypto.KeyAgreement);
- method public javax.crypto.Cipher getCipher();
+ method @Nullable public javax.crypto.Cipher getCipher();
method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement();
- method public javax.crypto.Mac getMac();
+ method @Nullable public javax.crypto.Mac getMac();
method @Nullable public android.security.identity.PresentationSession getPresentationSession();
- method public java.security.Signature getSignature();
+ method @Nullable public java.security.Signature getSignature();
}
}
@@ -20227,7 +20236,7 @@ package android.inputmethodservice {
method @Deprecated public boolean isPreviewEnabled();
method @Deprecated public boolean isProximityCorrectionEnabled();
method @Deprecated public boolean isShifted();
- method public void onClick(android.view.View);
+ method @Deprecated public void onClick(android.view.View);
method @Deprecated public void onDetachedFromWindow();
method @Deprecated public void onDraw(android.graphics.Canvas);
method @Deprecated protected boolean onLongPress(android.inputmethodservice.Keyboard.Key);
@@ -22641,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
@@ -23153,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 {
@@ -23377,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();
}
@@ -23450,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
@@ -23457,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 {
@@ -24759,7 +24783,7 @@ package android.media {
@Deprecated public class RemoteControlClient.MetadataEditor extends android.media.MediaMetadataEditor {
method @Deprecated public void apply();
- method public Object clone() throws java.lang.CloneNotSupportedException;
+ method @Deprecated public Object clone() throws java.lang.CloneNotSupportedException;
method @Deprecated public android.media.RemoteControlClient.MetadataEditor putBitmap(int, android.graphics.Bitmap) throws java.lang.IllegalArgumentException;
method @Deprecated public android.media.RemoteControlClient.MetadataEditor putLong(int, long) throws java.lang.IllegalArgumentException;
method @Deprecated public android.media.RemoteControlClient.MetadataEditor putObject(int, Object) throws java.lang.IllegalArgumentException;
@@ -27613,6 +27637,7 @@ package android.media.tv {
method @NonNull public java.util.List<android.media.AudioPresentation> getAudioPresentations();
method public String getSelectedTrack(int);
method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
+ method protected void onLayout(boolean, int, int, int, int);
method public boolean onUnhandledInputEvent(android.view.InputEvent);
method public void overrideTvAppAttributionSource(@NonNull android.content.AttributionSource);
method public void reset();
@@ -28950,6 +28975,7 @@ package android.net.vcn {
method @NonNull public long[] getRetryIntervalsMillis();
method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
method public boolean hasGatewayOption(int);
+ method @FlaggedApi("android.net.vcn.safe_mode_config") public boolean isSafeModeEnabled();
field public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0; // 0x0
}
@@ -28963,6 +28989,7 @@ package android.net.vcn {
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
+ method @FlaggedApi("android.net.vcn.safe_mode_config") @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setSafeModeEnabled(boolean);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
}
@@ -32321,6 +32348,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
@@ -32661,7 +32689,7 @@ package android.os {
field public static final int S_V2 = 32; // 0x20
field public static final int TIRAMISU = 33; // 0x21
field public static final int UPSIDE_DOWN_CAKE = 34; // 0x22
- field public static final int VANILLA_ICE_CREAM = 10000; // 0x2710
+ field @FlaggedApi("android.os.android_os_build_vanilla_ice_cream") public static final int VANILLA_ICE_CREAM = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -33962,7 +33990,7 @@ package android.os {
field public static final String DISALLOW_MICROPHONE_TOGGLE = "disallow_microphone_toggle";
field public static final String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
field public static final String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media";
- field public static final String DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO = "no_near_field_communication_radio";
+ field @FlaggedApi("android.nfc.enable_nfc_user_restriction") public static final String DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO = "no_near_field_communication_radio";
field public static final String DISALLOW_NETWORK_RESET = "no_network_reset";
field public static final String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";
field public static final String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
@@ -44040,7 +44068,6 @@ package android.telephony {
method public int getLongitude();
method public int getNetworkId();
method public int getSystemId();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
}
@@ -44056,7 +44083,6 @@ package android.telephony {
method @Nullable public String getMncString();
method @Nullable public String getMobileNetworkOperator();
method @Deprecated public int getPsc();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityGsm> CREATOR;
}
@@ -44074,7 +44100,6 @@ package android.telephony {
method @Nullable public String getMobileNetworkOperator();
method public int getPci();
method public int getTac();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityLte> CREATOR;
}
@@ -44087,7 +44112,6 @@ package android.telephony {
method @IntRange(from=0, to=3279165) public int getNrarfcn();
method @IntRange(from=0, to=1007) public int getPci();
method @IntRange(from=0, to=16777215) public int getTac();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityNr> CREATOR;
}
@@ -44101,7 +44125,6 @@ package android.telephony {
method @Nullable public String getMncString();
method @Nullable public String getMobileNetworkOperator();
method public int getUarfcn();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR;
}
@@ -44117,7 +44140,6 @@ package android.telephony {
method @Nullable public String getMobileNetworkOperator();
method public int getPsc();
method public int getUarfcn();
- method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellIdentityWcdma> CREATOR;
}
@@ -44201,6 +44223,7 @@ package android.telephony {
public final class CellSignalStrengthCdma extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method public int getCdmaDbm();
method public int getCdmaEcio();
@@ -44211,24 +44234,28 @@ package android.telephony {
method public int getEvdoLevel();
method public int getEvdoSnr();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthCdma> CREATOR;
}
public final class CellSignalStrengthGsm extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method public int getBitErrorRate();
method public int getDbm();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
method public int getRssi();
method public int getTimingAdvance();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
}
public final class CellSignalStrengthLte extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method @IntRange(from=0, to=15) public int getCqi();
method @IntRange(from=1, to=6) public int getCqiTableIndex();
@@ -44239,12 +44266,14 @@ package android.telephony {
method public int getRssi();
method public int getRssnr();
method public int getTimingAdvance();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthLte> CREATOR;
}
public final class CellSignalStrengthNr extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method @IntRange(from=0, to=15) @NonNull public java.util.List<java.lang.Integer> getCsiCqiReport();
method @IntRange(from=1, to=3) public int getCsiCqiTableIndex();
@@ -44257,26 +44286,31 @@ package android.telephony {
method public int getSsRsrq();
method public int getSsSinr();
method @IntRange(from=0, to=1282) public int getTimingAdvanceMicros();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthNr> CREATOR;
}
public final class CellSignalStrengthTdscdma extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method public int getDbm();
method @IntRange(from=0, to=4) public int getLevel();
method public int getRscp();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthTdscdma> CREATOR;
}
public final class CellSignalStrengthWcdma extends android.telephony.CellSignalStrength implements android.os.Parcelable {
method public int describeContents();
+ method public boolean equals(Object);
method public int getAsuLevel();
method public int getDbm();
method public int getEcNo();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
+ method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR;
}
@@ -47056,6 +47090,7 @@ package android.text {
method @Deprecated public int length();
method @Deprecated public static android.text.AlteredCharSequence make(CharSequence, char[], int, int);
method @Deprecated public CharSequence subSequence(int, int);
+ method @Deprecated public String toString();
}
@Deprecated public class AndroidCharacter {
@@ -47469,6 +47504,7 @@ package android.text {
method public void removeSpan(Object);
method public void setSpan(Object, int, int, int);
method public CharSequence subSequence(int, int);
+ method public String toString();
}
public static final class PrecomputedText.Params {
@@ -47598,6 +47634,7 @@ package android.text {
method public void setFilters(android.text.InputFilter[]);
method public void setSpan(Object, int, int, int);
method public CharSequence subSequence(int, int);
+ method public String toString();
method public static android.text.SpannableStringBuilder valueOf(CharSequence);
}
@@ -47771,6 +47808,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);
}
@@ -49224,7 +49262,10 @@ package android.util {
method public boolean containsValue(Object);
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);
method public int indexOfValue(Object);
method public boolean isEmpty();
@@ -49236,6 +49277,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();
@@ -49256,7 +49298,9 @@ package android.util {
method public boolean contains(Object);
method public boolean containsAll(java.util.Collection<?>);
method public void ensureCapacity(int);
+ method public boolean equals(@Nullable Object);
method public void forEach(java.util.function.Consumer<? super E>);
+ method public int hashCode();
method public int indexOf(Object);
method public boolean isEmpty();
method public java.util.Iterator<E> iterator();
@@ -49264,6 +49308,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();
@@ -53118,6 +53163,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();
@@ -53159,6 +53205,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);
@@ -55318,12 +55365,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);
@@ -55331,6 +55380,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 {
@@ -55665,6 +55715,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();
@@ -55673,18 +55724,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 {
@@ -57952,6 +58014,7 @@ package android.widget {
ctor @Deprecated public AbsoluteLayout(android.content.Context, android.util.AttributeSet);
ctor @Deprecated public AbsoluteLayout(android.content.Context, android.util.AttributeSet, int);
ctor @Deprecated public AbsoluteLayout(android.content.Context, android.util.AttributeSet, int, int);
+ method @Deprecated protected void onLayout(boolean, int, int, int, int);
}
@Deprecated public static class AbsoluteLayout.LayoutParams extends android.view.ViewGroup.LayoutParams {
@@ -58030,6 +58093,7 @@ package android.widget {
method public long getSelectedItemId();
method public int getSelectedItemPosition();
method public abstract android.view.View getSelectedView();
+ method protected void onLayout(boolean, int, int, int, int);
method public boolean performItemClick(android.view.View, int, long);
method public abstract void setAdapter(T);
method public void setEmptyView(android.view.View);
@@ -58249,6 +58313,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();
@@ -58676,6 +58741,7 @@ package android.widget {
method public android.widget.FrameLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
method @Deprecated public boolean getConsiderGoneChildrenWhenMeasuring();
method public boolean getMeasureAllChildren();
+ method protected void onLayout(boolean, int, int, int, int);
method public void setMeasureAllChildren(boolean);
}
@@ -58729,6 +58795,7 @@ package android.widget {
method public boolean getUseDefaultMargins();
method public boolean isColumnOrderPreserved();
method public boolean isRowOrderPreserved();
+ method protected void onLayout(boolean, int, int, int, int);
method public void setAlignmentMode(int);
method public void setColumnCount(int);
method public void setColumnOrderPreserved(boolean);
@@ -58951,6 +59018,7 @@ package android.widget {
method public float getWeightSum();
method public boolean isBaselineAligned();
method public boolean isMeasureWithLargestChildEnabled();
+ method protected void onLayout(boolean, int, int, int, int);
method public void setBaselineAligned(boolean);
method public void setBaselineAlignedChildIndex(int);
method public void setDividerDrawable(android.graphics.drawable.Drawable);
@@ -59499,6 +59567,7 @@ package android.widget {
method public android.widget.RelativeLayout.LayoutParams generateLayoutParams(android.util.AttributeSet);
method public int getGravity();
method public int getIgnoreGravity();
+ method protected void onLayout(boolean, int, int, int, int);
method public void setGravity(int);
method public void setHorizontalGravity(int);
method public void setIgnoreGravity(int);
@@ -59953,6 +60022,7 @@ package android.widget {
method @Deprecated public boolean isMoving();
method @Deprecated public boolean isOpened();
method @Deprecated public void lock();
+ method @Deprecated protected void onLayout(boolean, int, int, int, int);
method @Deprecated public void open();
method @Deprecated public void setOnDrawerCloseListener(android.widget.SlidingDrawer.OnDrawerCloseListener);
method @Deprecated public void setOnDrawerOpenListener(android.widget.SlidingDrawer.OnDrawerOpenListener);
@@ -60597,6 +60667,7 @@ package android.widget {
method public boolean hideOverflowMenu();
method public void inflateMenu(@MenuRes int);
method public boolean isOverflowMenuShowing();
+ method protected void onLayout(boolean, int, int, int, int);
method public void setCollapseContentDescription(@StringRes int);
method public void setCollapseContentDescription(@Nullable CharSequence);
method public void setCollapseIcon(@DrawableRes int);
@@ -60751,7 +60822,7 @@ package android.widget {
method @Deprecated public android.view.View getZoomControls();
method @Deprecated public boolean isAutoDismissed();
method @Deprecated public boolean isVisible();
- method public boolean onTouch(android.view.View, android.view.MotionEvent);
+ method @Deprecated public boolean onTouch(android.view.View, android.view.MotionEvent);
method @Deprecated public void setAutoDismissed(boolean);
method @Deprecated public void setFocusable(boolean);
method @Deprecated public void setOnZoomListener(android.widget.ZoomButtonsController.OnZoomListener);
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
new file mode 100644
index 000000000000..f1c82796d559
--- /dev/null
+++ b/core/api/lint-baseline.txt
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+UnflaggedApi: android.content.pm.PackageManager#FEATURE_THREAD_NETWORK:
+ New API must be flagged with @FlaggedApi: field android.content.pm.PackageManager.FEATURE_THREAD_NETWORK
+UnflaggedApi: android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM:
+ New API must be flagged with @FlaggedApi: field android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM
+UnflaggedApi: android.os.UserManager#DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO:
+ New API must be flagged with @FlaggedApi: field android.os.UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 01cf77abd3ed..1a22e9b10ad1 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android {
public static final class Manifest.permission {
@@ -16,7 +14,7 @@ package android.app {
@UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
method public final boolean addDumpable(@NonNull android.util.Dumpable);
- method public final boolean isResumed();
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public final boolean isResumed();
}
public class ActivityManager {
diff --git a/core/api/module-lib-lint-baseline.txt b/core/api/module-lib-lint-baseline.txt
index 27436ce35867..16b8c429ac07 100644
--- a/core/api/module-lib-lint-baseline.txt
+++ b/core/api/module-lib-lint-baseline.txt
@@ -57,3 +57,11 @@ SamShouldBeLast: android.os.IBinder#linkToDeath(android.os.IBinder.DeathRecipien
SAM-compatible parameters (such as parameter 1, "recipient", in android.os.IBinder.linkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.os.IBinder#unlinkToDeath(android.os.IBinder.DeathRecipient, int):
SAM-compatible parameters (such as parameter 1, "recipient", in android.os.IBinder.unlinkToDeath) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
+
+UnflaggedApi: android.app.Activity#isResumed():
+ New API must be flagged with @FlaggedApi: method android.app.Activity.isResumed()
+UnflaggedApi: android.content.Context#REMOTE_AUTH_SERVICE:
+ New API must be flagged with @FlaggedApi: field android.content.Context.REMOTE_AUTH_SERVICE
+UnflaggedApi: android.os.IpcDataCache#MODULE_TELEPHONY:
+ New API must be flagged with @FlaggedApi: field android.os.IpcDataCache.MODULE_TELEPHONY
diff --git a/core/api/module-lib-removed.txt b/core/api/module-lib-removed.txt
index 14191ebcb080..d802177e249b 100644
--- a/core/api/module-lib-removed.txt
+++ b/core/api/module-lib-removed.txt
@@ -1,3 +1 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/core/api/removed.txt b/core/api/removed.txt
index ede88a29624d..8b3696a1e6d9 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android.app {
public class Notification implements android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 40eee4c277ac..3d0101167782 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android {
public static final class Manifest.permission {
@@ -359,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";
@@ -3483,7 +3482,7 @@ package android.content {
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final String TETHERING_SERVICE = "tethering";
- field public static final String THREAD_NETWORK_SERVICE = "thread_network";
+ field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String THREAD_NETWORK_SERVICE = "thread_network";
field public static final String TIME_MANAGER_SERVICE = "time_manager";
field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
@@ -4042,7 +4041,9 @@ package android.content.pm {
}
public static final class PackageManager.UninstallCompleteCallback implements android.os.Parcelable {
+ method public int describeContents();
method public void onUninstallComplete(@NonNull String, int, @Nullable String);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.UninstallCompleteCallback> CREATOR;
}
@@ -5941,6 +5942,7 @@ package android.hardware.soundtrigger {
public static final class SoundTrigger.Keyphrase implements android.os.Parcelable {
ctor public SoundTrigger.Keyphrase(int, int, @NonNull java.util.Locale, @NonNull String, @Nullable int[]);
+ method public int describeContents();
method public int getId();
method @NonNull public java.util.Locale getLocale();
method public int getRecognitionModes();
@@ -5963,6 +5965,7 @@ package android.hardware.soundtrigger {
public static final class SoundTrigger.KeyphraseSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable {
ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int);
ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[]);
+ method public int describeContents();
method @NonNull public android.hardware.soundtrigger.SoundTrigger.Keyphrase[] getKeyphrases();
method @NonNull public static android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel readFromParcel(@NonNull android.os.Parcel);
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -5970,6 +5973,7 @@ package android.hardware.soundtrigger {
}
public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
+ method public int describeContents();
method public int getEnd();
method public int getStart();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -7412,6 +7416,7 @@ package android.media.musicrecognition {
public abstract class MusicRecognitionService extends android.app.Service {
ctor public MusicRecognitionService();
+ method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onRecognize(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.media.musicrecognition.MusicRecognitionService.Callback);
}
@@ -7470,6 +7475,7 @@ package android.media.soundtrigger {
public abstract class SoundTriggerDetectionService extends android.app.Service {
ctor public SoundTriggerDetectionService();
+ method public final android.os.IBinder onBind(android.content.Intent);
method @MainThread public void onConnected(@NonNull java.util.UUID, @Nullable android.os.Bundle);
method @MainThread public void onDisconnected(@NonNull java.util.UUID, @Nullable android.os.Bundle);
method @MainThread public void onError(@NonNull java.util.UUID, @Nullable android.os.Bundle, int, int);
@@ -8183,6 +8189,7 @@ package android.media.tv.tuner.filter {
}
public class MediaEvent extends android.media.tv.tuner.filter.FilterEvent {
+ method protected void finalize();
method public long getAudioHandle();
method @NonNull public java.util.List<android.media.AudioPresentation> getAudioPresentations();
method public long getAvDataId();
@@ -9577,6 +9584,8 @@ package android.net {
package android.net.metrics {
@Deprecated public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
}
@Deprecated public static final class ApfProgramEvent.Builder {
@@ -9591,6 +9600,8 @@ package android.net.metrics {
}
@Deprecated public final class ApfStats implements android.net.metrics.IpConnectivityLog.Event {
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
}
@Deprecated public static final class ApfStats.Builder {
@@ -9609,6 +9620,8 @@ package android.net.metrics {
}
@Deprecated public final class DhcpClientEvent implements android.net.metrics.IpConnectivityLog.Event {
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
}
@Deprecated public static final class DhcpClientEvent.Builder {
@@ -9620,7 +9633,9 @@ package android.net.metrics {
@Deprecated public final class DhcpErrorEvent implements android.net.metrics.IpConnectivityLog.Event {
ctor @Deprecated public DhcpErrorEvent(int);
+ method @Deprecated public int describeContents();
method @Deprecated public static int errorCodeWithOption(int, int);
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
field @Deprecated public static final int BOOTP_TOO_SHORT = 67174400; // 0x4010000
field @Deprecated public static final int BUFFER_UNDERFLOW = 83951616; // 0x5010000
field @Deprecated public static final int DHCP_BAD_MAGIC_COOKIE = 67239936; // 0x4020000
@@ -9658,6 +9673,8 @@ package android.net.metrics {
@Deprecated public final class IpManagerEvent implements android.net.metrics.IpConnectivityLog.Event {
ctor @Deprecated public IpManagerEvent(int, long);
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
field @Deprecated public static final int COMPLETE_LIFECYCLE = 3; // 0x3
field @Deprecated public static final int ERROR_INTERFACE_NOT_FOUND = 8; // 0x8
field @Deprecated public static final int ERROR_INVALID_PROVISIONING = 7; // 0x7
@@ -9670,6 +9687,8 @@ package android.net.metrics {
@Deprecated public final class IpReachabilityEvent implements android.net.metrics.IpConnectivityLog.Event {
ctor @Deprecated public IpReachabilityEvent(int);
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
field @Deprecated public static final int NUD_FAILED = 512; // 0x200
field @Deprecated public static final int NUD_FAILED_ORGANIC = 1024; // 0x400
field @Deprecated public static final int PROBE = 256; // 0x100
@@ -9680,6 +9699,8 @@ package android.net.metrics {
@Deprecated public final class NetworkEvent implements android.net.metrics.IpConnectivityLog.Event {
ctor @Deprecated public NetworkEvent(int, long);
ctor @Deprecated public NetworkEvent(int);
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
field @Deprecated public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4
field @Deprecated public static final int NETWORK_CONNECTED = 1; // 0x1
field @Deprecated public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; // 0xc
@@ -9696,6 +9717,8 @@ package android.net.metrics {
}
@Deprecated public final class RaEvent implements android.net.metrics.IpConnectivityLog.Event {
+ method @Deprecated public int describeContents();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
}
@Deprecated public static final class RaEvent.Builder {
@@ -9710,7 +9733,9 @@ package android.net.metrics {
}
@Deprecated public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event {
+ method @Deprecated public int describeContents();
method @Deprecated @NonNull public static String getProbeName(int);
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
field @Deprecated public static final int DNS_FAILURE = 0; // 0x0
field @Deprecated public static final int DNS_SUCCESS = 1; // 0x1
field @Deprecated public static final int PROBE_DNS = 0; // 0x0
@@ -10220,6 +10245,7 @@ package android.nfc {
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState();
method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
@@ -10227,8 +10253,10 @@ package android.nfc {
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setReaderMode(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff
field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0
field public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2; // 0xfffffffe
@@ -10276,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);
@@ -10286,10 +10315,15 @@ 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;
}
+ public final class CardEmulation {
+ method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int);
+ }
+
@FlaggedApi("android.nfc.enable_nfc_mainline") public final class NfcFServiceInfo implements android.os.Parcelable {
ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
@@ -10764,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);
@@ -12108,6 +12143,7 @@ package android.service.assist.classification {
public abstract class FieldClassificationService extends android.app.Service {
ctor public FieldClassificationService();
+ method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onClassificationRequest(@NonNull android.service.assist.classification.FieldClassificationRequest, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.service.assist.classification.FieldClassificationResponse,java.lang.Exception>);
method public void onConnected();
method public void onDisconnected();
@@ -12186,6 +12222,7 @@ package android.service.autofill.augmented {
method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]);
method @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
+ method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
method public void onDisconnected();
method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
@@ -12224,6 +12261,7 @@ package android.service.autofill.augmented {
public final class FillWindow implements java.lang.AutoCloseable {
ctor public FillWindow();
+ method public void close();
method public void destroy();
method public boolean update(@NonNull android.service.autofill.augmented.PresentationParams.Area, @NonNull android.view.View, long);
}
@@ -12249,6 +12287,7 @@ package android.service.carrier {
public final class CarrierMessagingServiceWrapper implements java.lang.AutoCloseable {
ctor public CarrierMessagingServiceWrapper();
method public boolean bindToCarrierMessagingService(@NonNull android.content.Context, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull Runnable);
+ method public void close();
method public void disconnect();
method public void downloadMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback);
method public void receiveSms(@NonNull android.service.carrier.MessagePdu, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallback);
@@ -12299,6 +12338,7 @@ package android.service.contentcapture {
method public final void disableSelf();
method public void onActivityEvent(@NonNull android.service.contentcapture.ActivityEvent);
method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData);
+ method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent);
method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId);
@@ -12337,6 +12377,7 @@ package android.service.contentsuggestions {
public abstract class ContentSuggestionsService extends android.app.Service {
ctor public ContentSuggestionsService();
+ method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onClassifyContentSelections(@NonNull android.app.contentsuggestions.ClassificationsRequest, @NonNull android.app.contentsuggestions.ContentSuggestionsManager.ClassificationsCallback);
method public abstract void onNotifyInteraction(@NonNull String, @NonNull android.os.Bundle);
method public abstract void onProcessContextImage(int, @Nullable android.graphics.Bitmap, @NonNull android.os.Bundle);
@@ -12350,6 +12391,7 @@ package android.service.dataloader {
public abstract class DataLoaderService extends android.app.Service {
ctor public DataLoaderService();
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader(@NonNull android.content.pm.DataLoaderParams);
}
@@ -13022,6 +13064,7 @@ package android.service.tracing {
public class TraceReportService extends android.app.Service {
ctor public TraceReportService();
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public void onReportTrace(@NonNull android.service.tracing.TraceReportService.TraceParams);
}
@@ -13610,6 +13653,7 @@ package android.telecom {
method @Nullable public android.content.ComponentName getCallScreeningComponent();
method public boolean isBlocked();
method public boolean isInContacts();
+ method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.Connection.CallFilteringCompletionInfo> CREATOR;
}
@@ -13918,6 +13962,7 @@ package android.telephony {
method public int getReason();
method public int getTimeoutSeconds();
method public boolean isEnabled();
+ method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallForwardingInfo> CREATOR;
field public static final int REASON_ALL = 4; // 0x4
field public static final int REASON_ALL_CONDITIONAL = 5; // 0x5
@@ -14202,6 +14247,7 @@ package android.telephony {
method public int getDownlinkCapacityKbps();
method public int getType();
method public int getUplinkCapacityKbps();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LinkCapacityEstimate> CREATOR;
field public static final int INVALID = -1; // 0xffffffff
field public static final int LCE_TYPE_COMBINED = 2; // 0x2
@@ -14211,8 +14257,10 @@ package android.telephony {
public final class LteVopsSupportInfo extends android.telephony.VopsSupportInfo {
ctor public LteVopsSupportInfo(int, int);
+ method public boolean equals(@Nullable Object);
method public int getEmcBearerSupport();
method public int getVopsSupport();
+ method public int hashCode();
method public boolean isEmergencyServiceFallbackSupported();
method public boolean isEmergencyServiceSupported();
method public boolean isVopsSupported();
@@ -14312,9 +14360,11 @@ package android.telephony {
public final class NrVopsSupportInfo extends android.telephony.VopsSupportInfo {
ctor public NrVopsSupportInfo(int, int, int);
+ method public boolean equals(@Nullable Object);
method public int getEmcSupport();
method public int getEmfSupport();
method public int getVopsSupport();
+ method public int hashCode();
method public boolean isEmergencyServiceFallbackSupported();
method public boolean isEmergencyServiceSupported();
method public boolean isVopsSupported();
@@ -15427,6 +15477,7 @@ package android.telephony.data {
public abstract class QualifiedNetworksService extends android.app.Service {
ctor public QualifiedNetworksService();
+ method public android.os.IBinder onBind(android.content.Intent);
method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int);
field public static final String QUALIFIED_NETWORKS_SERVICE_INTERFACE = "android.telephony.data.QualifiedNetworksService";
}
@@ -15618,6 +15669,7 @@ package android.telephony.gba {
public class GbaService extends android.app.Service {
ctor public GbaService();
method public void onAuthenticationRequest(int, int, int, @NonNull android.net.Uri, @NonNull byte[], boolean);
+ method public android.os.IBinder onBind(android.content.Intent);
method public final void reportAuthenticationFailure(int, int) throws java.lang.RuntimeException;
method public final void reportKeysAvailable(int, @NonNull byte[], @NonNull String) throws java.lang.RuntimeException;
field public static final String SERVICE_INTERFACE = "android.telephony.gba.GbaService";
@@ -16120,6 +16172,7 @@ package android.telephony.ims {
method @Deprecated public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
method @NonNull public android.telephony.ims.stub.ImsRegistrationImplBase getRegistrationForSubscription(int, int);
method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
+ method public android.os.IBinder onBind(android.content.Intent);
method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException;
method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures();
method public void readyForFeatureCreation();
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index 71c02dc42c62..1e9e9be881d3 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -226,6 +226,11 @@ SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplora
SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams):
SAM-compatible parameters (such as parameter 2, "filePathCallback", in android.webkit.WebChromeClient.onShowFileChooser) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
+UnflaggedApi: android.Manifest.permission#REGISTER_NSD_OFFLOAD_ENGINE:
+ New API must be flagged with @FlaggedApi: field android.Manifest.permission.REGISTER_NSD_OFFLOAD_ENGINE
+UnflaggedApi: android.content.Context#THREAD_NETWORK_SERVICE:
+ New API must be flagged with @FlaggedApi: field android.content.Context.THREAD_NETWORK_SERVICE
UnflaggedApi: android.nfc.cardemulation.AidGroup#CONTENTS_FILE_DESCRIPTOR:
New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.AidGroup.CONTENTS_FILE_DESCRIPTOR
UnflaggedApi: android.nfc.cardemulation.AidGroup#PARCELABLE_WRITE_RETURN_VALUE:
@@ -238,3 +243,7 @@ UnflaggedApi: android.nfc.cardemulation.NfcFServiceInfo#CONTENTS_FILE_DESCRIPTOR
New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.NfcFServiceInfo.CONTENTS_FILE_DESCRIPTOR
UnflaggedApi: android.nfc.cardemulation.NfcFServiceInfo#PARCELABLE_WRITE_RETURN_VALUE:
New API must be flagged with @FlaggedApi: field android.nfc.cardemulation.NfcFServiceInfo.PARCELABLE_WRITE_RETURN_VALUE
+UnflaggedApi: android.telephony.mbms.vendor.MbmsDownloadServiceBase#DESCRIPTOR:
+ New API must be flagged with @FlaggedApi: field android.telephony.mbms.vendor.MbmsDownloadServiceBase.DESCRIPTOR
+UnflaggedApi: android.telephony.mbms.vendor.MbmsStreamingServiceBase#DESCRIPTOR:
+ New API must be flagged with @FlaggedApi: field android.telephony.mbms.vendor.MbmsStreamingServiceBase.DESCRIPTOR
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 4161330dcb37..1c10356c6b03 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android.app {
public class AppOpsManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8d0e396f110d..7742b86447e6 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android {
public static final class Manifest.permission {
@@ -3932,7 +3930,9 @@ package android.widget.inline {
package android.window {
public final class BackNavigationInfo implements android.os.Parcelable {
+ method public int describeContents();
method @NonNull public static String typeToString(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.window.BackNavigationInfo> CREATOR;
field public static final String KEY_TRIGGER_BACK = "TriggerBack";
field public static final int TYPE_CALLBACK = 4; // 0x4
@@ -3993,11 +3993,13 @@ package android.window {
}
public final class TaskFragmentCreationParams implements android.os.Parcelable {
+ method public int describeContents();
method @NonNull public android.os.IBinder getFragmentToken();
method @NonNull public android.graphics.Rect getInitialRelativeBounds();
method @NonNull public android.window.TaskFragmentOrganizerToken getOrganizer();
method @NonNull public android.os.IBinder getOwnerToken();
method public int getWindowingMode();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentCreationParams> CREATOR;
}
@@ -4009,6 +4011,7 @@ package android.window {
}
public final class TaskFragmentInfo implements android.os.Parcelable {
+ method public int describeContents();
method public boolean equalsForTaskFragmentOrganizer(@Nullable android.window.TaskFragmentInfo);
method @NonNull public java.util.List<android.os.IBinder> getActivities();
method @NonNull public java.util.List<android.os.IBinder> getActivitiesRequestedInTaskFragment();
@@ -4022,6 +4025,7 @@ package android.window {
method public boolean isEmpty();
method public boolean isTaskClearedForReuse();
method public boolean isVisible();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentInfo> CREATOR;
}
@@ -4044,6 +4048,8 @@ package android.window {
}
public final class TaskFragmentOrganizerToken implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentOrganizerToken> CREATOR;
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index cf0264353159..98f492f1de6d 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -145,6 +145,10 @@ IntentName: android.provider.Telephony.Sms.Intents#SMS_CARRIER_PROVISION_ACTION:
Intent action constant name must be ACTION_FOO: SMS_CARRIER_PROVISION_ACTION
+KotlinKeyword: android.app.Notification#when:
+ Avoid field names that are Kotlin hard keywords ("when"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords
+
+
KotlinOperator: android.os.PackageTagsList#contains(android.os.PackageTagsList):
Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
KotlinOperator: android.util.SparseArrayMap#get(int, K):
@@ -761,6 +765,8 @@ MissingNullability: android.widget.TimePicker#getMinuteView():
Missing nullability on method `getMinuteView` return
MissingNullability: android.widget.TimePicker#getPmView():
Missing nullability on method `getPmView` return
+MissingNullability: android.window.TaskFragmentOrganizerToken#writeToParcel(android.os.Parcel, int) parameter #0:
+ Missing nullability on parameter `dest` in method `writeToParcel`
MutableBareField: android.content.AutofillOptions#appDisabledExpiration:
@@ -963,16 +969,100 @@ RethrowRemoteException: android.app.ActivityManager#resumeAppSwitches():
Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+SamShouldBeLast: android.animation.ValueAnimator#ofObject(android.animation.TypeEvaluator, java.lang.Object...):
+ SAM-compatible parameters (such as parameter 1, "evaluator", in android.animation.ValueAnimator.ofObject) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.Activity#convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions):
+ SAM-compatible parameters (such as parameter 1, "callback", in android.app.Activity.convertToTranslucent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.app.ActivityManager.addOnUidImportanceListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#set(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 4, "listener", in android.app.AlarmManager.set) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#set(int, long, long, long, android.app.AlarmManager.OnAlarmListener, android.os.Handler, android.os.WorkSource):
+ SAM-compatible parameters (such as parameter 5, "listener", in android.app.AlarmManager.set) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#setExact(int, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 4, "listener", in android.app.AlarmManager.setExact) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.AlarmManager#setWindow(int, long, long, String, android.app.AlarmManager.OnAlarmListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 5, "listener", in android.app.AlarmManager.setWindow) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler):
+ SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, String):
+ SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, String, android.os.Bundle):
+ SAM-compatible parameters (such as parameter 4, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.PendingIntent#send(int, android.app.PendingIntent.OnFinished, android.os.Handler):
+ SAM-compatible parameters (such as parameter 2, "onFinished", in android.app.PendingIntent.send) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.UiAutomation#executeAndWaitForEvent(Runnable, android.app.UiAutomation.AccessibilityEventFilter, long):
+ SAM-compatible parameters (such as parameter 2, "filter", in android.app.UiAutomation.executeAndWaitForEvent) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.app.WallpaperManager#addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.app.WallpaperManager.addOnColorsChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ActivityInfo#dump(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ActivityInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ApplicationInfo#dump(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ApplicationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ComponentInfo#dumpBack(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ComponentInfo.dumpBack) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.ComponentInfo#dumpFront(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.ComponentInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.PackageItemInfo#dumpBack(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpBack) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.content.pm.PackageItemInfo#dumpFront(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.content.pm.PackageItemInfo.dumpFront) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.database.sqlite.SQLiteCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
+ SAM-compatible parameters (such as parameter 1, "factory", in android.database.sqlite.SQLiteCursorDriver.query) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.database.sqlite.SQLiteDebug#dump(android.util.Printer, String[]):
SAM-compatible parameters (such as parameter 1, "printer", in android.database.sqlite.SQLiteDebug.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.database.sqlite.SQLiteDirectCursorDriver#query(android.database.sqlite.SQLiteDatabase.CursorFactory, String[]):
SAM-compatible parameters (such as parameter 1, "factory", in android.database.sqlite.SQLiteDirectCursorDriver.query) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.AdaptiveIconDrawable#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.AdaptiveIconDrawable.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.Drawable#scheduleSelf(Runnable, long):
+ SAM-compatible parameters (such as parameter 1, "what", in android.graphics.drawable.Drawable.scheduleSelf) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.Drawable.Callback#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.Drawable.Callback.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.graphics.drawable.LayerDrawable#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.graphics.drawable.LayerDrawable.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.location.LocationManager#addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.location.LocationManager.addNmeaListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(String, long, float, android.location.LocationListener, android.os.Looper):
+ SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper):
+ SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.location.LocationManager#requestLocationUpdates(long, float, android.location.Criteria, android.location.LocationListener, android.os.Looper):
+ SAM-compatible parameters (such as parameter 4, "listener", in android.location.LocationManager.requestLocationUpdates) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(String, android.location.LocationListener, android.os.Looper):
+ SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper):
+ SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.abandonAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioManager#requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int):
+ SAM-compatible parameters (such as parameter 1, "l", in android.media.AudioManager.requestAudioFocus) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioRecord.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioRecord#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioRecord.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.AudioTrack#addOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.AudioTrack.addOnRoutingChangedListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.media.MediaCas#setEventListener(android.media.MediaCas.EventListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.media.MediaCas.setEventListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.os.Parcel#createFixedArray(Class<T>, java.util.function.Function<android.os.IBinder,S>, int...):
+ SAM-compatible parameters (such as parameter 2, "asInterface", in android.os.Parcel.createFixedArray) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String):
SAM-compatible parameters (such as parameter 1, "pw", in android.os.StrictMode.ViolationInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
SAM-compatible parameters (such as parameter 3, "callback", in android.permission.PermissionControllerManager.countPermissionApps) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
SAM-compatible parameters (such as parameter 2, "callback", in android.permission.PermissionControllerManager.getAppPermissions) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], String, int, String):
+ SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, String[], java.security.Principal[], android.net.Uri, String):
+ SAM-compatible parameters (such as parameter 2, "response", in android.security.KeyChain.choosePrivateKeyAlias) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.service.autofill.CharSequenceTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
SAM-compatible parameters (such as parameter 1, "finder", in android.service.autofill.CharSequenceTransformation.apply) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.service.autofill.DateTransformation#apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int):
@@ -985,8 +1075,24 @@ SamShouldBeLast: android.view.Choreographer#postCallback(int, Runnable, Object):
SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.postCallback) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long):
SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.postCallbackDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.Choreographer#postFrameCallbackDelayed(android.view.Choreographer.FrameCallback, long):
+ SAM-compatible parameters (such as parameter 1, "callback", in android.view.Choreographer.postFrameCallbackDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
SamShouldBeLast: android.view.Choreographer#removeCallbacks(int, Runnable, Object):
SAM-compatible parameters (such as parameter 2, "action", in android.view.Choreographer.removeCallbacks) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#postDelayed(Runnable, long):
+ SAM-compatible parameters (such as parameter 1, "action", in android.view.View.postDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#postOnAnimationDelayed(Runnable, long):
+ SAM-compatible parameters (such as parameter 1, "action", in android.view.View.postOnAnimationDelayed) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.View#scheduleDrawable(android.graphics.drawable.Drawable, Runnable, long):
+ SAM-compatible parameters (such as parameter 2, "what", in android.view.View.scheduleDrawable) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.Window#addOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.view.Window.addOnFrameMetricsAvailableListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.accessibility.AccessibilityManager#addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addAccessibilityStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.accessibility.AccessibilityManager#addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, android.os.Handler):
+ SAM-compatible parameters (such as parameter 1, "listener", in android.view.accessibility.AccessibilityManager.addTouchExplorationStateChangeListener) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+SamShouldBeLast: android.view.inputmethod.InputMethodInfo#dump(android.util.Printer, String):
+ SAM-compatible parameters (such as parameter 1, "pw", in android.view.inputmethod.InputMethodInfo.dump) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
StaticUtils: android.os.health.HealthKeys:
@@ -1003,6 +1109,48 @@ StreamFiles: android.os.FileUtils#contains(java.io.File, java.io.File):
Methods accepting `File` should also accept `FileDescriptor` or streams: method android.os.FileUtils.contains(java.io.File,java.io.File)
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#PERMITTED_INPUT_METHODS_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#PERSONAL_APPS_SUSPENDED_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.PERSONAL_APPS_SUSPENDED_POLICY
+UnflaggedApi: android.app.admin.DevicePolicyIdentifiers#SCREEN_CAPTURE_DISABLED_POLICY:
+ New API must be flagged with @FlaggedApi: field android.app.admin.DevicePolicyIdentifiers.SCREEN_CAPTURE_DISABLED_POLICY
+UnflaggedApi: android.media.soundtrigger.SoundTriggerInstrumentation#setInPhoneCallState(boolean):
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerInstrumentation.setInPhoneCallState(boolean)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#createManagerForModule(android.hardware.soundtrigger.SoundTrigger.ModuleProperties):
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.createManagerForModule(android.hardware.soundtrigger.SoundTrigger.ModuleProperties)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#createManagerForTestModule():
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.createManagerForTestModule()
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#listModuleProperties():
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.listModuleProperties()
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager#loadSoundModel(android.hardware.soundtrigger.SoundTrigger.SoundModel):
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.loadSoundModel(android.hardware.soundtrigger.SoundTrigger.SoundModel)
+UnflaggedApi: android.media.soundtrigger.SoundTriggerManager.Model#getSoundModel():
+ New API must be flagged with @FlaggedApi: method android.media.soundtrigger.SoundTriggerManager.Model.getSoundModel()
+UnflaggedApi: android.os.BatteryManager#BATTERY_PLUGGED_ANY:
+ New API must be flagged with @FlaggedApi: field android.os.BatteryManager.BATTERY_PLUGGED_ANY
+UnflaggedApi: android.os.UserHandle#USER_CURRENT:
+ New API must be flagged with @FlaggedApi: field android.os.UserHandle.USER_CURRENT
+UnflaggedApi: android.os.UserManager#getAliveUsers():
+ New API must be flagged with @FlaggedApi: method android.os.UserManager.getAliveUsers()
+UnflaggedApi: android.os.UserManager#getUsers():
+ New API must be flagged with @FlaggedApi: method android.os.UserManager.getUsers()
+UnflaggedApi: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities:
+ New API must be flagged with @FlaggedApi: class android.telephony.ims.feature.MmTelFeature.MmTelCapabilities
+UnflaggedApi: android.view.WindowManager.LayoutParams#preferredMaxDisplayRefreshRate:
+ New API must be flagged with @FlaggedApi: field android.view.WindowManager.LayoutParams.preferredMaxDisplayRefreshRate
+UnflaggedApi: android.view.WindowManager.LayoutParams#preferredMinDisplayRefreshRate:
+ New API must be flagged with @FlaggedApi: field android.view.WindowManager.LayoutParams.preferredMinDisplayRefreshRate
+UnflaggedApi: android.view.inputmethod.InputMethodManager#isCurrentRootView(android.view.View):
+ New API must be flagged with @FlaggedApi: method android.view.inputmethod.InputMethodManager.isCurrentRootView(android.view.View)
+UnflaggedApi: android.view.inputmethod.InsertModeGesture:
+ New API must be flagged with @FlaggedApi: class android.view.inputmethod.InsertModeGesture
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#displayId:
+ New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.displayId
+UnflaggedApi: android.window.WindowInfosListenerForTest.WindowInfo#isVisible:
+ New API must be flagged with @FlaggedApi: field android.window.WindowInfosListenerForTest.WindowInfo.isVisible
+
+
UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getKeyphraseMetadata(String, java.util.Locale) parameter #1:
Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale) parameter #2:
diff --git a/core/api/test-removed.txt b/core/api/test-removed.txt
index 14191ebcb080..d802177e249b 100644
--- a/core/api/test-removed.txt
+++ b/core/api/test-removed.txt
@@ -1,3 +1 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 70864d532986..b6789aa5ea13 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -14,6 +14,15 @@ aidl_library {
hdrs: ["android/hardware/HardwareBuffer.aidl"],
}
+// TODO (b/303286040): Remove this once |ENABLE_NFC_MAINLINE_FLAG| is rolled out
+filegroup {
+ name: "framework-core-nfc-infcadapter-sources",
+ srcs: [
+ "android/nfc/INfcAdapter.aidl",
+ ],
+ visibility: ["//frameworks/base/services/core"],
+}
+
filegroup {
name: "framework-core-sources",
srcs: [
@@ -217,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",
@@ -446,7 +454,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",
@@ -496,7 +503,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/Activity.java b/core/java/android/app/Activity.java
index 259086932465..06c139f444ef 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -31,6 +31,7 @@ import android.annotation.CallSuper;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.LayoutRes;
@@ -79,6 +80,7 @@ import android.graphics.drawable.Icon;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.net.Uri;
+import android.nfc.Flags;
import android.os.BadParcelableException;
import android.os.Build;
import android.os.Bundle;
@@ -8915,6 +8917,7 @@ public class Activity extends ContextThemeWrapper
* @hide
*/
@UnsupportedAppUsage
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final boolean isResumed() {
return mResumed;
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 6a51171b8f90..2d554031ab48 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -115,6 +115,26 @@ public class KeyguardManager {
"android.app.action.CONFIRM_REMOTE_DEVICE_CREDENTIAL";
/**
+ * Intent used to prompt user for device credential for entering repair
+ * mode. If the credential is verified successfully, then the information
+ * needed to verify the credential again will be written to a location that
+ * is available to repair mode. This makes it possible for repair mode to
+ * require that the same credential be provided to exit repair mode.
+ * @hide
+ */
+ public static final String ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL =
+ "android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL";
+
+ /**
+ * Intent used to prompt user for device credential that is written by
+ * {@link #ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL} for exiting
+ * repair mode.
+ * @hide
+ */
+ public static final String ACTION_CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL =
+ "android.app.action.CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL";
+
+ /**
* A CharSequence dialog title to show to the user when used with a
* {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
* @hide
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index b5efb73225d6..0414f792f159 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -50,7 +50,6 @@ import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.security.net.config.NetworkSecurityConfigProvider;
-import android.sysprop.VndkProperties;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
@@ -901,14 +900,10 @@ public final class LoadedApk {
}
// Similar to vendor apks, we should add /product/lib for apks from product partition
- // when product apps are marked as unbundled. We cannot use the same way from vendor
- // to check if lib path exists because there is possibility that /product/lib would not
- // exist from legacy device while product apks are bundled. To make this clear, we use
- // "ro.product.vndk.version" property. If the property is defined, we regard all product
- // apks as unbundled.
+ // when product apps are marked as unbundled. Product is separated as long as the
+ // partition exists, so it can be handled with same approach from the vendor partition.
if (mApplicationInfo.getCodePath() != null
- && mApplicationInfo.isProduct()
- && VndkProperties.product_vndk_version().isPresent()) {
+ && mApplicationInfo.isProduct()) {
isBundledApp = false;
}
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e1c45d98e678..164bdbe9f516 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -36,6 +36,7 @@ per-file GameMode* = file:/GAME_MANAGER_OWNERS
per-file GameState* = file:/GAME_MANAGER_OWNERS
per-file IGameManager* = file:/GAME_MANAGER_OWNERS
per-file IGameMode* = file:/GAME_MANAGER_OWNERS
+per-file BackgroundStartPrivileges.java = file:/BAL_OWNERS
# ActivityThread
per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 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/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index da5e40aedbd2..f3cc59428a30 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16111,11 +16111,6 @@ public class DevicePolicyManager {
* Called by a profile owner of an organization-owned managed profile to suspend personal
* apps on the device. When personal apps are suspended the device can only be used for calls.
*
- * <p>When personal apps are suspended, an ongoing notification about that is shown to the user.
- * When the user taps the notification, system invokes {@link #ACTION_CHECK_POLICY_COMPLIANCE}
- * in the profile owner package. Profile owner implementation that uses personal apps suspension
- * must handle this intent.
- *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
* @param suspended Whether personal apps should be suspended.
* @throws IllegalStateException if the profile owner doesn't have an activity that handles
diff --git a/core/java/android/app/contentsuggestions/OWNERS b/core/java/android/app/contentsuggestions/OWNERS
index cf54c2a6fcbc..5f8de77c9dda 100644
--- a/core/java/android/app/contentsuggestions/OWNERS
+++ b/core/java/android/app/contentsuggestions/OWNERS
@@ -1,7 +1,4 @@
# Bug component: 643919
-augale@google.com
-joannechung@google.com
-markpun@google.com
-lpeter@google.com
-tymtsai@google.com
+hackz@google.com
+volnov@google.com
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/content/Context.java b/core/java/android/content/Context.java
index 10857e51f916..3d43845f1781 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -23,6 +23,7 @@ import android.annotation.ColorInt;
import android.annotation.ColorRes;
import android.annotation.DisplayContext;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.LongDef;
import android.annotation.NonNull;
@@ -4766,6 +4767,7 @@ public abstract class Context {
* @see android.net.thread.ThreadNetworkManager
* @hide
*/
+ @FlaggedApi("com.android.net.thread.flags.thread_enabled")
@SystemApi
public static final String THREAD_NETWORK_SERVICE = "thread_network";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c3fd744469e9..0fd0e1531771 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1161,6 +1161,11 @@ public class Intent implements Parcelable, Cloneable {
* numbers. Applications can <strong>dial</strong> emergency numbers using
* {@link #ACTION_DIAL}, however.
*
+ * <p>Note: This Intent can only be used to dial call forwarding MMI codes if the application
+ * using this intent is set as the default or system dialer. The system will treat any other
+ * application using this Intent for the purpose of dialing call forwarding MMI codes as if the
+ * {@link #ACTION_DIAL} Intent was used instead.
+ *
* <p>Note: An app filling the {@link android.app.role.RoleManager#ROLE_DIALER} role should use
* {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls rather than
* relying on this intent.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 02650c65e1a4..f07a23f7ba21 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.CheckResult;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.LongDef;
@@ -1449,6 +1450,7 @@ public abstract class PackageManager {
INSTALL_ALLOW_DOWNGRADE,
INSTALL_STAGED,
INSTALL_REQUEST_UPDATE_OWNERSHIP,
+ INSTALL_IGNORE_DEXOPT_PROFILE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallFlags {}
@@ -1674,6 +1676,18 @@ public abstract class PackageManager {
public static final int INSTALL_FROM_MANAGED_USER_OR_PROFILE = 1 << 26;
/**
+ * If set, all dexopt profiles are ignored by dexopt during the installation, including the
+ * profile in the DM file and the profile embedded in the APK file. If an invalid profile is
+ * provided during installation, no warning will be reported by {@code adb install}.
+ *
+ * This option does not affect later dexopt operations (e.g., background dexopt and manual `pm
+ * compile` invocations).
+ *
+ * @hide
+ */
+ public static final int INSTALL_IGNORE_DEXOPT_PROFILE = 1 << 28;
+
+ /**
* Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
* a development-only feature and should not be used on end user devices.
*
@@ -3638,6 +3652,7 @@ public abstract class PackageManager {
* The device is capable of communicating with other devices via
* <a href="https://www.threadgroup.org">Thread</a> networking protocol.
*/
+ @FlaggedApi("com.android.net.thread.flags.thread_enabled")
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
diff --git a/core/java/android/hardware/HardwareBuffer.aidl b/core/java/android/hardware/HardwareBuffer.aidl
index 1333f0da725f..a9742cb6c084 100644
--- a/core/java/android/hardware/HardwareBuffer.aidl
+++ b/core/java/android/hardware/HardwareBuffer.aidl
@@ -16,4 +16,4 @@
package android.hardware;
-@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable HardwareBuffer ndk_header "android/hardware_buffer_aidl.h";
+@JavaOnlyStableParcelable @NdkOnlyStableParcelable @RustOnlyStableParcelable parcelable HardwareBuffer ndk_header "android/hardware_buffer_aidl.h" rust_type "nativewindow::HardwareBuffer";
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 943eee461809..61d87026b6e9 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -18,6 +18,7 @@ package android.hardware.biometrics;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -296,4 +297,15 @@ public interface BiometricConstants {
@Retention(RetentionPolicy.SOURCE)
@IntDef({BIOMETRIC_LOCKOUT_NONE, BIOMETRIC_LOCKOUT_TIMED, BIOMETRIC_LOCKOUT_PERMANENT})
@interface LockoutMode {}
+
+ //
+ // Other miscellaneous constants
+ //
+
+ /**
+ * Returned from {@link BiometricManager#getLastAuthenticationTime(int)} when there has
+ * been no successful authentication for the given authenticator since boot.
+ */
+ @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
+ long BIOMETRIC_NO_AUTHENTICATION = -1;
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 82694ee3463b..90bbca8336e1 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -23,6 +23,8 @@ import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import static com.android.internal.util.FrameworkStatsLog.AUTH_DEPRECATED_APIUSED__DEPRECATED_API__API_BIOMETRIC_MANAGER_CAN_AUTHENTICATE;
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +32,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
+import android.app.KeyguardManager;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
@@ -86,6 +89,17 @@ public class BiometricManager {
BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
/**
+ * Returned from {@link BiometricManager#getLastAuthenticationTime(int)} when no matching
+ * successful authentication has been performed since boot.
+ */
+ @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
+ public static final long BIOMETRIC_NO_AUTHENTICATION =
+ BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+
+ private static final int GET_LAST_AUTH_TIME_ALLOWED_AUTHENTICATORS =
+ Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_STRONG;
+
+ /**
* @hide
*/
@IntDef({BIOMETRIC_SUCCESS,
@@ -637,5 +651,58 @@ public class BiometricManager {
}
}
+
+ /**
+ * Gets the last time the user successfully authenticated using one of the given authenticators.
+ * The returned value is time in
+ * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()} (time since
+ * boot, including sleep).
+ * <p>
+ * {@link BiometricManager#BIOMETRIC_NO_AUTHENTICATION} is returned in the case where there
+ * has been no successful authentication using any of the given authenticators since boot.
+ * <p>
+ * Currently, only {@link Authenticators#DEVICE_CREDENTIAL} and
+ * {@link Authenticators#BIOMETRIC_STRONG} are accepted. {@link IllegalArgumentException} will
+ * be thrown if {@code authenticators} contains other authenticator types.
+ * <p>
+ * Note that this may return successful authentication times even if the device is currently
+ * locked. You may use {@link KeyguardManager#isDeviceLocked()} to determine if the device
+ * is unlocked or not. Additionally, this method may return valid times for an authentication
+ * method that is no longer available. For instance, if the user unlocked the device with a
+ * {@link Authenticators#BIOMETRIC_STRONG} authenticator but then deleted that authenticator
+ * (e.g., fingerprint data), this method will still return the time of that unlock for
+ * {@link Authenticators#BIOMETRIC_STRONG} if it is the most recent successful event. The caveat
+ * is that {@link BiometricManager#BIOMETRIC_NO_AUTHENTICATION} will be returned if the device
+ * no longer has a secure lock screen at all, even if there were successful authentications
+ * performed before the lock screen was made insecure.
+ *
+ * @param authenticators bit field consisting of constants defined in {@link Authenticators}.
+ * @return the time of last authentication or
+ * {@link BiometricManager#BIOMETRIC_NO_AUTHENTICATION}
+ * @throws IllegalArgumentException if {@code authenticators} contains values other than
+ * {@link Authenticators#DEVICE_CREDENTIAL} and {@link Authenticators#BIOMETRIC_STRONG} or is
+ * 0.
+ */
+ @RequiresPermission(USE_BIOMETRIC)
+ @ElapsedRealtimeLong
+ @FlaggedApi(Flags.FLAG_LAST_AUTHENTICATION_TIME)
+ public long getLastAuthenticationTime(
+ @BiometricManager.Authenticators.Types int authenticators) {
+ if (authenticators == 0
+ || (GET_LAST_AUTH_TIME_ALLOWED_AUTHENTICATORS & authenticators) != authenticators) {
+ throw new IllegalArgumentException(
+ "Only BIOMETRIC_STRONG and DEVICE_CREDENTIAL authenticators may be used.");
+ }
+
+ if (mService != null) {
+ try {
+ return mService.getLastAuthenticationTime(UserHandle.myUserId(), authenticators);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ } else {
+ return BIOMETRIC_NO_AUTHENTICATION;
+ }
+ }
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 294813d76b99..35ae11c7397c 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -786,7 +786,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
*/
- public Signature getSignature() {
+ public @Nullable Signature getSignature() {
return super.getSignature();
}
@@ -794,7 +794,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* Get {@link Cipher} object.
* @return {@link Cipher} object or null if this doesn't contain one.
*/
- public Cipher getCipher() {
+ public @Nullable Cipher getCipher() {
return super.getCipher();
}
@@ -802,7 +802,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
* Get {@link Mac} object.
* @return {@link Mac} object or null if this doesn't contain one.
*/
- public Mac getMac() {
+ public @Nullable Mac getMac() {
return super.getMac();
}
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 6ac1efb49839..39fbe83b6abb 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -20,6 +20,7 @@ import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OB
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.security.identity.IdentityCredential;
import android.security.identity.PresentationSession;
import android.security.keystore2.AndroidKeyStoreProvider;
@@ -33,20 +34,35 @@ import javax.crypto.Mac;
/**
* A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
* Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
- * {@link IdentityCredential}, and {@link PresentationSession} objects.
+ * {@link KeyAgreement}, {@link IdentityCredential}, and {@link PresentationSession} objects.
* @hide
*/
public class CryptoObject {
private final Object mCrypto;
+ /**
+ * Create from a {@link Signature} object.
+ *
+ * @param signature a {@link Signature} object.
+ */
public CryptoObject(@NonNull Signature signature) {
mCrypto = signature;
}
+ /**
+ * Create from a {@link Cipher} object.
+ *
+ * @param cipher a {@link Cipher} object.
+ */
public CryptoObject(@NonNull Cipher cipher) {
mCrypto = cipher;
}
+ /**
+ * Create from a {@link Mac} object.
+ *
+ * @param mac a {@link Mac} object.
+ */
public CryptoObject(@NonNull Mac mac) {
mCrypto = mac;
}
@@ -62,10 +78,20 @@ public class CryptoObject {
mCrypto = credential;
}
+ /**
+ * Create from a {@link PresentationSession} object.
+ *
+ * @param session a {@link PresentationSession} object.
+ */
public CryptoObject(@NonNull PresentationSession session) {
mCrypto = session;
}
+ /**
+ * Create from a {@link KeyAgreement} object.
+ *
+ * @param keyAgreement a {@link KeyAgreement} object.
+ */
@FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
public CryptoObject(@NonNull KeyAgreement keyAgreement) {
mCrypto = keyAgreement;
@@ -75,7 +101,7 @@ public class CryptoObject {
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
*/
- public Signature getSignature() {
+ public @Nullable Signature getSignature() {
return mCrypto instanceof Signature ? (Signature) mCrypto : null;
}
@@ -83,7 +109,7 @@ public class CryptoObject {
* Get {@link Cipher} object.
* @return {@link Cipher} object or null if this doesn't contain one.
*/
- public Cipher getCipher() {
+ public @Nullable Cipher getCipher() {
return mCrypto instanceof Cipher ? (Cipher) mCrypto : null;
}
@@ -91,7 +117,7 @@ public class CryptoObject {
* Get {@link Mac} object.
* @return {@link Mac} object or null if this doesn't contain one.
*/
- public Mac getMac() {
+ public @Nullable Mac getMac() {
return mCrypto instanceof Mac ? (Mac) mCrypto : null;
}
@@ -101,7 +127,7 @@ public class CryptoObject {
* @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
*/
@Deprecated
- public IdentityCredential getIdentityCredential() {
+ public @Nullable IdentityCredential getIdentityCredential() {
return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
}
@@ -109,16 +135,18 @@ public class CryptoObject {
* Get {@link PresentationSession} object.
* @return {@link PresentationSession} object or null if this doesn't contain one.
*/
- public PresentationSession getPresentationSession() {
+ public @Nullable PresentationSession getPresentationSession() {
return mCrypto instanceof PresentationSession ? (PresentationSession) mCrypto : null;
}
/**
- * Get {@link KeyAgreement} object.
+ * Get {@link KeyAgreement} object. A key-agreement protocol is a protocol whereby
+ * two or more parties can agree on a shared secret using public key cryptography.
+ *
* @return {@link KeyAgreement} object or null if this doesn't contain one.
*/
@FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
- public KeyAgreement getKeyAgreement() {
+ public @Nullable KeyAgreement getKeyAgreement() {
return mCrypto instanceof KeyAgreement ? (KeyAgreement) mCrypto : null;
}
diff --git a/core/java/android/hardware/biometrics/IAuthService.aidl b/core/java/android/hardware/biometrics/IAuthService.aidl
index c2e5c0b6d519..5bdbe2b573b2 100644
--- a/core/java/android/hardware/biometrics/IAuthService.aidl
+++ b/core/java/android/hardware/biometrics/IAuthService.aidl
@@ -57,6 +57,9 @@ interface IAuthService {
// Checks if biometrics can be used.
int canAuthenticate(String opPackageName, int userId, int authenticators);
+ // Gets the time of last authentication for the given user and authenticators.
+ long getLastAuthenticationTime(int userId, int authenticators);
+
// Checks if any biometrics are enrolled.
boolean hasEnrolledBiometrics(int userId, String opPackageName);
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index 18c8d1bd3a1e..058f302af62b 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -53,6 +53,10 @@ interface IBiometricService {
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
int canAuthenticate(String opPackageName, int userId, int callingUserId, int authenticators);
+ // Gets the time of last authentication for the given user and authenticators.
+ @EnforcePermission("USE_BIOMETRIC_INTERNAL")
+ long getLastAuthenticationTime(int userId, int authenticators);
+
// Checks if any biometrics are enrolled.
@EnforcePermission("USE_BIOMETRIC_INTERNAL")
boolean hasEnrolledBiometrics(int userId, String opPackageName);
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 0924e0d12bd3..c370ad658c3b 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -1,6 +1,13 @@
package: "android.hardware.biometrics"
flag {
+ name: "last_authentication_time"
+ namespace: "wallet_integration"
+ description: "Feature flag for adding getLastAuthenticationTime API to BiometricManager"
+ bug: "301979982"
+}
+
+flag {
name: "add_key_agreement_crypto_object"
namespace: "biometrics"
description: "Feature flag for adding KeyAgreement api to CryptoObject."
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 21540bf1a18b..d6c58ac1d7fe 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1403,6 +1403,7 @@ public final class OutputConfiguration implements Parcelable {
if (mSurfaces.get(i) != other.mSurfaces.get(i))
return false;
}
+ if (!mIsDeferredConfig && mSurfaces.size() != other.mSurfaces.size()) return false;
if (mDynamicRangeProfile != other.mDynamicRangeProfile) {
return false;
}
diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS
index 861e4409b014..6952e5d78d98 100644
--- a/core/java/android/hardware/hdmi/OWNERS
+++ b/core/java/android/hardware/hdmi/OWNERS
@@ -2,5 +2,4 @@
include /services/core/java/com/android/server/display/OWNERS
-marvinramin@google.com
-lcnathalie@google.com
+quxiangfang@google.com
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/NetworkStack.java b/core/java/android/net/NetworkStack.java
index dbb312720373..19ba6a1d22ed 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -23,6 +23,7 @@ import android.content.Context;
import android.os.IBinder;
import android.os.ServiceManager;
+import com.android.net.flags.Flags;
import com.android.net.module.util.PermissionUtils;
/**
* Constants and utilities for client code communicating with the network stack service.
@@ -103,4 +104,16 @@ public class NetworkStack {
final @NonNull String... otherPermissions) {
PermissionUtils.enforceNetworkStackPermissionOr(context, otherPermissions);
}
+
+ /**
+ * Get setting of the "set_data_saver_via_cm" flag.
+ *
+ * @hide
+ */
+ // A workaround for aconfig. Currently, aconfig value read from platform and mainline code can
+ // be inconsistent. To avoid the problem, CTS for mainline code can get the flag value by this
+ // method.
+ public static boolean getDataSaverViaCmFlag() {
+ return Flags.setDataSaverViaCm();
+ }
}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index a40fb154c256..6f11d3ae661c 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,10 +16,12 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+import static android.net.vcn.Flags.FLAG_SAFE_MODE_CONFIG;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -235,6 +237,9 @@ public final class VcnGatewayConnectionConfig {
"mMinUdpPort4500NatTimeoutSeconds";
private final int mMinUdpPort4500NatTimeoutSeconds;
+ private static final String IS_SAFE_MODE_DISABLED_KEY = "mIsSafeModeDisabled";
+ private final boolean mIsSafeModeDisabled;
+
private static final String GATEWAY_OPTIONS_KEY = "mGatewayOptions";
@NonNull private final Set<Integer> mGatewayOptions;
@@ -247,6 +252,7 @@ public final class VcnGatewayConnectionConfig {
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu,
@NonNull int minUdpPort4500NatTimeoutSeconds,
+ boolean isSafeModeDisabled,
@NonNull Set<Integer> gatewayOptions) {
mGatewayConnectionName = gatewayConnectionName;
mTunnelConnectionParams = tunnelConnectionParams;
@@ -255,6 +261,7 @@ public final class VcnGatewayConnectionConfig {
mMaxMtu = maxMtu;
mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
mGatewayOptions = Collections.unmodifiableSet(new ArraySet(gatewayOptions));
+ mIsSafeModeDisabled = isSafeModeDisabled;
mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates);
if (mUnderlyingNetworkTemplates.isEmpty()) {
@@ -317,6 +324,7 @@ public final class VcnGatewayConnectionConfig {
in.getInt(
MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS_KEY,
MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET);
+ mIsSafeModeDisabled = in.getBoolean(IS_SAFE_MODE_DISABLED_KEY);
validate();
}
@@ -483,6 +491,16 @@ public final class VcnGatewayConnectionConfig {
}
/**
+ * Check whether safe mode is enabled
+ *
+ * @see Builder#setSafeModeEnabled(boolean)
+ */
+ @FlaggedApi(FLAG_SAFE_MODE_CONFIG)
+ public boolean isSafeModeEnabled() {
+ return !mIsSafeModeDisabled;
+ }
+
+ /**
* Checks if the given VCN gateway option is enabled.
*
* @param option the option to check.
@@ -528,6 +546,7 @@ public final class VcnGatewayConnectionConfig {
result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
result.putInt(MAX_MTU_KEY, mMaxMtu);
result.putInt(MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS_KEY, mMinUdpPort4500NatTimeoutSeconds);
+ result.putBoolean(IS_SAFE_MODE_DISABLED_KEY, mIsSafeModeDisabled);
return result;
}
@@ -542,6 +561,7 @@ public final class VcnGatewayConnectionConfig {
Arrays.hashCode(mRetryIntervalsMs),
mMaxMtu,
mMinUdpPort4500NatTimeoutSeconds,
+ mIsSafeModeDisabled,
mGatewayOptions);
}
@@ -559,6 +579,7 @@ public final class VcnGatewayConnectionConfig {
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
&& mMaxMtu == rhs.mMaxMtu
&& mMinUdpPort4500NatTimeoutSeconds == rhs.mMinUdpPort4500NatTimeoutSeconds
+ && mIsSafeModeDisabled == rhs.mIsSafeModeDisabled
&& mGatewayOptions.equals(rhs.mGatewayOptions);
}
@@ -577,6 +598,7 @@ public final class VcnGatewayConnectionConfig {
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
private int mMaxMtu = DEFAULT_MAX_MTU;
private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
+ private boolean mIsSafeModeDisabled = false;
@NonNull private final Set<Integer> mGatewayOptions = new ArraySet<>();
@@ -789,6 +811,27 @@ public final class VcnGatewayConnectionConfig {
}
/**
+ * Enable/disable safe mode
+ *
+ * <p>If a VCN fails to provide connectivity within a system-provided timeout, it will enter
+ * safe mode. In safe mode, the VCN Network will be torn down and the system will restore
+ * connectivity by allowing underlying cellular or WiFi networks to be used as default. At
+ * the same time, VCN will continue to retry until it succeeds.
+ *
+ * <p>When safe mode is disabled and VCN connection fails to provide connectivity, end users
+ * might not have connectivity, and may not have access to carrier-owned underlying
+ * networks.
+ *
+ * @param enabled whether safe mode should be enabled. Defaults to {@code true}
+ */
+ @FlaggedApi(FLAG_SAFE_MODE_CONFIG)
+ @NonNull
+ public Builder setSafeModeEnabled(boolean enabled) {
+ mIsSafeModeDisabled = !enabled;
+ return this;
+ }
+
+ /**
* Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
@@ -803,6 +846,7 @@ public final class VcnGatewayConnectionConfig {
mRetryIntervalsMs,
mMaxMtu,
mMinUdpPort4500NatTimeoutSeconds,
+ mIsSafeModeDisabled,
mGatewayOptions);
}
}
diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig
new file mode 100644
index 000000000000..6956916af0f1
--- /dev/null
+++ b/core/java/android/net/vcn/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.net.vcn"
+
+flag {
+ name: "safe_mode_config"
+ namespace: "vcn"
+ description: "Feature flag for safe mode configurability"
+ bug: "276358140"
+} \ No newline at end of file
diff --git a/core/java/android/nfc/Constants.java b/core/java/android/nfc/Constants.java
new file mode 100644
index 000000000000..f76833063605
--- /dev/null
+++ b/core/java/android/nfc/Constants.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+/**
+ * @hide
+ * TODO(b/303286040): Holds @hide API constants. Formalize these APIs.
+ */
+public final class Constants {
+ private Constants() { }
+
+ public static final String SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground";
+ public static final String SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
+ public static final String FEATURE_NFC_ANY = "android.hardware.nfc.any";
+}
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index 53843fe73d33..c7b3b2c03f65 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -40,5 +40,6 @@ interface INfcCardEmulation
boolean unsetPreferredService();
boolean supportsAidPrefixRegistration();
ApduServiceInfo getPreferredPaymentService(int userHandle);
+ boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
boolean isDefaultPaymentRegistered();
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 46586308e3cf..c89759553810 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.app.Activity;
@@ -37,6 +38,7 @@ import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
import android.nfc.tech.NfcF;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -376,6 +378,8 @@ public final class NfcAdapter {
* <p>An external NFC field detected when device locked and SecureNfc enabled.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC =
"android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
@@ -942,7 +946,8 @@ public final class NfcAdapter {
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public int getAdapterState() {
try {
return sService.getState();
@@ -1594,6 +1599,40 @@ public final class NfcAdapter {
mNfcActivityManager.disableReaderMode(activity);
}
+ // Flags arguments to NFC adapter to enable/disable NFC
+ private static final int DISABLE_POLLING_FLAGS = 0x1000;
+ private static final int ENABLE_POLLING_FLAGS = 0x0000;
+
+ /**
+ * Privileged API to enable disable reader polling.
+ * Note: Use with caution! The app is responsible for ensuring that the polling state is
+ * returned to normal.
+ *
+ * @see #enableReaderMode(Activity, ReaderCallback, int, Bundle) for more detailed
+ * documentation.
+ *
+ * @param enablePolling whether to enable or disable polling.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @SuppressLint("VisiblySynchronized")
+ public void setReaderMode(boolean enablePolling) {
+ synchronized (NfcAdapter.class) {
+ if (!sHasNfcFeature) {
+ throw new UnsupportedOperationException();
+ }
+ }
+ Binder token = new Binder();
+ int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
+ try {
+ NfcAdapter.sService.setReaderMode(token, null, flags, null);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ }
+ }
+
/**
* Manually invoke Android Beam to share data.
*
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index 958669ee5852..ae3e333051d7 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -34,6 +34,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.regex.Pattern;
/**********************************************************************
* This file is not a part of the NFC mainline module *
@@ -79,7 +80,7 @@ public final class AidGroup implements Parcelable {
throw new IllegalArgumentException("Too many AIDs in AID group.");
}
for (String aid : aids) {
- if (!CardEmulation.isValidAid(aid)) {
+ if (!isValidAid(aid)) {
throw new IllegalArgumentException("AID " + aid + " is not a valid AID.");
}
}
@@ -264,4 +265,34 @@ public final class AidGroup implements Parcelable {
return CardEmulation.CATEGORY_PAYMENT.equals(category) ||
CardEmulation.CATEGORY_OTHER.equals(category);
}
+
+ private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
+ /**
+ * Copied over from {@link CardEmulation#isValidAid(String)}
+ * @hide
+ */
+ private static boolean isValidAid(String aid) {
+ if (aid == null)
+ return false;
+
+ // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*')
+ if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) {
+ Log.e(TAG, "AID " + aid + " is not a valid AID.");
+ return false;
+ }
+
+ // If not a prefix/subset AID, the total length must be even (even # of AID chars)
+ if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) {
+ Log.e(TAG, "AID " + aid + " is not a valid AID.");
+ return false;
+ }
+
+ // Verify hex characters
+ if (!AID_PATTERN.matcher(aid).matches()) {
+ Log.e(TAG, "AID " + aid + " is not a valid AID.");
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 18ec914860fb..597c948bd515 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -52,6 +52,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.regex.Pattern;
/**
* Class holding APDU service info.
@@ -126,6 +127,11 @@ public final class ApduServiceInfo implements Parcelable {
private final String mSettingsActivityName;
/**
+ * State of the service for CATEGORY_OTHER selection
+ */
+ private boolean mOtherServiceEnabled;
+
+ /**
* @hide
*/
public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
@@ -133,8 +139,21 @@ public final class ApduServiceInfo implements Parcelable {
boolean requiresUnlock, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost) {
this(info, onHost, description, staticAidGroups, dynamicAidGroups,
+ requiresUnlock, bannerResource, uid, settingsActivityName,
+ offHost, staticOffHost, false);
+ }
+
+ /**
+ * @hide
+ */
+ public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+ List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
+ boolean requiresUnlock, int bannerResource, int uid,
+ String settingsActivityName, String offHost, String staticOffHost,
+ boolean isEnabled) {
+ this(info, onHost, description, staticAidGroups, dynamicAidGroups,
requiresUnlock, onHost ? true : false, bannerResource, uid,
- settingsActivityName, offHost, staticOffHost);
+ settingsActivityName, offHost, staticOffHost, isEnabled);
}
/**
@@ -143,7 +162,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) {
+ String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled) {
this.mService = info;
this.mDescription = description;
this.mStaticAidGroups = new HashMap<String, AidGroup>();
@@ -162,6 +181,8 @@ public final class ApduServiceInfo implements Parcelable {
this.mBannerResourceId = bannerResource;
this.mUid = uid;
this.mSettingsActivityName = settingsActivityName;
+ this.mOtherServiceEnabled = isEnabled;
+
}
/**
@@ -307,7 +328,7 @@ public final class ApduServiceInfo implements Parcelable {
com.android.internal.R.styleable.AidFilter);
String aid = a.getString(com.android.internal.R.styleable.AidFilter_name).
toUpperCase();
- if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
+ if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
currentGroup.getAids().add(aid);
} else {
Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
@@ -321,7 +342,7 @@ public final class ApduServiceInfo implements Parcelable {
toUpperCase();
// Add wildcard char to indicate prefix
aid = aid.concat("*");
- if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
+ if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
currentGroup.getAids().add(aid);
} else {
Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
@@ -335,7 +356,7 @@ public final class ApduServiceInfo implements Parcelable {
toUpperCase();
// Add wildcard char to indicate suffix
aid = aid.concat("#");
- if (CardEmulation.isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
+ if (isValidAid(aid) && !currentGroup.getAids().contains(aid)) {
currentGroup.getAids().add(aid);
} else {
Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
@@ -350,6 +371,9 @@ public final class ApduServiceInfo implements Parcelable {
}
// Set uid
mUid = si.applicationInfo.uid;
+
+ mOtherServiceEnabled = false; // support other category
+
}
/**
@@ -719,43 +743,47 @@ public final class ApduServiceInfo implements Parcelable {
dest.writeInt(mBannerResourceId);
dest.writeInt(mUid);
dest.writeString(mSettingsActivityName);
+
+ dest.writeInt(mOtherServiceEnabled ? 1 : 0);
};
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public static final @NonNull Parcelable.Creator<ApduServiceInfo> CREATOR =
new Parcelable.Creator<ApduServiceInfo>() {
- @Override
- public ApduServiceInfo createFromParcel(Parcel source) {
- ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
- String description = source.readString();
- boolean onHost = source.readInt() != 0;
- String offHostName = source.readString();
- String staticOffHostName = source.readString();
- ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>();
- int numStaticGroups = source.readInt();
- if (numStaticGroups > 0) {
- source.readTypedList(staticAidGroups, AidGroup.CREATOR);
- }
- ArrayList<AidGroup> dynamicAidGroups = new ArrayList<AidGroup>();
- int numDynamicGroups = source.readInt();
- if (numDynamicGroups > 0) {
- source.readTypedList(dynamicAidGroups, AidGroup.CREATOR);
- }
- boolean requiresUnlock = source.readInt() != 0;
- boolean requiresScreenOn = source.readInt() != 0;
- int bannerResource = source.readInt();
- int uid = source.readInt();
- String settingsActivityName = source.readString();
- return new ApduServiceInfo(info, onHost, description, staticAidGroups,
- dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
- settingsActivityName, offHostName, staticOffHostName);
- }
+ @Override
+ public ApduServiceInfo createFromParcel(Parcel source) {
+ ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source);
+ String description = source.readString();
+ boolean onHost = source.readInt() != 0;
+ String offHostName = source.readString();
+ String staticOffHostName = source.readString();
+ ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>();
+ int numStaticGroups = source.readInt();
+ if (numStaticGroups > 0) {
+ source.readTypedList(staticAidGroups, AidGroup.CREATOR);
+ }
+ ArrayList<AidGroup> dynamicAidGroups = new ArrayList<AidGroup>();
+ int numDynamicGroups = source.readInt();
+ if (numDynamicGroups > 0) {
+ source.readTypedList(dynamicAidGroups, AidGroup.CREATOR);
+ }
+ boolean requiresUnlock = source.readInt() != 0;
+ boolean requiresScreenOn = source.readInt() != 0;
+ int bannerResource = source.readInt();
+ int uid = source.readInt();
+ String settingsActivityName = source.readString();
+ boolean isEnabled = source.readInt() != 0;
+ return new ApduServiceInfo(info, onHost, description, staticAidGroups,
+ dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
+ settingsActivityName, offHostName, staticOffHostName,
+ isEnabled);
+ }
- @Override
- public ApduServiceInfo[] newArray(int size) {
- return new ApduServiceInfo[size];
- }
- };
+ @Override
+ public ApduServiceInfo[] newArray(int size) {
+ return new ApduServiceInfo[size];
+ }
+ };
/**
* Dump contents for debugging.
@@ -778,14 +806,16 @@ public final class ApduServiceInfo implements Parcelable {
}
pw.println(" Static AID groups:");
for (AidGroup group : mStaticAidGroups.values()) {
- pw.println(" Category: " + group.getCategory());
+ pw.println(" Category: " + group.getCategory()
+ + "(enabled: " + mOtherServiceEnabled + ")");
for (String aid : group.getAids()) {
pw.println(" AID: " + aid);
}
}
pw.println(" Dynamic AID groups:");
for (AidGroup group : mDynamicAidGroups.values()) {
- pw.println(" Category: " + group.getCategory());
+ pw.println(" Category: " + group.getCategory()
+ + "(enabled: " + mOtherServiceEnabled + ")");
for (String aid : group.getAids()) {
pw.println(" AID: " + aid);
}
@@ -795,6 +825,28 @@ public final class ApduServiceInfo implements Parcelable {
pw.println(" Requires Device ScreenOn: " + mRequiresDeviceScreenOn);
}
+
+ /**
+ * Enable or disable this CATEGORY_OTHER service.
+ *
+ * @param enabled true to indicate if user has enabled this service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public void setOtherServiceEnabled(boolean enabled) {
+ mOtherServiceEnabled = enabled;
+ }
+
+
+ /**
+ * Returns whether this CATEGORY_OTHER service is enabled or not.
+ *
+ * @return true to indicate if user has enabled this service
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public boolean isOtherServiceEnabled() {
+ return mOtherServiceEnabled;
+ }
+
/**
* Dump debugging info as ApduServiceInfoProto.
*
@@ -806,7 +858,7 @@ public final class ApduServiceInfo implements Parcelable {
*/
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public void dumpDebug(@NonNull ProtoOutputStream proto) {
- Utils.dumpDebugComponentName(getComponent(), proto, ApduServiceInfoProto.COMPONENT_NAME);
+ getComponent().dumpDebug(proto, ApduServiceInfoProto.COMPONENT_NAME);
proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription());
proto.write(ApduServiceInfoProto.ON_HOST, mOnHost);
if (!mOnHost) {
@@ -825,4 +877,34 @@ public final class ApduServiceInfo implements Parcelable {
}
proto.write(ApduServiceInfoProto.SETTINGS_ACTIVITY_NAME, mSettingsActivityName);
}
+
+ private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
+ /**
+ * Copied over from {@link CardEmulation#isValidAid(String)}
+ * @hide
+ */
+ private static boolean isValidAid(String aid) {
+ if (aid == null)
+ return false;
+
+ // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*')
+ if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) {
+ Log.e(TAG, "AID " + aid + " is not a valid AID.");
+ return false;
+ }
+
+ // If not a prefix/subset AID, the total length must be even (even # of AID chars)
+ if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) {
+ Log.e(TAG, "AID " + aid + " is not a valid AID.");
+ return false;
+ }
+
+ // Verify hex characters
+ if (!AID_PATTERN.matcher(aid).matches()) {
+ Log.e(TAG, "AID " + aid + " is not a valid AID.");
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 4909b0830eeb..d048b595ad1e 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -16,16 +16,21 @@
package android.nfc.cardemulation;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.nfc.Constants;
+import android.nfc.Flags;
import android.nfc.INfcCardEmulation;
import android.nfc.NfcAdapter;
import android.os.RemoteException;
@@ -274,7 +279,7 @@ public final class CardEmulation {
try {
preferForeground = Settings.Secure.getInt(
contextAsUser.getContentResolver(),
- Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0;
+ Constants.SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND) != 0;
} catch (SettingNotFoundException e) {
}
return preferForeground;
@@ -876,9 +881,16 @@ public final class CardEmulation {
}
/**
+ * Retrieves list of services registered of the provided category for the provided user.
+ *
+ * @param category Category string, one of {@link #CATEGORY_PAYMENT} or {@link #CATEGORY_OTHER}
+ * @param userId the user handle of the user whose information is being requested.
* @hide
*/
- public List<ApduServiceInfo> getServices(String category, int userId) {
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @NonNull
+ public List<ApduServiceInfo> getServices(@NonNull String category, @UserIdInt int userId) {
try {
return sService.getServices(userId, category);
} catch (RemoteException e) {
@@ -935,6 +947,39 @@ public final class CardEmulation {
return true;
}
+ /**
+ * Allows to set or unset preferred service (category other) to avoid AID Collision.
+ *
+ * @param service The ComponentName of the service
+ * @param status true to enable, false to disable
+ * @return set service for the category and true if service is already set return false.
+ *
+ * @hide
+ */
+ public boolean setServiceEnabledForCategoryOther(ComponentName service, boolean status) {
+ if (service == null) {
+ throw new NullPointerException("activity or service or category is null");
+ }
+ int userId = mContext.getUser().getIdentifier();
+
+ try {
+ return sService.setServiceEnabledForCategoryOther(userId, service, status);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.setServiceEnabledForCategoryOther(userId, service, status);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
void recoverService() {
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
sService = adapter.getCardEmulationService();
diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
index ec919e4d66bc..33bc16978721 100644
--- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
@@ -173,7 +173,7 @@ public final class NfcFServiceInfo implements Parcelable {
com.android.internal.R.styleable.SystemCodeFilter);
systemCode = a.getString(
com.android.internal.R.styleable.SystemCodeFilter_name).toUpperCase();
- if (!NfcFCardEmulation.isValidSystemCode(systemCode) &&
+ if (!isValidSystemCode(systemCode) &&
!systemCode.equalsIgnoreCase("NULL")) {
Log.e(TAG, "Invalid System Code: " + systemCode);
systemCode = null;
@@ -187,7 +187,7 @@ public final class NfcFServiceInfo implements Parcelable {
com.android.internal.R.styleable.Nfcid2Filter_name).toUpperCase();
if (!nfcid2.equalsIgnoreCase("RANDOM") &&
!nfcid2.equalsIgnoreCase("NULL") &&
- !NfcFCardEmulation.isValidNfcid2(nfcid2)) {
+ !isValidNfcid2(nfcid2)) {
Log.e(TAG, "Invalid NFCID2: " + nfcid2);
nfcid2 = null;
}
@@ -436,10 +436,62 @@ public final class NfcFServiceInfo implements Parcelable {
*/
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
public void dumpDebug(@NonNull ProtoOutputStream proto) {
- Utils.dumpDebugComponentName(getComponent(), proto, NfcFServiceInfoProto.COMPONENT_NAME);
+ getComponent().dumpDebug(proto, NfcFServiceInfoProto.COMPONENT_NAME);
proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription());
proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode());
proto.write(NfcFServiceInfoProto.NFCID2, getNfcid2());
proto.write(NfcFServiceInfoProto.T3T_PMM, getT3tPmm());
}
+
+ /**
+ * Copied over from {@link NfcFCardEmulation#isValidSystemCode(String)}
+ * @hide
+ */
+ private static boolean isValidSystemCode(String systemCode) {
+ if (systemCode == null) {
+ return false;
+ }
+ if (systemCode.length() != 4) {
+ Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
+ return false;
+ }
+ // check if the value is between "4000" and "4FFF" (excluding "4*FF")
+ if (!systemCode.startsWith("4") || systemCode.toUpperCase().endsWith("FF")) {
+ Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
+ return false;
+ }
+ try {
+ Integer.parseInt(systemCode, 16);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "System Code " + systemCode + " is not a valid System Code.");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Copied over from {@link NfcFCardEmulation#isValidNfcid2(String)}
+ * @hide
+ */
+ private static boolean isValidNfcid2(String nfcid2) {
+ if (nfcid2 == null) {
+ return false;
+ }
+ if (nfcid2.length() != 16) {
+ Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
+ return false;
+ }
+ // check if the the value starts with "02FE"
+ if (!nfcid2.toUpperCase().startsWith("02FE")) {
+ Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
+ return false;
+ }
+ try {
+ Long.parseLong(nfcid2, 16);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "NFCID2 " + nfcid2 + " is not a valid NFCID2.");
+ return false;
+ }
+ return true;
+ }
}
diff --git a/core/java/android/nfc/flags.aconfig b/core/java/android/nfc/flags.aconfig
index 55b0b4261763..cd50ace036de 100644
--- a/core/java/android/nfc/flags.aconfig
+++ b/core/java/android/nfc/flags.aconfig
@@ -13,3 +13,10 @@ flag {
description: "Flag for NFC reader option API changes"
bug: "291187960"
}
+
+flag {
+ name: "enable_nfc_user_restriction"
+ namespace: "nfc"
+ description: "Flag for NFC user restriction"
+ bug: "291187960"
+}
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 6a4ec9b7605a..25fba60b9bb5 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -21,6 +21,7 @@ import static android.os.Flags.FLAG_STATE_OF_HEALTH_PUBLIC;
import android.Manifest.permission;
import android.annotation.FlaggedApi;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -236,6 +237,7 @@ public class BatteryManager {
OsProtoEnums.CHARGING_POLICY_ADAPTIVE_LONGLIFE; // = 4
/** @hide */
+ @SuppressLint("UnflaggedApi") // TestApi without associated feature.
@TestApi
public static final int BATTERY_PLUGGED_ANY =
BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 509c3b88441e..a9b7257a5406 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -17,6 +17,7 @@
package android.os;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -1227,6 +1228,7 @@ public class Build {
/**
* Vanilla Ice Cream.
*/
+ @FlaggedApi(Flags.FLAG_ANDROID_OS_BUILD_VANILLA_ICE_CREAM)
public static final int VANILLA_ICE_CREAM = CUR_DEVELOPMENT;
}
diff --git a/core/java/android/os/DeadObjectException.java b/core/java/android/os/DeadObjectException.java
index e06b0f9f4bc2..65ed618fb075 100644
--- a/core/java/android/os/DeadObjectException.java
+++ b/core/java/android/os/DeadObjectException.java
@@ -19,7 +19,8 @@ import android.os.RemoteException;
/**
* The object you are calling has died, because its hosting process
- * no longer exists.
+ * no longer exists. This is also thrown for low-level binder
+ * errors.
*/
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..82b1ad8a8630 100644
--- a/core/java/android/os/DeadSystemRuntimeException.java
+++ b/core/java/android/os/DeadSystemRuntimeException.java
@@ -18,9 +18,10 @@ 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.
*
* @hide
*/
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 92be4c0c7dc3..8fcff78fb025 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -115,6 +115,7 @@ public class GraphicsEnvironment {
private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
+ private static final String SYSTEM_ANGLE_STRING = "system";
private ClassLoader mClassLoader;
private String mLibrarySearchPaths;
@@ -122,7 +123,7 @@ public class GraphicsEnvironment {
private GameManager mGameManager;
private int mAngleOptInIndex = -1;
- private boolean mEnabledByGameMode = false;
+ private boolean mShouldUseAngle = false;
/**
* Set up GraphicsEnvironment
@@ -141,19 +142,16 @@ public class GraphicsEnvironment {
// Setup ANGLE and pass down ANGLE details to the C++ code
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle");
- boolean useAngle = false;
if (setupAngle(context, coreSettings, pm, packageName)) {
- if (shouldUseAngle(context, coreSettings, packageName)) {
- useAngle = true;
- setGpuStats(ANGLE_DRIVER_NAME, ANGLE_DRIVER_VERSION_NAME, ANGLE_DRIVER_VERSION_CODE,
- 0, packageName, getVulkanVersion(pm));
- }
+ mShouldUseAngle = true;
+ setGpuStats(ANGLE_DRIVER_NAME, ANGLE_DRIVER_VERSION_NAME, ANGLE_DRIVER_VERSION_CODE,
+ 0, packageName, getVulkanVersion(pm));
}
Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver");
if (!chooseDriver(context, coreSettings, pm, packageName, appInfoWithMetaData)) {
- if (!useAngle) {
+ if (!mShouldUseAngle) {
setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME,
SYSTEM_DRIVER_VERSION_CODE,
SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0),
@@ -178,32 +176,16 @@ public class GraphicsEnvironment {
}
/**
- * Query to determine if the Game Mode has enabled ANGLE.
+ * Query to determine the ANGLE driver choice.
*/
- private boolean isAngleEnabledByGameMode(Context context, String packageName) {
- try {
- final boolean gameModeEnabledAngle =
- (mGameManager != null) && mGameManager.isAngleEnabled(packageName);
- Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle);
- return gameModeEnabledAngle;
- } catch (SecurityException e) {
- Log.e(TAG, "Caught exception while querying GameManagerService if ANGLE is enabled "
- + "for package: " + packageName);
- }
-
- return false;
- }
-
- /**
- * Query to determine if ANGLE should be used
- */
- private boolean shouldUseAngle(Context context, Bundle coreSettings, String packageName) {
+ private String queryAngleChoice(Context context, Bundle coreSettings,
+ String packageName) {
if (TextUtils.isEmpty(packageName)) {
Log.v(TAG, "No package name specified; use the system driver");
- return false;
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
- return shouldUseAngleInternal(context, coreSettings, packageName);
+ return queryAngleChoiceInternal(context, coreSettings, packageName);
}
private int getVulkanVersion(PackageManager pm) {
@@ -421,13 +403,13 @@ public class GraphicsEnvironment {
* 2) The per-application switch (i.e. Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS and
* Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES; which corresponds to the
* “angle_gl_driver_selection_pkgs” and “angle_gl_driver_selection_values” settings); if it
- * forces a choice;
- * 3) Use ANGLE if isAngleEnabledByGameMode() returns true;
+ * forces a choice.
*/
- private boolean shouldUseAngleInternal(Context context, Bundle bundle, String packageName) {
+ private String queryAngleChoiceInternal(Context context, Bundle bundle,
+ String packageName) {
// Make sure we have a good package name
if (TextUtils.isEmpty(packageName)) {
- return false;
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
// Check the semi-global switch (i.e. once system has booted enough) for whether ANGLE
@@ -442,7 +424,7 @@ public class GraphicsEnvironment {
}
if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
Log.v(TAG, "Turn on ANGLE for all applications.");
- return true;
+ return ANGLE_GL_DRIVER_CHOICE_ANGLE;
}
// Get the per-application settings lists
@@ -455,17 +437,15 @@ public class GraphicsEnvironment {
Log.v(TAG, " angle_gl_driver_selection_pkgs=" + optInPackages);
Log.v(TAG, " angle_gl_driver_selection_values=" + optInValues);
- mEnabledByGameMode = isAngleEnabledByGameMode(context, packageName);
-
// Make sure we have good settings to use
- if (optInPackages.size() != optInValues.size()) {
+ if (optInPackages.size() == 0 || optInPackages.size() != optInValues.size()) {
Log.v(TAG,
"Global.Settings values are invalid: "
+ "number of packages: "
+ optInPackages.size() + ", "
+ "number of values: "
+ optInValues.size());
- return mEnabledByGameMode;
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
// See if this application is listed in the per-application settings list
@@ -473,7 +453,7 @@ public class GraphicsEnvironment {
if (pkgIndex < 0) {
Log.v(TAG, packageName + " is not listed in per-application setting");
- return mEnabledByGameMode;
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
mAngleOptInIndex = pkgIndex;
@@ -484,14 +464,12 @@ public class GraphicsEnvironment {
"ANGLE Developer option for '" + packageName + "' "
+ "set to: '" + optInValue + "'");
if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) {
- return true;
+ return ANGLE_GL_DRIVER_CHOICE_ANGLE;
} else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
- return false;
- } else {
- // The user either chose default or an invalid value; go with the default driver or what
- // the game mode indicates
- return mEnabledByGameMode;
+ return ANGLE_GL_DRIVER_CHOICE_NATIVE;
}
+ // The user either chose default or an invalid value; go with the default driver.
+ return ANGLE_GL_DRIVER_CHOICE_DEFAULT;
}
/**
@@ -503,10 +481,12 @@ public class GraphicsEnvironment {
final List<ResolveInfo> resolveInfos =
pm.queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
if (resolveInfos.size() != 1) {
- Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+ Log.v(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+ resolveInfos.size());
- for (ResolveInfo resolveInfo : resolveInfos) {
- Log.e(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+ if (DEBUG) {
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ Log.d(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+ }
}
return "";
}
@@ -541,26 +521,46 @@ public class GraphicsEnvironment {
}
/**
- * Determine whether ANGLE should be used, set it up if so, and pass ANGLE details down to
- * the C++ GraphicsEnv class.
- *
- * If ANGLE will be used, GraphicsEnv::setAngleInfo() will be called to enable ANGLE to be
- * properly used.
+ * Determine whether ANGLE should be used, attempt to set up from apk first, if ANGLE can be
+ * set up from apk, pass ANGLE details down to the C++ GraphicsEnv class via
+ * GraphicsEnv::setAngleInfo(). If apk setup fails, attempt to set up to use system ANGLE.
+ * Return false if both fail.
*
- * @param context
- * @param bundle
- * @param pm
+ * @param context - Context of the application.
+ * @param bundle - Bundle of the application.
+ * @param packageManager - PackageManager of the application process.
* @param packageName - package name of the application.
- * @return true: ANGLE setup successfully
- * false: ANGLE not setup (not on allowlist, ANGLE not present, etc.)
+ * @return true: can set up to use ANGLE successfully.
+ * false: can not set up to use ANGLE (not on allowlist, ANGLE not present, etc.)
*/
- private boolean setupAngle(Context context, Bundle bundle, PackageManager pm,
+ private boolean setupAngle(Context context, Bundle bundle, PackageManager packageManager,
String packageName) {
-
- if (!shouldUseAngle(context, bundle, packageName)) {
+ final String angleChoice = queryAngleChoice(context, bundle, packageName);
+ if (angleChoice.equals(ANGLE_GL_DRIVER_CHOICE_DEFAULT)) {
return false;
}
+ if (angleChoice.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
+ nativeSetAngleInfo("", true, packageName, null);
+ return false;
+ }
+
+ return setupAngleFromApk(context, bundle, packageManager, packageName)
+ || setupAngleFromSystem(context, bundle, packageName);
+ }
+ /**
+ * Attempt to set up ANGLE from the packaged apk, if the apk can be found, pass ANGLE details to
+ * the C++ GraphicsEnv class.
+ *
+ * @param context - Context of the application.
+ * @param bundle - Bundle of the application.
+ * @param packageManager - PackageManager of the application process.
+ * @param packageName - package name of the application.
+ * @return true: can set up to use ANGLE apk.
+ * false: can not set up to use ANGLE apk (ANGLE apk not present, etc.)
+ */
+ private boolean setupAngleFromApk(Context context, Bundle bundle, PackageManager packageManager,
+ String packageName) {
ApplicationInfo angleInfo = null;
// If the developer has specified a debug package over ADB, attempt to find it
@@ -569,7 +569,7 @@ public class GraphicsEnvironment {
Log.v(TAG, "ANGLE debug package enabled: " + anglePkgName);
try {
// Note the debug package does not have to be pre-installed
- angleInfo = pm.getApplicationInfo(anglePkgName, 0);
+ angleInfo = packageManager.getApplicationInfo(anglePkgName, 0);
} catch (PackageManager.NameNotFoundException e) {
// If the debug package is specified but not found, abort.
Log.v(TAG, "ANGLE debug package '" + anglePkgName + "' not installed");
@@ -579,7 +579,7 @@ public class GraphicsEnvironment {
// Otherwise, check to see if ANGLE is properly installed
if (angleInfo == null) {
- anglePkgName = getAnglePackageName(pm);
+ anglePkgName = getAnglePackageName(packageManager);
if (TextUtils.isEmpty(anglePkgName)) {
Log.v(TAG, "Failed to find ANGLE package.");
return false;
@@ -588,7 +588,7 @@ public class GraphicsEnvironment {
Log.v(TAG, "ANGLE package enabled: " + anglePkgName);
try {
// Production ANGLE libraries must be pre-installed as a system app
- angleInfo = pm.getApplicationInfo(anglePkgName,
+ angleInfo = packageManager.getApplicationInfo(anglePkgName,
PackageManager.MATCH_SYSTEM_ONLY);
} catch (PackageManager.NameNotFoundException e) {
Log.v(TAG, "ANGLE package '" + anglePkgName + "' not installed");
@@ -609,11 +609,28 @@ public class GraphicsEnvironment {
Log.d(TAG, "ANGLE package libs: " + paths);
}
- // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name,
- // and features to use.
+ // If we make it to here, ANGLE apk will be used. Call nativeSetAngleInfo() with the
+ // application package name and ANGLE features to use.
final String[] features = getAngleEglFeatures(context, bundle);
- setAngleInfo(paths, packageName, ANGLE_GL_DRIVER_CHOICE_ANGLE, features);
+ nativeSetAngleInfo(paths, false, packageName, features);
+
+ return true;
+ }
+ /**
+ * Set up ANGLE from system.
+ *
+ * @param context - Context of the application.
+ * @param bundle - Bundle of the application.
+ * @param packageName - package name of the application.
+ * @return true: can set up to use system ANGLE.
+ * false: can not set up to use system ANGLE because it doesn't exist.
+ */
+ private boolean setupAngleFromSystem(Context context, Bundle bundle, String packageName) {
+ // System ANGLE always exists, call nativeSetAngleInfo() with the application package
+ // name and ANGLE features to use.
+ final String[] features = getAngleEglFeatures(context, bundle);
+ nativeSetAngleInfo(SYSTEM_ANGLE_STRING, false, packageName, features);
return true;
}
@@ -636,7 +653,10 @@ public class GraphicsEnvironment {
}
/**
- * Show the ANGLE in Use Dialog Box
+ * Show the ANGLE in use dialog box.
+ * The ANGLE in use dialog box will show up as long as the application
+ * should use ANGLE. It does not mean the application has successfully
+ * loaded ANGLE because this check happens before the loading completes.
* @param context
*/
public void showAngleInUseDialogBox(Context context) {
@@ -644,14 +664,15 @@ public class GraphicsEnvironment {
return;
}
- final String packageName = context.getPackageName();
- if (!getShouldUseAngle(packageName)) {
+ if (!mShouldUseAngle) {
return;
}
final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE);
final String anglePkg = getAnglePackageName(context.getPackageManager());
- intent.setPackage(anglePkg);
+ if (anglePkg.isEmpty()) {
+ return;
+ }
context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
@Override
@@ -890,9 +911,8 @@ public class GraphicsEnvironment {
private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
private static native void setGpuStats(String driverPackageName, String driverVersionName,
long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
- private static native void setAngleInfo(String path, String appPackage,
- String devOptIn, String[] features);
- private static native boolean getShouldUseAngle(String packageName);
+ private static native void nativeSetAngleInfo(String path, boolean useNativeDriver,
+ String packageName, String[] features);
private static native boolean setInjectLayersPrSetDumpable();
private static native void nativeToggleAngleAsSystemDriver(boolean enabled);
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 1a3dcee427d8..a49ee7d00751 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -104,16 +104,6 @@ interface INetworkManagementService
void setIPv6AddrGenMode(String iface, int mode);
/**
- * Add the specified route to the interface.
- */
- void addRoute(int netId, in RouteInfo route);
-
- /**
- * Remove the specified route from the interface.
- */
- void removeRoute(int netId, in RouteInfo route);
-
- /**
* Shuts down the service
*/
@EnforcePermission("SHUTDOWN")
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 180735bf48eb..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.
@@ -2372,14 +2376,14 @@ public final class StrictMode {
}
/** Assume locked until we hear otherwise */
- private static volatile boolean sUserKeyUnlocked = false;
+ private static volatile boolean sCeStorageUnlocked = false;
- private static boolean isUserKeyUnlocked(int userId) {
+ private static boolean isCeStorageUnlocked(int userId) {
final IStorageManager storage = IStorageManager.Stub
.asInterface(ServiceManager.getService("mount"));
if (storage != null) {
try {
- return storage.isUserKeyUnlocked(userId);
+ return storage.isCeStorageUnlocked(userId);
} catch (RemoteException ignored) {
}
}
@@ -2392,13 +2396,13 @@ public final class StrictMode {
// since any relocking of that user will always result in our
// process being killed to release any CE FDs we're holding onto.
if (userId == UserHandle.myUserId()) {
- if (sUserKeyUnlocked) {
+ if (sCeStorageUnlocked) {
return;
- } else if (isUserKeyUnlocked(userId)) {
- sUserKeyUnlocked = true;
+ } else if (isCeStorageUnlocked(userId)) {
+ sCeStorageUnlocked = true;
return;
}
- } else if (isUserKeyUnlocked(userId)) {
+ } else if (isCeStorageUnlocked(userId)) {
return;
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 159394b2b776..bbfd5938774b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -23,6 +23,7 @@ import android.Manifest;
import android.accounts.AccountManager;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -58,6 +59,7 @@ import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
+import android.nfc.Flags;
import android.provider.Settings;
import android.util.AndroidException;
import android.util.ArraySet;
@@ -1831,6 +1833,7 @@ public class UserManager {
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_USER_RESTRICTION)
public static final String DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO =
"no_near_field_communication_radio";
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 77229c44cc8d..40311535fc1e 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -1,6 +1,13 @@
package: "android.os"
flag {
+ name: "android_os_build_vanilla_ice_cream"
+ namespace: "build"
+ description: "Feature flag for adding the VANILLA_ICE_CREAM constant."
+ bug: "264658905"
+}
+
+flag {
name: "state_of_health_public"
namespace: "system_sw_battery"
description: "Feature flag for making state_of_health a public api."
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index bc52744078ea..25b2aa7842f2 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -134,20 +134,20 @@ interface IStorageManager {
@EnforcePermission("MOUNT_UNMOUNT_FILESYSTEMS")
void setDebugFlags(int flags, int mask) = 60;
@EnforcePermission("STORAGE_INTERNAL")
- void createUserKey(int userId, int serialNumber, boolean ephemeral) = 61;
+ void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) = 61;
@EnforcePermission("STORAGE_INTERNAL")
- void destroyUserKey(int userId) = 62;
+ void destroyUserStorageKeys(int userId) = 62;
@EnforcePermission("STORAGE_INTERNAL")
- void unlockUserKey(int userId, int serialNumber, in byte[] secret) = 63;
+ void unlockCeStorage(int userId, int serialNumber, in byte[] secret) = 63;
@EnforcePermission("STORAGE_INTERNAL")
- void lockUserKey(int userId) = 64;
- boolean isUserKeyUnlocked(int userId) = 65;
+ void lockCeStorage(int userId) = 64;
+ boolean isCeStorageUnlocked(int userId) = 65;
@EnforcePermission("STORAGE_INTERNAL")
void prepareUserStorage(in String volumeUuid, int userId, int serialNumber, int flags) = 66;
@EnforcePermission("STORAGE_INTERNAL")
void destroyUserStorage(in String volumeUuid, int userId, int flags) = 67;
@EnforcePermission("STORAGE_INTERNAL")
- void setUserKeyProtection(int userId, in byte[] secret) = 70;
+ void setCeStorageProtection(int userId, in byte[] secret) = 70;
@EnforcePermission("MOUNT_FORMAT_FILESYSTEMS")
void fstrim(int flags, IVoldTaskListener listener) = 72;
AppFuseMount mountProxyFileDescriptorBridge() = 73;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 80dd48825ba7..8e72e13051b0 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1580,28 +1580,64 @@ public class StorageManager {
DEFAULT_FULL_THRESHOLD_BYTES);
}
- /** {@hide} */
- public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
+ /**
+ * Creates the keys for a user's credential-encrypted (CE) and device-encrypted (DE) storage.
+ * <p>
+ * This creates the user's CE key and DE key for internal storage, then adds them to the kernel.
+ * Then, if the user is not ephemeral, this stores the DE key (encrypted) on flash. (The CE key
+ * is not stored until {@link IStorageManager#setCeStorageProtection()}.)
+ * <p>
+ * This does not create the CE and DE directories themselves. For that, see {@link
+ * #prepareUserStorage()}.
+ * <p>
+ * This is only intended to be called by UserManagerService, as part of creating a user.
+ *
+ * @param userId ID of the user
+ * @param serialNumber serial number of the user
+ * @param ephemeral whether the user is ephemeral
+ * @throws RuntimeException on error. The user's keys already existing is considered an error.
+ * @hide
+ */
+ public void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) {
try {
- mStorageManager.createUserKey(userId, serialNumber, ephemeral);
+ mStorageManager.createUserStorageKeys(userId, serialNumber, ephemeral);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- /** {@hide} */
- public void destroyUserKey(int userId) {
+ /**
+ * Destroys the keys for a user's credential-encrypted (CE) and device-encrypted (DE) storage.
+ * <p>
+ * This evicts the keys from the kernel (if present), which "locks" the corresponding
+ * directories. Then, this deletes the encrypted keys from flash. This operates on all the
+ * user's CE and DE keys, for both internal and adoptable storage.
+ * <p>
+ * This does not destroy the CE and DE directories themselves. For that, see {@link
+ * #destroyUserStorage()}.
+ * <p>
+ * This is only intended to be called by UserManagerService, as part of removing a user.
+ *
+ * @param userId ID of the user
+ * @throws RuntimeException on error. On error, as many things as possible are still destroyed.
+ * @hide
+ */
+ public void destroyUserStorageKeys(int userId) {
try {
- mStorageManager.destroyUserKey(userId);
+ mStorageManager.destroyUserStorageKeys(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- /** {@hide} */
- public void lockUserKey(int userId) {
+ /**
+ * Locks the user's credential-encrypted (CE) storage.
+ *
+ * @hide
+ */
+ public void lockCeStorage(int userId) {
try {
- mStorageManager.lockUserKey(userId);
+ mStorageManager.lockCeStorage(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1628,17 +1664,26 @@ public class StorageManager {
/** {@hide} */
@TestApi
public static boolean isUserKeyUnlocked(int userId) {
+ return isCeStorageUnlocked(userId);
+ }
+
+ /**
+ * Returns true if the user's credential-encrypted (CE) storage is unlocked.
+ *
+ * @hide
+ */
+ public static boolean isCeStorageUnlocked(int userId) {
if (sStorageManager == null) {
sStorageManager = IStorageManager.Stub
.asInterface(ServiceManager.getService("mount"));
}
if (sStorageManager == null) {
- Slog.w(TAG, "Early during boot, assuming locked");
+ Slog.w(TAG, "Early during boot, assuming CE storage is locked");
return false;
}
final long token = Binder.clearCallingIdentity();
try {
- return sStorageManager.isUserKeyUnlocked(userId);
+ return sStorageManager.isCeStorageUnlocked(userId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
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/provider/Settings.java b/core/java/android/provider/Settings.java
index 9cdb9cefdc43..2f86efec7086 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9926,6 +9926,13 @@ public final class Settings {
public static final String SPATIAL_AUDIO_ENABLED = "spatial_audio_enabled";
/**
+ * Internal collection of audio device inventory items
+ * The device item stored are {@link com.android.server.audio.AdiDeviceState}
+ * @hide
+ */
+ public static final String AUDIO_DEVICE_INVENTORY = "audio_device_inventory";
+
+ /**
* Indicates whether notification display on the lock screen is enabled.
* <p>
* Type: int (0 for false, 1 for true)
@@ -17965,6 +17972,15 @@ public final class Settings {
"review_permissions_notification_state";
/**
+ * Whether repair mode is active on the device.
+ * <p>
+ * Set to 1 for true and 0 for false.
+ *
+ * @hide
+ */
+ public static final String REPAIR_MODE_ACTIVE = "repair_mode_active";
+
+ /**
* Settings migrated from Wear OS settings provider.
* @hide
*/
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 96c0be7f803e..33a67aed6023 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -8,3 +8,4 @@ per-file *NetworkSecurityPolicy.java = file:net/OWNERS
per-file Confirmation*.java = file:/keystore/OWNERS
per-file FileIntegrityManager.java = file:platform/system/security:/fsverity/OWNERS
per-file IFileIntegrityService.aidl = file:platform/system/security:/fsverity/OWNERS
+per-file *.aconfig = victorhsieh@google.com
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index b6c2b83f0daa..5aa309753cc4 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -12,4 +12,5 @@ flag {
namespace: "hardware_backed_security"
description: "Fix bugs in behavior of UnlockedDeviceRequired keystore keys"
bug: "296464083"
+ is_fixed_read_only: true
}
diff --git a/core/java/android/service/rotationresolver/OWNERS b/core/java/android/service/rotationresolver/OWNERS
index 5b57fc7fa528..dce874dcd982 100644
--- a/core/java/android/service/rotationresolver/OWNERS
+++ b/core/java/android/service/rotationresolver/OWNERS
@@ -1,9 +1,7 @@
# Bug component: 814982
asalo@google.com
-augale@google.com
eejiang@google.com
payamp@google.com
siddikap@google.com
-svetoslavganov@google.com
tgadh@google.com
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index 42ac74ce7353..ad326e41a146 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -11,6 +11,7 @@ jjaggi@google.com
roosa@google.com
jreck@google.com
siyamed@google.com
+mount@google.com
# Autofill
per-file ViewStructure.java = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/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/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index 25bf85cfaa58..ec792197a329 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -21,11 +21,13 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.IBinder;
import android.os.InputConfig;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
+import android.util.SparseArray;
import android.view.InputWindowHandle;
import java.util.ArrayList;
@@ -119,13 +121,13 @@ public class WindowInfosListenerForTest {
"Exception thrown while waiting for listener to be called with "
+ "initial state");
}
- consumer.accept(buildWindowInfos(windowHandles));
+ consumer.accept(buildWindowInfos(windowHandles, displayInfos));
}
};
mListeners.put(consumer, listener);
Pair<InputWindowHandle[], WindowInfosListener.DisplayInfo[]> initialState =
listener.register();
- consumer.accept(buildWindowInfos(initialState.first));
+ consumer.accept(buildWindowInfos(initialState.first, initialState.second));
calledWithInitialState.countDown();
}
@@ -140,11 +142,28 @@ public class WindowInfosListenerForTest {
listener.unregister();
}
- private static List<WindowInfo> buildWindowInfos(InputWindowHandle[] windowHandles) {
+ private static List<WindowInfo> buildWindowInfos(
+ InputWindowHandle[] windowHandles, WindowInfosListener.DisplayInfo[] displayInfos) {
var windowInfos = new ArrayList<WindowInfo>(windowHandles.length);
+
+ var displayInfoById = new SparseArray<WindowInfosListener.DisplayInfo>(displayInfos.length);
+ for (var displayInfo : displayInfos) {
+ displayInfoById.put(displayInfo.mDisplayId, displayInfo);
+ }
+
+ var tmp = new RectF();
for (var handle : windowHandles) {
var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight,
handle.frameBottom);
+
+ // Transform bounds from physical display coordinates to logical display coordinates.
+ var display = displayInfoById.get(handle.displayId);
+ if (display != null) {
+ tmp.set(bounds);
+ display.mTransform.mapRect(tmp);
+ tmp.round(bounds);
+ }
+
windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, handle.displayId,
bounds, handle.inputConfig));
}
diff --git a/core/java/android/window/flags/OWNERS b/core/java/android/window/flags/OWNERS
new file mode 100644
index 000000000000..fa81ee3905c3
--- /dev/null
+++ b/core/java/android/window/flags/OWNERS
@@ -0,0 +1 @@
+per-file responsible_apis.aconfig = file:/BAL_OWNERS \ No newline at end of file
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 4e7bfe50cd30..71bbccb3d989 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -259,7 +259,7 @@ public class PlatLogoActivity extends Activity {
}
return true;
}
- return false;
+ return super.onKeyDown(keyCode,event);
}
@Override
@@ -268,7 +268,7 @@ public class PlatLogoActivity extends Activity {
stopWarp();
return true;
}
- return false;
+ return super.onKeyUp(keyCode,event);
}
private void startWarp() {
diff --git a/core/java/com/android/internal/foldables/OWNERS b/core/java/com/android/internal/foldables/OWNERS
new file mode 100644
index 000000000000..6ce1ee4d3de2
--- /dev/null
+++ b/core/java/com/android/internal/foldables/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/display/OWNERS
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 965277c4635e..1c5f4f0f1369 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -868,6 +868,11 @@ public final class Zygote {
args.mPkgDataInfoList, args.mAllowlistedDataInfoList,
args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
+ // While `specializeAppProcess` sets the thread name on the process's main thread, this
+ // is distinct from the app process name which appears in stack traces, as the latter is
+ // sourced from the argument buffer of the Process class. Set the app process name here.
+ Zygote.setAppProcessName(args, TAG);
+
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 993e4e7b4b3d..5fe086da8c6a 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -296,7 +296,6 @@ class ZygoteConnection {
} else {
// child; result is a Runnable.
zygoteServer.setForkChild();
- Zygote.setAppProcessName(parsedArgs, TAG); // ??? Necessary?
return result;
}
}
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;", "&#1;", "&#2;", "&#3;", "&#4;", "&#5;", "&#6;", "&#7;", // 0-7
- "&#8;", "&#9;", "&#10;", "&#11;", "&#12;", "&#13;", "&#14;", "&#15;", // 8-15
- "&#16;", "&#17;", "&#18;", "&#19;", "&#20;", "&#21;", "&#22;", "&#23;", // 16-23
- "&#24;", "&#25;", "&#26;", "&#27;", "&#28;", "&#29;", "&#30;", "&#31;", // 24-31
- null, null, "&quot;", null, null, null, "&amp;", 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, "&lt;", null, "&gt;", 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/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 406505517bcc..82367834f93d 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -108,4 +108,5 @@ interface ILockSettings {
boolean removeWeakEscrowToken(long handle, int userId);
boolean isWeakEscrowTokenActive(long handle, int userId);
boolean isWeakEscrowTokenValid(long handle, in byte[] token, int userId);
+ void unlockUserKeyIfUnsecured(int userId);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a1f8de41cfce..a3e0016f9174 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -47,6 +47,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -160,9 +161,17 @@ public class LockPatternUtils {
*/
public static final int VERIFY_FLAG_REQUEST_GK_PW_HANDLE = 1 << 0;
+ /**
+ * Flag provided to {@link #verifyCredential(LockscreenCredential, int, int)} . If set, the
+ * method writes the password data to the repair mode file after the credential is verified
+ * successfully.
+ */
+ public static final int VERIFY_FLAG_WRITE_REPAIR_MODE_PW = 1 << 1;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
- VERIFY_FLAG_REQUEST_GK_PW_HANDLE
+ VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
+ VERIFY_FLAG_WRITE_REPAIR_MODE_PW
})
public @interface VerifyFlag {}
@@ -171,6 +180,11 @@ public class LockPatternUtils {
*/
public static final int USER_FRP = UserHandle.USER_NULL + 1;
+ /**
+ * Special user id for triggering the exiting repair mode verification flow.
+ */
+ public static final int USER_REPAIR_MODE = UserHandle.USER_NULL + 2;
+
public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";
@Deprecated
public final static String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate";
@@ -200,6 +214,8 @@ public class LockPatternUtils {
public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle";
public static final String PASSWORD_HISTORY_DELIMITER = ",";
+ private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
+
/**
* drives the pin auto confirmation feature availability in code logic.
*/
@@ -388,7 +404,7 @@ public class LockPatternUtils {
@UnsupportedAppUsage
public void reportFailedPasswordAttempt(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
+ if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
return;
}
getDevicePolicyManager().reportFailedPasswordAttempt(userId);
@@ -397,7 +413,7 @@ public class LockPatternUtils {
@UnsupportedAppUsage
public void reportSuccessfulPasswordAttempt(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
+ if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
return;
}
getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);
@@ -405,21 +421,21 @@ public class LockPatternUtils {
}
public void reportPasswordLockout(int timeoutMs, int userId) {
- if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
+ if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
return;
}
getTrustManager().reportUnlockLockout(timeoutMs, userId);
}
public int getCurrentFailedPasswordAttempts(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
+ if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
return 0;
}
return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId);
}
public int getMaximumFailedPasswordsForWipe(int userId) {
- if (userId == USER_FRP && frpCredentialEnabled(mContext)) {
+ if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) {
return 0;
}
return getDevicePolicyManager().getMaximumFailedPasswordsForWipe(
@@ -739,6 +755,17 @@ public class LockPatternUtils {
}
}
+ /** Returns the credential type corresponding to the given PIN or password quality. */
+ public static int pinOrPasswordQualityToCredentialType(int quality) {
+ if (isQualityAlphabeticPassword(quality)) {
+ return CREDENTIAL_TYPE_PASSWORD;
+ }
+ if (isQualityNumericPin(quality)) {
+ return CREDENTIAL_TYPE_PIN;
+ }
+ throw new IllegalArgumentException("Quality is neither Pin nor password: " + quality);
+ }
+
/**
* Save a new lockscreen credential.
*
@@ -971,7 +998,7 @@ public class LockPatternUtils {
}
@Override
public boolean shouldBypassCache(Integer userHandle) {
- return userHandle == USER_FRP;
+ return isSpecialUserId(userHandle);
}
};
@@ -1819,6 +1846,64 @@ public class LockPatternUtils {
}
/**
+ * Return {@code true} if repair mode is supported by the device.
+ */
+ public static boolean isRepairModeSupported(Context context) {
+ return context.getResources().getBoolean(
+ com.android.internal.R.bool.config_repairModeSupported);
+ }
+
+ /**
+ * Return {@code true} if repair mode is active on the device.
+ */
+ public static boolean isRepairModeActive(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.REPAIR_MODE_ACTIVE, /* def= */ 0) > 0;
+ }
+
+ /**
+ * Return {@code true} if repair mode is supported by the device and the user has been granted
+ * admin privileges.
+ */
+ public static boolean canUserEnterRepairMode(Context context, UserInfo info) {
+ return info != null && info.isAdmin() && isRepairModeSupported(context);
+ }
+
+ /**
+ * Return {@code true} if GSI is running on the device.
+ */
+ public static boolean isGsiRunning() {
+ return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
+ }
+
+ /**
+ * Return {@code true} if the given user id is a special user such as {@link #USER_FRP}.
+ */
+ public static boolean isSpecialUserId(int userId) {
+ return isSpecialUserId(/* context= */ null, userId, /* checkDeviceSupported= */ false);
+ }
+
+ /**
+ * Return {@code true} if the given user id is a special user for the verification flow.
+ *
+ * @param checkDeviceSupported {@code true} to check the specified user is supported
+ * by the device.
+ */
+ private static boolean isSpecialUserId(@Nullable Context context, int userId,
+ boolean checkDeviceSupported) {
+ switch (userId) {
+ case USER_FRP:
+ if (checkDeviceSupported) return frpCredentialEnabled(context);
+ return true;
+
+ case USER_REPAIR_MODE:
+ if (checkDeviceSupported) return isRepairModeSupported(context);
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Attempt to rederive the unified work challenge for the specified profile user and unlock the
* user. If successful, this would allow the user to leave quiet mode automatically without
* additional user authentication.
@@ -1847,8 +1932,29 @@ 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.
+ * <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.
+ * <p>
+ * Except on permission denied, this method doesn't throw an exception on failure. However, the
+ * last thing that it does is unlock CE storage, and whether CE storage has been successfully
+ * unlocked can be determined by {@link StorageManager#isCeStorageUnlocked()}.
+ * <p>
+ * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ *
+ * @param userId the ID of the user whose keys to unlock
+ */
public void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
- getLockSettingsInternal().unlockUserKeyIfUnsecured(userId);
+ try {
+ getLockSettings().unlockUserKeyIfUnsecured(userId);
+ } catch (RemoteException re) {
+ re.rethrowFromSystemServer();
+ }
}
public void createNewUser(@UserIdInt int userId, int userSerialNumber) {
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index 6063c90d6ab9..8114e1fd3bb0 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -60,17 +60,6 @@ public abstract class LockSettingsInternal {
public abstract void onThirdPartyAppsStarted();
/**
- * Unlocks the credential-encrypted storage for the given user if the user is not secured, i.e.
- * doesn't have an LSKF.
- * <p>
- * This doesn't throw an exception on failure; whether the storage has been unlocked can be
- * determined by {@link StorageManager#isUserKeyUnlocked()}.
- *
- * @param userId the ID of the user whose storage to unlock
- */
- public abstract void unlockUserKeyIfUnsecured(@UserIdInt int userId);
-
- /**
* Creates the locksettings state for a new user.
* <p>
* This includes creating a synthetic password and protecting it with an empty LSKF.
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index b1dab85d2e27..8fa179b496b6 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -574,7 +574,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_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index 01dbceb38d3a..8fc30d1c248d 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -16,11 +16,12 @@
#define LOG_TAG "GraphicsEnvironment"
-#include <vector>
-
#include <graphicsenv/GraphicsEnv.h>
#include <nativehelper/ScopedUtfChars.h>
#include <nativeloader/native_loader.h>
+
+#include <vector>
+
#include "core_jni_helpers.h"
namespace {
@@ -49,11 +50,10 @@ void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName,
appPackageNameChars.c_str(), vulkanVersion);
}
-void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName,
- jstring devOptIn, jobjectArray featuresObj) {
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useNativeDriver,
+ jstring packageName, jobjectArray featuresObj) {
ScopedUtfChars pathChars(env, path);
- ScopedUtfChars appNameChars(env, appName);
- ScopedUtfChars devOptInChars(env, devOptIn);
+ ScopedUtfChars packageNameChars(env, packageName);
std::vector<std::string> features;
if (featuresObj != nullptr) {
@@ -73,13 +73,8 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appNa
}
}
- android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
- devOptInChars.c_str(), features);
-}
-
-bool shouldUseAngle_native(JNIEnv* env, jobject clazz, jstring appName) {
- ScopedUtfChars appNameChars(env, appName);
- return android::GraphicsEnv::getInstance().shouldUseAngle(appNameChars.c_str());
+ android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useNativeDriver,
+ packageNameChars.c_str(), features);
}
void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -123,11 +118,8 @@ const JNINativeMethod g_methods[] = {
reinterpret_cast<void*>(setGpuStats_native)},
{"setInjectLayersPrSetDumpable", "()Z",
reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)},
- {"setAngleInfo",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",
+ {"nativeSetAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V",
reinterpret_cast<void*>(setAngleInfo_native)},
- {"getShouldUseAngle", "(Ljava/lang/String;)Z",
- reinterpret_cast<void*>(shouldUseAngle_native)},
{"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V",
reinterpret_cast<void*>(setLayerPaths_native)},
{"setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native)},
diff --git a/core/proto/android/nfc/Android.bp b/core/proto/android/nfc/Android.bp
deleted file mode 100644
index 6a62c917f240..000000000000
--- a/core/proto/android/nfc/Android.bp
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// 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 {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-filegroup {
- name: "srcs_nfc_proto",
- srcs: [
- "*.proto",
- ],
-}
-
-// Will be statically linked by `framework-nfc`.
-java_library {
- name: "nfc-proto-java-gen",
- installable: false,
- proto: {
- type: "stream",
- include_dirs: [
- "external/protobuf/src",
- ],
- },
- srcs: [
- ":srcs_nfc_proto",
- ],
- sdk_version: "current",
- min_sdk_version: "current",
-}
diff --git a/core/proto/android/service/OWNERS b/core/proto/android/service/OWNERS
index 70cb50f75362..7a19155e4278 100644
--- a/core/proto/android/service/OWNERS
+++ b/core/proto/android/service/OWNERS
@@ -1 +1,2 @@
per-file sensor_service.proto = arthuri@google.com, bduddie@google.com, stange@google.com
+per-file package.proto = file:platform/frameworks/base:/PACKAGE_MANAGER_OWNERS
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/config.xml b/core/res/res/values/config.xml
index 93048ea4fc85..a872efefed00 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6467,4 +6467,9 @@
<!-- Whether the AOSP support for app cloning building blocks is to be enabled for the
device. -->
<bool name="config_enableAppCloningBuildingBlocks">true</bool>
+
+ <!-- Enables or disables support for repair mode. The feature creates a secure
+ environment to protect the user's privacy when the device is being repaired.
+ Off by default, since OEMs may have had a similar feature on their devices. -->
+ <bool name="config_repairModeSupported">false</bool>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9bb338773e53..e81dfe74c2f0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4917,6 +4917,8 @@
<java-symbol type="bool" name="config_safetyProtectionEnabled" />
+ <java-symbol type="bool" name="config_repairModeSupported" />
+
<java-symbol type="string" name="config_devicePolicyManagementUpdater" />
<java-symbol type="string" name="config_deviceSpecificDeviceStatePolicyProvider" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index af8c69ea1441..3a2e50aa06e8 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -54,6 +54,9 @@
<!-- Azerbaijan: 4-5 digits, known premium codes listed -->
<shortcode country="az" pattern="\\d{4,5}" premium="330[12]|87744|901[234]|93(?:94|101)|9426|9525" />
+ <!-- Bangladesh: 1-5 digits (standard system default, not country specific) -->
+ <shortcode country="bd" pattern="\\d{1,5}" free="16672" />
+
<!-- Belgium: 4 digits, plus EU: http://www.mobileweb.be/en/mobileweb/sms-numberplan.asp -->
<shortcode country="be" premium="\\d{4}" free="8\\d{3}|116\\d{3}" />
@@ -145,7 +148,7 @@
<shortcode country="in" pattern="\\d{1,5}" free="59336|53969" />
<!-- Indonesia: 1-5 digits (standard system default, not country specific) -->
- <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363" />
+ <shortcode country="id" pattern="\\d{1,5}" free="99477|6006|46645|363|93457" />
<!-- Ireland: 5 digits, 5xxxx (50xxx=free, 5[12]xxx=standard), plus EU:
http://www.comreg.ie/_fileupload/publications/ComReg1117.pdf -->
@@ -190,7 +193,7 @@
<shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
<!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101" />
+ <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963|91101|45453" />
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
<shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" />
@@ -205,7 +208,7 @@
<shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" free="2171" />
<!-- New Zealand: 3-4 digits, known premium codes listed -->
- <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
+ <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|3876|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
<!-- Peru: 4-5 digits (not confirmed), known premium codes listed -->
<shortcode country="pe" pattern="\\d{4,5}" free="9963|40778" />
diff --git a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
index fac3a0ecdec2..d34c91ee48ba 100644
--- a/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
+++ b/core/tests/GameManagerTests/src/android/app/GameManagerTests.java
@@ -86,16 +86,6 @@ public final class GameManagerTests {
GameModeInfo gameModeInfo = mGameManager.getGameModeInfo(mPackageName);
assertNotNull(gameModeInfo);
assertNull(gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM));
- GameModeConfiguration unsupportedFpsConfig =
- new GameModeConfiguration.Builder().setFpsOverride(
- 70).setScalingFactor(0.5f).build();
- mGameManager.updateCustomGameModeConfiguration(mPackageName, unsupportedFpsConfig);
- gameModeInfo = mGameManager.getGameModeInfo(mPackageName);
- assertNotNull(gameModeInfo);
- // TODO(b/243448953): update to non-zero FPS when matching is implemented
- assertEquals(new GameModeConfiguration.Builder().setFpsOverride(
- GameModeConfiguration.FPS_OVERRIDE_NONE).setScalingFactor(0.5f).build(),
- gameModeInfo.getGameModeConfiguration(GameManager.GAME_MODE_CUSTOM));
GameModeConfiguration supportedFpsConfig =
new GameModeConfiguration.Builder().setFpsOverride(
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index 64f5e6c68a95..5636f9bc436c 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -10,3 +10,7 @@ per-file *UiAutomation* = file:/services/accessibility/OWNERS
# KeyguardManagerTest
per-file KeyguardManagerTest.java = file:/services/core/java/com/android/server/locksettings/OWNERS
+
+# Files related to background activity launches
+per-file Background*Start* = file:/BAL_OWNERS
+
diff --git a/core/tests/coretests/src/android/content/BrickDeniedTest.java b/core/tests/coretests/src/android/content/BrickDeniedTest.java
deleted file mode 100644
index d8c9baa8df5b..000000000000
--- a/core/tests/coretests/src/android/content/BrickDeniedTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.test.AndroidTestCase;
-
-import androidx.test.filters.SmallTest;
-
-/** Test to make sure brick intents <b>don't</b> work without permission. */
-public class BrickDeniedTest extends AndroidTestCase {
- @SmallTest
- public void testBrick() {
- // Try both the old and new brick intent names. Neither should work,
- // since this test application doesn't have the required permission.
- // If it does work, well, the test certainly won't pass.
- getContext().sendBroadcast(new Intent("SHES_A_BRICK_HOUSE"));
- getContext().sendBroadcast(new Intent("android.intent.action.BRICK"));
- }
-}
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index 2ca99943a8a0..23668a4b2afb 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -20,4 +20,7 @@ per-file *ContentRecord* = file:/services/core/java/com/android/server/wm/OWNER
per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
# Stylus
-per-file stylus/* = file:/core/java/android/text/OWNERS \ No newline at end of file
+per-file stylus/* = file:/core/java/android/text/OWNERS
+
+# View
+file:/core/java/android/view/OWNERS \ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
index 6167c4b80cee..1a668f7bdc8f 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
@@ -37,10 +37,23 @@ public class LockPatternUtilsTest {
}
@Test
+ public void testUserRepairMode_isNotRegularUser() {
+ assertTrue(LockPatternUtils.USER_REPAIR_MODE < 0);
+ }
+
+ @Test
public void testUserFrp_isNotAReservedSpecialUser() throws Exception {
assertNotEquals(UserHandle.USER_NULL, LockPatternUtils.USER_FRP);
assertNotEquals(UserHandle.USER_ALL, LockPatternUtils.USER_FRP);
assertNotEquals(UserHandle.USER_CURRENT, LockPatternUtils.USER_FRP);
assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_FRP);
}
+
+ @Test
+ public void testUserRepairMode_isNotAReservedSpecialUser() throws Exception {
+ assertNotEquals(UserHandle.USER_NULL, LockPatternUtils.USER_REPAIR_MODE);
+ assertNotEquals(UserHandle.USER_ALL, LockPatternUtils.USER_REPAIR_MODE);
+ assertNotEquals(UserHandle.USER_CURRENT, LockPatternUtils.USER_REPAIR_MODE);
+ assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_REPAIR_MODE);
+ }
}
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/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index e27cd978e6be..31092536bac5 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -439,8 +439,10 @@ key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE
key usage 0x0c0173 MEDIA_AUDIO_TRACK
key usage 0x0c019C PROFILE_SWITCH
key usage 0x0c01A2 ALL_APPS
-key usage 0x0d0044 STYLUS_BUTTON_PRIMARY
-key usage 0x0d005a STYLUS_BUTTON_SECONDARY
+# TODO(b/297094448): Add stylus button mappings as a fallback when we have a way to determine
+# if a device can actually report it.
+# key usage 0x0d0044 STYLUS_BUTTON_PRIMARY
+# key usage 0x0d005a STYLUS_BUTTON_SECONDARY
# Joystick and game controller axes.
# Axes that are not mapped will be assigned generic axis numbers by the input subsystem.
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 31c2eb2efaed..b7ea04fdfe07 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -128,25 +128,6 @@ public class AndroidKeyStoreMaintenance {
}
/**
- * Queries user state from Keystore 2.0.
- *
- * @param userId - Android user id of the user.
- * @return UserState enum variant as integer if successful or an error
- */
- public static int getState(int userId) {
- StrictMode.noteDiskRead();
- try {
- return getService().getState(userId);
- } catch (ServiceSpecificException e) {
- Log.e(TAG, "getState 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 off body event was detected.
*/
public static void onDeviceOffBody() {
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index 2d2dd24763c4..b4b3e9275035 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -18,7 +18,9 @@ package android.security;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
@@ -37,7 +39,10 @@ public class Authorization {
public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
- private static IKeystoreAuthorization getService() {
+ /**
+ * @return an instance of IKeystoreAuthorization
+ */
+ public static IKeystoreAuthorization getService() {
return IKeystoreAuthorization.Stub.asInterface(
ServiceManager.checkService("android.security.authorization"));
}
@@ -100,4 +105,24 @@ public class Authorization {
}
}
+ /**
+ * Gets the last authentication time of the given user and authenticators.
+ *
+ * @param userId user id
+ * @param authenticatorTypes an array of {@link HardwareAuthenticatorType}.
+ * @return the last authentication time or
+ * {@link BiometricConstants#BIOMETRIC_NO_AUTHENTICATION}.
+ */
+ public static long getLastAuthenticationTime(
+ long userId, @HardwareAuthenticatorType int[] authenticatorTypes) {
+ try {
+ return getService().getLastAuthTime(userId, authenticatorTypes);
+ } catch (RemoteException | NullPointerException e) {
+ Log.w(TAG, "Can not connect to keystore", e);
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ } catch (ServiceSpecificException e) {
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ }
+ }
+
}
diff --git a/keystore/java/android/security/GateKeeper.java b/keystore/java/android/security/GateKeeper.java
index af188a95c929..464714fe2895 100644
--- a/keystore/java/android/security/GateKeeper.java
+++ b/keystore/java/android/security/GateKeeper.java
@@ -45,8 +45,19 @@ public abstract class GateKeeper {
@UnsupportedAppUsage
public static long getSecureUserId() throws IllegalStateException {
+ return getSecureUserId(UserHandle.myUserId());
+ }
+
+ /**
+ * Return the secure user id for a given user id
+ * @param userId the user id, e.g. 0
+ * @return the secure user id or {@link GateKeeper#INVALID_SECURE_USER_ID} if no such mapping
+ * for the given user id is found.
+ * @throws IllegalStateException if there is an error retrieving the secure user id
+ */
+ public static long getSecureUserId(int userId) throws IllegalStateException {
try {
- return getService().getSecureUserId(UserHandle.myUserId());
+ return getService().getSecureUserId(userId);
} catch (RemoteException e) {
throw new IllegalStateException(
"Failed to obtain secure user ID from gatekeeper", e);
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 8045f55f6b4c..11b827117aa3 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -19,8 +19,6 @@ package android.security;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.StrictMode;
-import android.os.UserHandle;
-import android.security.maintenance.UserState;
/**
* @hide This should not be made public in its present form because it
@@ -37,15 +35,6 @@ public class KeyStore {
// Used for UID field to indicate the calling UID.
public static final int UID_SELF = -1;
- // States
- public enum State {
- @UnsupportedAppUsage
- UNLOCKED,
- @UnsupportedAppUsage
- LOCKED,
- UNINITIALIZED
- };
-
private static final KeyStore KEY_STORE = new KeyStore();
@UnsupportedAppUsage
@@ -55,28 +44,6 @@ public class KeyStore {
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public State state(int userId) {
- int userState = AndroidKeyStoreMaintenance.getState(userId);
- switch (userState) {
- case UserState.UNINITIALIZED:
- return KeyStore.State.UNINITIALIZED;
- case UserState.LSKF_UNLOCKED:
- return KeyStore.State.UNLOCKED;
- case UserState.LSKF_LOCKED:
- return KeyStore.State.LOCKED;
- default:
- throw new AssertionError(userState);
- }
- }
-
- /** @hide */
- @UnsupportedAppUsage
- public State state() {
- return state(UserHandle.myUserId());
- }
-
- /** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public byte[] get(String key) {
return null;
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 96c257b304a0..b7140355e489 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -75,16 +75,18 @@ import javax.security.auth.x500.X500Principal;
* {@link java.security.interfaces.ECPublicKey} or {@link java.security.interfaces.RSAPublicKey}
* interfaces.
*
- * <p>For asymmetric key pairs, a self-signed X.509 certificate will be also generated and stored in
- * the Android Keystore. This is because the {@link java.security.KeyStore} abstraction does not
- * support storing key pairs without a certificate. The subject, serial number, and validity dates
- * of the certificate can be customized in this spec. The self-signed certificate may be replaced at
- * a later time by a certificate signed by a Certificate Authority (CA).
+ * <p>For asymmetric key pairs, a X.509 certificate will be also generated and stored in the Android
+ * Keystore. This is because the {@link java.security.KeyStore} abstraction does not support storing
+ * key pairs without a certificate. The subject, serial number, and validity dates of the
+ * certificate can be customized in this spec. The certificate may be replaced at a later time by a
+ * certificate signed by a Certificate Authority (CA).
*
- * <p>NOTE: If a private key is not authorized to sign the self-signed certificate, then the
- * certificate will be created with an invalid signature which will not verify. Such a certificate
- * is still useful because it provides access to the public key. To generate a valid signature for
- * the certificate the key needs to be authorized for all of the following:
+ * <p>NOTE: If attestation is not requested using {@link Builder#setAttestationChallenge(byte[])},
+ * generated certificate may be self-signed. If a private key is not authorized to sign the
+ * certificate, then the certificate will be created with an invalid signature which will not
+ * verify. Such a certificate is still useful because it provides access to the public key. To
+ * generate a valid signature for the certificate the key needs to be authorized for all of the
+ * following:
* <ul>
* <li>{@link KeyProperties#PURPOSE_SIGN},</li>
* <li>operation without requiring the user to be authenticated (see
@@ -989,12 +991,6 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
* @param purposes set of purposes (e.g., encrypt, decrypt, sign) for which the key can be
* used. Attempts to use the key for any other purpose will be rejected.
*
- * <p>If the set of purposes for which the key can be used does not contain
- * {@link KeyProperties#PURPOSE_SIGN}, the self-signed certificate generated by
- * {@link KeyPairGenerator} of {@code AndroidKeyStore} provider will contain an
- * invalid signature. This is OK if the certificate is only used for obtaining the
- * public key from Android KeyStore.
- *
* <p>See {@link KeyProperties}.{@code PURPOSE} flags.
*/
public Builder(@NonNull String keystoreAlias, @KeyProperties.PurposeEnum int purposes) {
@@ -1140,7 +1136,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
- * Sets the subject used for the self-signed certificate of the generated key pair.
+ * Sets the subject used for the certificate of the generated key pair.
*
* <p>By default, the subject is {@code CN=fake}.
*/
@@ -1154,7 +1150,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
- * Sets the serial number used for the self-signed certificate of the generated key pair.
+ * Sets the serial number used for the certificate of the generated key pair.
*
* <p>By default, the serial number is {@code 1}.
*/
@@ -1168,8 +1164,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
- * Sets the start of the validity period for the self-signed certificate of the generated
- * key pair.
+ * Sets the start of the validity period for the certificate of the generated key pair.
*
* <p>By default, this date is {@code Jan 1 1970}.
*/
@@ -1183,8 +1178,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
- * Sets the end of the validity period for the self-signed certificate of the generated key
- * pair.
+ * Sets the end of the validity period for the certificate of the generated key pair.
*
* <p>By default, this date is {@code Jan 1 2048}.
*/
@@ -1602,6 +1596,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
* {@link #getAttestationChallenge()} returns non-null and the spec is used to generate a
* symmetric (AES or HMAC) key, {@link javax.crypto.KeyGenerator#generateKey()} will throw
* {@link java.security.InvalidAlgorithmParameterException}.
+ *
+ * <p>The challenge may be up to 128 bytes.
*/
@NonNull
public Builder setAttestationChallenge(byte[] attestationChallenge) {
diff --git a/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/api/current.txt b/libs/hwui/api/current.txt
index 794082124344..c396a2032eed 100644
--- a/libs/hwui/api/current.txt
+++ b/libs/hwui/api/current.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android.graphics {
public class ColorMatrix {
diff --git a/libs/hwui/api/module-lib-current.txt b/libs/hwui/api/module-lib-current.txt
index 14191ebcb080..d802177e249b 100644
--- a/libs/hwui/api/module-lib-current.txt
+++ b/libs/hwui/api/module-lib-current.txt
@@ -1,3 +1 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/libs/hwui/api/module-lib-removed.txt b/libs/hwui/api/module-lib-removed.txt
index 14191ebcb080..d802177e249b 100644
--- a/libs/hwui/api/module-lib-removed.txt
+++ b/libs/hwui/api/module-lib-removed.txt
@@ -1,3 +1 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/libs/hwui/api/removed.txt b/libs/hwui/api/removed.txt
index 14191ebcb080..d802177e249b 100644
--- a/libs/hwui/api/removed.txt
+++ b/libs/hwui/api/removed.txt
@@ -1,3 +1 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/libs/hwui/api/system-current.txt b/libs/hwui/api/system-current.txt
index 14191ebcb080..d802177e249b 100644
--- a/libs/hwui/api/system-current.txt
+++ b/libs/hwui/api/system-current.txt
@@ -1,3 +1 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/libs/hwui/api/system-removed.txt b/libs/hwui/api/system-removed.txt
index 14191ebcb080..d802177e249b 100644
--- a/libs/hwui/api/system-removed.txt
+++ b/libs/hwui/api/system-removed.txt
@@ -1,3 +1 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
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/location/Android.bp b/location/Android.bp
index ead46e9b28d9..46dca74e7e40 100644
--- a/location/Android.bp
+++ b/location/Android.bp
@@ -15,10 +15,27 @@ filegroup {
],
}
-java_library {
- name: "framework-location.stubs.module_lib",
+java_sdk_library {
+ name: "framework-location",
srcs: [
":framework-location-nonupdatable-sources",
],
- sdk_version: "core_platform",
+ defaults: ["framework-non-updatable-unbundled-defaults"],
+ permitted_packages: [
+ "android.location",
+ "com.android.internal.location",
+ ],
+ libs: [
+ "app-compat-annotations",
+ "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+ ],
+ hidden_api_packages: [
+ "com.android.internal.location",
+ ],
+ aidl: {
+ include_dirs: [
+ "frameworks/base/location/java",
+ "frameworks/base/core/java",
+ ],
+ },
}
diff --git a/location/api/current.txt b/location/api/current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/module-lib-current.txt b/location/api/module-lib-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/module-lib-removed.txt b/location/api/module-lib-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/removed.txt b/location/api/removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/system-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/system-removed.txt b/location/api/system-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/test-current.txt b/location/api/test-current.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/test-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/location/api/test-removed.txt b/location/api/test-removed.txt
new file mode 100644
index 000000000000..d802177e249b
--- /dev/null
+++ b/location/api/test-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
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/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 29e8716f08ac..cda919f14bc6 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -442,6 +442,7 @@ public final class MediaSession {
* but it must be released if your activity or service is being destroyed.
*/
public void release() {
+ setCallback(null);
try {
mBinder.destroySession();
} catch (RemoteException e) {
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index ed1072cf409f..6031ef70535d 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -81,6 +81,7 @@ cc_library_shared {
"libhidlallocatorutils",
"libhidlbase",
"libsonivox",
+ "server_configurable_flags",
"android.hardware.cas@1.0",
"android.hardware.cas.native@1.0",
"android.hardware.drm@1.3",
@@ -99,6 +100,7 @@ cc_library_shared {
static_libs: [
"libgrallocusage",
"libmedia_midiiowrapper",
+ "android.media.playback.flags-aconfig-cc",
],
include_dirs: [
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 14587589372b..2a10fa7957bf 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -35,7 +35,9 @@
#include "android_media_MediaDataSource.h"
#include "android_media_Streams.h"
#include "android_util_Binder.h"
+#include <com_android_media_playback_flags.h>
+namespace playback_flags = com::android::media::playback::flags;
using namespace android;
struct fields_t {
@@ -374,9 +376,12 @@ static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
return NULL;
}
- // For getFrameAtTime family of calls, default to ANDROID_BITMAP_FORMAT_RGB_565
- // to keep the behavior consistent with older releases
- AndroidBitmapFormat colorFormat = getColorFormat(env, params, ANDROID_BITMAP_FORMAT_RGB_565);
+
+ AndroidBitmapFormat defaultColorFormat =
+ playback_flags::mediametadataretriever_default_rgba8888()
+ ? ANDROID_BITMAP_FORMAT_RGBA_8888
+ : ANDROID_BITMAP_FORMAT_RGB_565;
+ AndroidBitmapFormat colorFormat = getColorFormat(env, params, defaultColorFormat);
// Call native method to retrieve a video frame
VideoFrame *videoFrame = NULL;
diff --git a/media/jni/playback_flags.aconfig b/media/jni/playback_flags.aconfig
new file mode 100644
index 000000000000..2bb0ec5375fd
--- /dev/null
+++ b/media/jni/playback_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.media.playback.flags"
+
+flag {
+ name: "mediametadataretriever_default_rgba8888"
+ namespace: "media_solutions"
+ description: "Change MediaMetadataRetriever to use RGBA8888 for bitmap handling by default."
+ bug: "298965955"
+}
diff --git a/media/tests/MediaFrameworkTest/Android.bp b/media/tests/MediaFrameworkTest/Android.bp
index 06ec949e6736..7a329bccf940 100644
--- a/media/tests/MediaFrameworkTest/Android.bp
+++ b/media/tests/MediaFrameworkTest/Android.bp
@@ -20,7 +20,11 @@ android_test {
"androidx.test.ext.junit",
"androidx.test.rules",
"android-ex-camera2",
+ "android.media.playback.flags-aconfig-java",
+ "flag-junit",
+ "testables",
"testng",
+ "truth",
],
jni_libs: [
"libdexmakerjvmtiagent",
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index e88655894156..7d79a6c266e4 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
diff --git a/media/tests/MediaFrameworkTest/AndroidTest.xml b/media/tests/MediaFrameworkTest/AndroidTest.xml
index 132028ce98dc..91c92cc13d1b 100644
--- a/media/tests/MediaFrameworkTest/AndroidTest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidTest.xml
@@ -23,5 +23,6 @@
<option name="package" value="com.android.mediaframeworktest" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
<option name="hidden-api-checks" value="false"/>
+ <option name="isolated-storage" value="false"/>
</test>
</configuration>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
index 9be7004c5701..30edfa40802b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
@@ -44,7 +44,6 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
@Override
public TestSuite getAllTests() {
TestSuite suite = new InstrumentationTestSuite(this);
- addMediaMetadataRetrieverStateUnitTests(suite);
addMediaRecorderStateUnitTests(suite);
addMediaPlayerStateUnitTests(suite);
addMediaScannerUnitTests(suite);
@@ -70,11 +69,6 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
}
// Running all unit tests checking the state machine may be time-consuming.
- private void addMediaMetadataRetrieverStateUnitTests(TestSuite suite) {
- suite.addTestSuite(MediaMetadataRetrieverTest.class);
- }
-
- // Running all unit tests checking the state machine may be time-consuming.
private void addMediaRecorderStateUnitTests(TestSuite suite) {
suite.addTestSuite(MediaRecorderPrepareStateUnitTest.class);
suite.addTestSuite(MediaRecorderResetStateUnitTest.class);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
index bdca4744ff74..e3d389737bb0 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaMetadataRetrieverTest.java
@@ -16,26 +16,45 @@
package com.android.mediaframeworktest.unit;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
-import android.test.AndroidTestCase;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.media.playback.flags.Flags;
import com.android.mediaframeworktest.MediaNames;
import com.android.mediaframeworktest.MediaProfileReader;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.FileOutputStream;
import java.io.IOException;
-public class MediaMetadataRetrieverTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaMetadataRetrieverTest {
private static final String TAG = "MediaMetadataRetrieverTest";
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
// Test album art extraction.
@MediumTest
- public static void testGetEmbeddedPicture() throws Exception {
+ @Test
+ public void testGetEmbeddedPicture() throws Exception {
Log.v(TAG, "testGetEmbeddedPicture starts.");
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
boolean supportWMA = MediaProfileReader.getWMAEnable();
@@ -78,7 +97,8 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase {
// Test frame capture
@LargeTest
- public static void testThumbnailCapture() throws Exception {
+ @Test
+ public void testThumbnailCapture() throws Exception {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
boolean supportWMA = MediaProfileReader.getWMAEnable();
boolean supportWMV = MediaProfileReader.getWMVEnable();
@@ -134,7 +154,8 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase {
}
@LargeTest
- public static void testMetadataRetrieval() throws Exception {
+ @Test
+ public void testMetadataRetrieval() throws Exception {
boolean supportWMA = MediaProfileReader.getWMAEnable();
boolean supportWMV = MediaProfileReader.getWMVEnable();
boolean hasFailed = false;
@@ -169,7 +190,8 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase {
// If the specified call order and valid media file is used, no exception
// should be thrown.
@MediumTest
- public static void testBasicNormalMethodCallSequence() throws Exception {
+ @Test
+ public void testBasicNormalMethodCallSequence() throws Exception {
boolean hasFailed = false;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
@@ -197,7 +219,8 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase {
// If setDataSource() has not been called, both getFrameAtTime() and extractMetadata() must
// return null.
@MediumTest
- public static void testBasicAbnormalMethodCallSequence() {
+ @Test
+ public void testBasicAbnormalMethodCallSequence() {
boolean hasFailed = false;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
if (retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM) != null) {
@@ -213,7 +236,8 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase {
// Test setDataSource()
@MediumTest
- public static void testSetDataSource() throws IOException {
+ @Test
+ public void testSetDataSource() throws IOException {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
boolean hasFailed = false;
@@ -271,6 +295,34 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase {
assertTrue(!hasFailed);
}
+ /** Test the thumbnail is generated when the default is set to RGBA8888 */
+ @MediumTest
+ // TODO(b/305160754) Remove the following annotation and use SetFlagsRule.enableFlags
+ @RequiresFlagsEnabled(Flags.FLAG_MEDIAMETADATARETRIEVER_DEFAULT_RGBA8888)
+ @Test
+ public void testGetFrameAtTimeWithRGBA8888Flag_Set() throws IOException {
+ try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+ retriever.setDataSource(MediaNames.TEST_PATH_1);
+ Bitmap bitmap = retriever.getFrameAtTime(-1);
+ assertNotNull(bitmap);
+ assertEquals(Bitmap.Config.ARGB_8888, bitmap.getConfig());
+ }
+ }
+
+ /** Test the thumbnail is generated when the default is not set to RGBA8888 */
+ @MediumTest
+ // TODO(b/305160754) Remove the following annotation and use SetFlagsRule.disableFlags
+ @RequiresFlagsDisabled(Flags.FLAG_MEDIAMETADATARETRIEVER_DEFAULT_RGBA8888)
+ @Test
+ public void testGetFrameAtTimeWithRGBA8888Flag_Unset() throws IOException {
+ try (MediaMetadataRetriever retriever = new MediaMetadataRetriever()) {
+ retriever.setDataSource(MediaNames.TEST_PATH_1);
+ Bitmap bitmap = retriever.getFrameAtTime(-1);
+ assertNotNull(bitmap);
+ assertEquals(Bitmap.Config.RGB_565, bitmap.getConfig());
+ }
+ }
+
// TODO:
// Encode and test for the correct mix of metadata elements on a per-file basis?
// We should be able to compare the actual returned metadata with the expected metadata
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index fe3132e3d2a3..ceab164aa324 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -21,22 +21,20 @@
#include <android/font.h>
#include <android/font_matcher.h>
#include <android/system_fonts.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
#include <errno.h>
#include <fcntl.h>
-#include <libxml/tree.h>
-#include <log/log.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
#include <hwui/MinikinSkia.h>
+#include <libxml/parser.h>
+#include <log/log.h>
#include <minikin/FontCollection.h>
#include <minikin/LocaleList.h>
#include <minikin/SystemFonts.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
struct XmlCharDeleter {
void operator()(xmlChar* b) { xmlFree(b); }
diff --git a/nfc-extras/OWNERS b/nfc-extras/OWNERS
new file mode 100644
index 000000000000..35e9713f5715
--- /dev/null
+++ b/nfc-extras/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 48448
+include platform/packages/apps/Nfc:/OWNERS
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
index ffed80479662..fe8386e523b2 100644
--- a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
@@ -16,6 +16,8 @@
package com.android.nfc_extras;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.HashMap;
import android.content.Context;
@@ -30,6 +32,8 @@ import android.util.Log;
*
* There is a 1-1 relationship between an {@link NfcAdapterExtras} object and
* a {@link NfcAdapter} object.
+ *
+ * TODO(b/303286040): Deprecate this API surface since this is no longer supported (see ag/443092)
*/
public final class NfcAdapterExtras {
private static final String TAG = "NfcAdapterExtras";
@@ -72,15 +76,40 @@ public final class NfcAdapterExtras {
private final NfcAdapter mAdapter;
final String mPackageName;
+ private static INfcAdapterExtras
+ getNfcAdapterExtrasInterfaceFromNfcAdapter(NfcAdapter adapter) {
+ try {
+ Method method = NfcAdapter.class.getDeclaredMethod("getNfcAdapterExtrasInterface");
+ method.setAccessible(true);
+ return (INfcAdapterExtras) method.invoke(adapter);
+ } catch (SecurityException | NoSuchMethodException | IllegalArgumentException
+ | IllegalAccessException | IllegalAccessError | InvocationTargetException e) {
+ Log.e(TAG, "Unable to get context from NfcAdapter");
+ }
+ return null;
+ }
+
/** get service handles */
private static void initService(NfcAdapter adapter) {
- final INfcAdapterExtras service = adapter.getNfcAdapterExtrasInterface();
+ final INfcAdapterExtras service = getNfcAdapterExtrasInterfaceFromNfcAdapter(adapter);
if (service != null) {
// Leave stale rather than receive a null value.
sService = service;
}
}
+ private static Context getContextFromNfcAdapter(NfcAdapter adapter) {
+ try {
+ Method method = NfcAdapter.class.getDeclaredMethod("getContext");
+ method.setAccessible(true);
+ return (Context) method.invoke(adapter);
+ } catch (SecurityException | NoSuchMethodException | IllegalArgumentException
+ | IllegalAccessException | IllegalAccessError | InvocationTargetException e) {
+ Log.e(TAG, "Unable to get context from NfcAdapter");
+ }
+ return null;
+ }
+
/**
* Get the {@link NfcAdapterExtras} for the given {@link NfcAdapter}.
*
@@ -91,7 +120,7 @@ public final class NfcAdapterExtras {
* @return the {@link NfcAdapterExtras} object for the given {@link NfcAdapter}
*/
public static NfcAdapterExtras get(NfcAdapter adapter) {
- Context context = adapter.getContext();
+ Context context = getContextFromNfcAdapter(adapter);
if (context == null) {
throw new UnsupportedOperationException(
"You must pass a context to your NfcAdapter to use the NFC extras APIs");
@@ -112,7 +141,7 @@ public final class NfcAdapterExtras {
private NfcAdapterExtras(NfcAdapter adapter) {
mAdapter = adapter;
- mPackageName = adapter.getContext().getPackageName();
+ mPackageName = getContextFromNfcAdapter(adapter).getPackageName();
mEmbeddedEe = new NfcExecutionEnvironment(this);
mRouteOnWhenScreenOn = new CardEmulationRoute(CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON,
mEmbeddedEe);
@@ -156,12 +185,24 @@ public final class NfcAdapterExtras {
}
}
+ private static void attemptDeadServiceRecoveryOnNfcAdapter(NfcAdapter adapter, Exception e) {
+ try {
+ Method method = NfcAdapter.class.getDeclaredMethod(
+ "attemptDeadServiceRecovery", Exception.class);
+ method.setAccessible(true);
+ method.invoke(adapter, e);
+ } catch (SecurityException | NoSuchMethodException | IllegalArgumentException
+ | IllegalAccessException | IllegalAccessError | InvocationTargetException ex) {
+ Log.e(TAG, "Unable to attempt dead service recovery on NfcAdapter");
+ }
+ }
+
/**
* NFC service dead - attempt best effort recovery
*/
void attemptDeadServiceRecovery(Exception e) {
Log.e(TAG, "NFC Adapter Extras dead - attempting to recover");
- mAdapter.attemptDeadServiceRecovery(e);
+ attemptDeadServiceRecoveryOnNfcAdapter(mAdapter, e);
initService(mAdapter);
}
diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp
index 58bcd1d49c69..e71597a27a39 100644
--- a/omapi/aidl/Android.bp
+++ b/omapi/aidl/Android.bp
@@ -24,6 +24,11 @@ aidl_interface {
backend: {
java: {
sdk_version: "module_current",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.nfcservices",
+ ],
+
},
rust: {
enabled: true,
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
index 3505cfb9d38a..74f04e093162 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -33,8 +33,6 @@ import android.view.View;
import androidx.annotation.Nullable;
-import java.io.File;
-
/**
* Installation failed: Return status code to the caller or display failure UI to user
*/
@@ -101,14 +99,8 @@ public class InstallFailed extends AlertActivity {
// Set header icon and title
PackageUtil.AppSnippet as;
PackageManager pm = getPackageManager();
-
- if ("package".equals(packageURI.getScheme())) {
- as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
- pm.getApplicationIcon(appInfo));
- } else {
- final File sourceFile = new File(packageURI.getPath());
- as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
- }
+ as = intent.getParcelableExtra(PackageInstallerActivity.EXTRA_APP_SNIPPET,
+ PackageUtil.AppSnippet.class);
// Store label for dialog
mLabel = as.label;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 7bea33971259..1088acef0fb0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -16,6 +16,7 @@
package com.android.packageinstaller;
+import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_APP_SNIPPET;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
import android.app.PendingIntent;
@@ -86,7 +87,8 @@ public class InstallInstalling extends AlertActivity {
// ContentResolver.SCHEME_FILE
// STAGED_SESSION_ID extra contains an ID of a previously staged install session.
final File sourceFile = new File(mPackageURI.getPath());
- PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
+ PackageUtil.AppSnippet as = getIntent()
+ .getParcelableExtra(EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class);
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index 73c03a57cade..579fb7b7de06 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -23,7 +23,6 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@@ -31,7 +30,6 @@ import android.widget.Button;
import androidx.annotation.Nullable;
-import java.io.File;
import java.util.List;
/**
@@ -66,18 +64,8 @@ public class InstallSuccess extends AlertActivity {
ApplicationInfo appInfo =
intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mAppPackageName = appInfo.packageName;
- Uri packageURI = intent.getData();
-
- // Set header icon and title
- PackageManager pm = getPackageManager();
-
- if ("package".equals(packageURI.getScheme())) {
- mAppSnippet = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
- pm.getApplicationIcon(appInfo));
- } else {
- File sourceFile = new File(packageURI.getPath());
- mAppSnippet = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
- }
+ mAppSnippet = intent.getParcelableExtra(PackageInstallerActivity.EXTRA_APP_SNIPPET,
+ PackageUtil.AppSnippet.class);
mLaunchIntent = getPackageManager().getLaunchIntentForPackage(mAppPackageName);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 38d5d3a15914..1e201689ed6a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -81,6 +81,7 @@ public class PackageInstallerActivity extends AlertActivity {
static final String EXTRA_CALLING_ATTRIBUTION_TAG = "EXTRA_CALLING_ATTRIBUTION_TAG";
static final String EXTRA_ORIGINAL_SOURCE_INFO = "EXTRA_ORIGINAL_SOURCE_INFO";
static final String EXTRA_STAGED_SESSION_ID = "EXTRA_STAGED_SESSION_ID";
+ static final String EXTRA_APP_SNIPPET = "EXTRA_APP_SNIPPET";
private static final String ALLOW_UNKNOWN_SOURCES_KEY =
PackageInstallerActivity.class.getName() + "ALLOW_UNKNOWN_SOURCES_KEY";
@@ -595,7 +596,7 @@ public class PackageInstallerActivity extends AlertActivity {
CharSequence label = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
if (mLocalLOGV) Log.i(TAG, "creating snippet for " + label);
mAppSnippet = new PackageUtil.AppSnippet(label,
- mPm.getApplicationIcon(mPkgInfo.applicationInfo));
+ mPm.getApplicationIcon(mPkgInfo.applicationInfo), getBaseContext());
} break;
case ContentResolver.SCHEME_FILE: {
@@ -633,7 +634,7 @@ public class PackageInstallerActivity extends AlertActivity {
mPkgInfo = generateStubPackageInfo(info.getAppPackageName());
mAppSnippet = new PackageUtil.AppSnippet(info.getAppLabel(),
info.getAppIcon() != null ? new BitmapDrawable(getResources(), info.getAppIcon())
- : getPackageManager().getDefaultActivityIcon());
+ : getPackageManager().getDefaultActivityIcon(), getBaseContext());
return true;
}
@@ -693,6 +694,9 @@ public class PackageInstallerActivity extends AlertActivity {
if (stagedSessionId > 0) {
newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId);
}
+ if (mAppSnippet != null) {
+ newIntent.putExtra(EXTRA_APP_SNIPPET, mAppSnippet);
+ }
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
startActivity(newIntent);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index ff0e5fb296b1..f6f7acc0e77c 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -18,6 +18,7 @@
package com.android.packageinstaller;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -27,8 +28,13 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
@@ -115,18 +121,75 @@ public class PackageUtil {
icon);
}
- static final class AppSnippet {
+ static final class AppSnippet implements Parcelable {
@NonNull public CharSequence label;
@Nullable public Drawable icon;
- public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon) {
+ public int iconSize;
+
+ public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon, Context context) {
this.label = label;
this.icon = icon;
+ final ActivityManager am = context.getSystemService(ActivityManager.class);
+ this.iconSize = am.getLauncherLargeIconSize();
+ }
+
+ private AppSnippet(Parcel in) {
+ label = in.readString();
+ Bitmap bmp = in.readParcelable(getClass().getClassLoader(), Bitmap.class);
+ icon = new BitmapDrawable(Resources.getSystem(), bmp);
+ iconSize = in.readInt();
}
@Override
public String toString() {
return "AppSnippet[" + label + (icon != null ? "(has" : "(no ") + " icon)]";
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(label.toString());
+ Bitmap bmp = getBitmapFromDrawable(icon);
+ dest.writeParcelable(bmp, 0);
+ dest.writeInt(iconSize);
+ }
+
+ private Bitmap getBitmapFromDrawable(Drawable drawable) {
+ // Create an empty bitmap with the dimensions of our drawable
+ final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+ drawable.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ // Associate it with a canvas. This canvas will draw the icon on the bitmap
+ final Canvas canvas = new Canvas(bmp);
+ // Draw the drawable in the canvas. The canvas will ultimately paint the drawable in the
+ // bitmap held within
+ drawable.draw(canvas);
+
+ // Scale it down if the icon is too large
+ if ((bmp.getWidth() > iconSize * 2) || (bmp.getHeight() > iconSize * 2)) {
+ Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp, iconSize, iconSize, true);
+ if (scaledBitmap != bmp) {
+ bmp.recycle();
+ }
+ return scaledBitmap;
+ }
+
+ return bmp;
+ }
+
+ public static final Parcelable.Creator<AppSnippet> CREATOR = new Parcelable.Creator<>() {
+ public AppSnippet createFromParcel(Parcel in) {
+ return new AppSnippet(in);
+ }
+
+ public AppSnippet[] newArray(int size) {
+ return new AppSnippet[size];
+ }
+ };
}
/**
@@ -171,7 +234,7 @@ public class PackageUtil {
} catch (OutOfMemoryError e) {
Log.i(LOG_TAG, "Could not load app icon", e);
}
- return new PackageUtil.AppSnippet(label, icon);
+ return new PackageUtil.AppSnippet(label, icon, pContext);
}
/**
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 873b434aa4fd..618f7edc433a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -590,6 +590,7 @@ public class SettingsBackupTest {
Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS,
Settings.Global.AUTO_REVOKE_PARAMETERS,
Settings.Global.ENABLE_RADIO_BUG_DETECTION,
+ Settings.Global.REPAIR_MODE_ACTIVE,
Settings.Global.RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD,
Settings.Global.RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD,
Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT,
@@ -711,6 +712,7 @@ public class SettingsBackupTest {
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY,
+ Settings.Secure.AUDIO_DEVICE_INVENTORY, // setting not controllable by user
Settings.Secure.BACKUP_AUTO_RESTORE,
Settings.Secure.BACKUP_ENABLED,
Settings.Secure.BACKUP_PROVISIONED,
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 8f329b3dddd8..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
@@ -36,6 +38,7 @@ gwasserman@google.com
hwwang@google.com
hyunyoungs@google.com
ikateryna@google.com
+iyz@google.com
jaggies@google.com
jamesoleary@google.com
jbolinger@google.com
@@ -54,10 +57,12 @@ justinweir@google.com
kozynski@google.com
kprevas@google.com
lusilva@google.com
+liuyining@google.com
lynhan@google.com
madym@google.com
mankoff@google.com
mateuszc@google.com
+matiashe@google.com
mgalhardo@google.com
michaelmikhil@google.com
michschn@google.com
@@ -77,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
@@ -94,10 +100,12 @@ tracyzhou@google.com
tsuji@google.com
twickham@google.com
vadimt@google.com
+valiiftime@google.com
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/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index d9fe949bb327..b9ee63a5da6e 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -62,7 +62,8 @@
<com.android.systemui.statusbar.LightRevealScrim
android:id="@+id/light_reveal_scrim"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ sysui:ignoreRightInset="true" />
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS
new file mode 100644
index 000000000000..7f5384d1dbf3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include /packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS \ No newline at end of file
diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java
index 1866a9983495..67c2caa338a6 100644
--- a/rs/java/android/renderscript/ScriptC.java
+++ b/rs/java/android/renderscript/ScriptC.java
@@ -16,9 +16,12 @@
package android.renderscript;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.res.Resources;
+import android.util.Slog;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -35,6 +38,15 @@ public class ScriptC extends Script {
private static final String TAG = "ScriptC";
/**
+ * In targetSdkVersion 35 and above, Renderscript's ScriptC stops being supported
+ * and an exception is thrown when the class is instantiated.
+ * In targetSdkVersion 34 and below, Renderscript's ScriptC still works.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = 35)
+ private static final long RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID = 297019750L;
+
+ /**
* Only intended for use by the generated derived classes.
*
* @param id
@@ -89,7 +101,19 @@ public class ScriptC extends Script {
setID(id);
}
+ private static void throwExceptionIfSDKTooHigh() {
+ String message =
+ "ScriptC scripts are not supported when targeting an API Level >= 35. Please refer "
+ + "to https://developer.android.com/guide/topics/renderscript/migration-guide "
+ + "for proposed alternatives.";
+ Slog.w(TAG, message);
+ if (CompatChanges.isChangeEnabled(RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID)) {
+ throw new UnsupportedOperationException(message);
+ }
+ }
+
private static synchronized long internalCreate(RenderScript rs, Resources resources, int resourceID) {
+ throwExceptionIfSDKTooHigh();
byte[] pgm;
int pgmLength;
InputStream is = resources.openRawResource(resourceID);
@@ -126,6 +150,7 @@ public class ScriptC extends Script {
private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) {
// Log.v(TAG, "Create script for resource = " + resName);
+ throwExceptionIfSDKTooHigh();
return rs.nScriptCCreate(resName, RenderScript.getCachePath(), bitcode, bitcode.length);
}
}
diff --git a/services/Android.bp b/services/Android.bp
index 66181f5ed45c..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
@@ -235,12 +236,14 @@ filegroup {
stubs_defaults {
name: "services-stubs-default",
installable: false,
- args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" +
- " --hide-annotation android.annotation.Hide" +
- " --hide InternalClasses" + // com.android.* classes are okay in this interface
+ flags: [
+ "--show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)",
+ "--hide-annotation android.annotation.Hide",
+ "--hide InternalClasses", // com.android.* classes are okay in this interface
// TODO: remove the --hide options below
- " --hide DeprecationMismatch" +
- " --hide HiddenTypedefConstant",
+ "--hide DeprecationMismatch",
+ "--hide HiddenTypedefConstant",
+ ],
visibility: ["//frameworks/base:__subpackages__"],
filter_packages: ["com.android."],
}
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/Android.bp b/services/core/Android.bp
index 9ac30f334d0b..22f85703f428 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -130,6 +130,7 @@ java_library_static {
"android.hardware.light-V2.0-java",
"android.hardware.gnss-V2-java",
"android.hardware.vibrator-V2-java",
+ "android.nfc.flags-aconfig-java",
"app-compat-annotations",
"framework-tethering.stubs.module_lib",
"service-art.stubs.system_server",
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index b67e62703067..e92f043457f5 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -179,6 +179,8 @@ option java_package com.android.server
3130 pm_snapshot_stats (build_count|1|1),(reuse_count|1|1),(big_builds|1|1),(short_lived|1|1),(max_build_time|1|3),(cumm_build_time|2|3)
# Snapshot rebuild instance
3131 pm_snapshot_rebuild (build_time|1|3),(lifetime|1|3)
+# Caller information to clear application data
+3132 pm_clear_app_data_caller (pid|1),(uid|1),(package|3)
# ---------------------------
# InputMethodManagerService.java
@@ -218,6 +220,14 @@ 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/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 71428284882d..61584b727aa7 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -377,15 +377,14 @@ class StorageManagerService extends IStorageManager.Stub
private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_STORAGE);
/**
- * mLocalUnlockedUsers affects the return value of isUserUnlocked. If
- * any value in the array changes, then the binder cache for
- * isUserUnlocked must be invalidated. When adding mutating methods to
- * WatchedLockedUsers, be sure to invalidate the cache in the new
- * methods.
+ * mCeUnlockedUsers affects the return value of {@link UserManager#isUserUnlocked}. If any
+ * value in the array changes, then the binder cache for {@link UserManager#isUserUnlocked} must
+ * be invalidated. When adding mutating methods to this class, be sure to invalidate the cache
+ * in the new methods.
*/
- private static class WatchedLockedUsers {
+ private static class WatchedUnlockedUsers {
private int[] users = EmptyArray.INT;
- public WatchedLockedUsers() {
+ public WatchedUnlockedUsers() {
invalidateIsUserUnlockedCache();
}
public void append(int userId) {
@@ -417,10 +416,14 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- /** Set of users that we know are unlocked. */
+ /** Set of users whose CE storage is unlocked. */
@GuardedBy("mLock")
- private WatchedLockedUsers mLocalUnlockedUsers = new WatchedLockedUsers();
- /** Set of users that system knows are unlocked. */
+ private WatchedUnlockedUsers mCeUnlockedUsers = new WatchedUnlockedUsers();
+
+ /**
+ * Set of users that are in the RUNNING_UNLOCKED state. This differs from {@link
+ * mCeUnlockedUsers} in that a user can be stopped but still have its CE storage unlocked.
+ */
@GuardedBy("mLock")
private int[] mSystemUnlockedUsers = EmptyArray.INT;
@@ -1135,11 +1138,10 @@ class StorageManagerService extends IStorageManager.Stub
}
}
- // If vold knows that some users have their storage unlocked already (which
- // can happen after a "userspace reboot"), then add those users to
- // mLocalUnlockedUsers. Do this right away and don't wait until
- // PHASE_BOOT_COMPLETED, since the system may unlock users before then.
- private void restoreLocalUnlockedUsers() {
+ // If vold knows that some users have their CE storage unlocked already (which can happen after
+ // a "userspace reboot"), then add those users to mCeUnlockedUsers. Do this right away and
+ // don't wait until PHASE_BOOT_COMPLETED, since the system may unlock users before then.
+ private void restoreCeUnlockedUsers() {
final int[] userIds;
try {
userIds = mVold.getUnlockedUsers();
@@ -1155,7 +1157,7 @@ class StorageManagerService extends IStorageManager.Stub
// reconnecting to vold after it crashed and was restarted, in
// which case things will be the other way around --- we'll know
// about the unlocked users but vold won't.
- mLocalUnlockedUsers.appendAll(userIds);
+ mCeUnlockedUsers.appendAll(userIds);
}
}
}
@@ -2062,7 +2064,7 @@ class StorageManagerService extends IStorageManager.Stub
connectVold();
}, DateUtils.SECOND_IN_MILLIS);
} else {
- restoreLocalUnlockedUsers();
+ restoreCeUnlockedUsers();
onDaemonConnected();
}
}
@@ -2982,7 +2984,7 @@ class StorageManagerService extends IStorageManager.Stub
// We need all the users unlocked to move their primary storage
users = mContext.getSystemService(UserManager.class).getUsers();
for (UserInfo user : users) {
- if (StorageManager.isFileEncrypted() && !isUserKeyUnlocked(user.id)) {
+ if (StorageManager.isFileEncrypted() && !isCeStorageUnlocked(user.id)) {
Slog.w(TAG, "Failing move due to locked user " + user.id);
onMoveStatusLocked(PackageManager.MOVE_FAILED_LOCKED_USER);
return;
@@ -3206,15 +3208,15 @@ class StorageManagerService extends IStorageManager.Stub
@android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
@Override
- public void createUserKey(int userId, int serialNumber, boolean ephemeral) {
+ public void createUserStorageKeys(int userId, int serialNumber, boolean ephemeral) {
- super.createUserKey_enforcePermission();
+ super.createUserStorageKeys_enforcePermission();
try {
- mVold.createUserKey(userId, serialNumber, ephemeral);
- // New keys are always unlocked.
+ mVold.createUserStorageKeys(userId, serialNumber, ephemeral);
+ // Since the user's CE key was just created, the user's CE storage is now unlocked.
synchronized (mLock) {
- mLocalUnlockedUsers.append(userId);
+ mCeUnlockedUsers.append(userId);
}
} catch (Exception e) {
Slog.wtf(TAG, e);
@@ -3223,15 +3225,15 @@ class StorageManagerService extends IStorageManager.Stub
@android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
@Override
- public void destroyUserKey(int userId) {
+ public void destroyUserStorageKeys(int userId) {
- super.destroyUserKey_enforcePermission();
+ super.destroyUserStorageKeys_enforcePermission();
try {
- mVold.destroyUserKey(userId);
- // Destroying a key also locks it.
+ mVold.destroyUserStorageKeys(userId);
+ // Since the user's CE key was just destroyed, the user's CE storage is now locked.
synchronized (mLock) {
- mLocalUnlockedUsers.remove(userId);
+ mCeUnlockedUsers.remove(userId);
}
} catch (Exception e) {
Slog.wtf(TAG, e);
@@ -3241,60 +3243,60 @@ class StorageManagerService extends IStorageManager.Stub
/* Only for use by LockSettingsService */
@android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
@Override
- public void setUserKeyProtection(@UserIdInt int userId, byte[] secret) throws RemoteException {
- super.setUserKeyProtection_enforcePermission();
+ public void setCeStorageProtection(@UserIdInt int userId, byte[] secret)
+ throws RemoteException {
+ super.setCeStorageProtection_enforcePermission();
- mVold.setUserKeyProtection(userId, HexDump.toHexString(secret));
+ mVold.setCeStorageProtection(userId, HexDump.toHexString(secret));
}
/* Only for use by LockSettingsService */
@android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
@Override
- public void unlockUserKey(@UserIdInt int userId, int serialNumber, byte[] secret)
- throws RemoteException {
- super.unlockUserKey_enforcePermission();
+ public void unlockCeStorage(@UserIdInt int userId, int serialNumber, byte[] secret)
+ throws RemoteException {
+ super.unlockCeStorage_enforcePermission();
if (StorageManager.isFileEncrypted()) {
- mVold.unlockUserKey(userId, serialNumber, HexDump.toHexString(secret));
+ mVold.unlockCeStorage(userId, serialNumber, HexDump.toHexString(secret));
}
synchronized (mLock) {
- mLocalUnlockedUsers.append(userId);
+ mCeUnlockedUsers.append(userId);
}
}
@android.annotation.EnforcePermission(android.Manifest.permission.STORAGE_INTERNAL)
@Override
- public void lockUserKey(int userId) {
- // Do not lock user 0 data for headless system user
- super.lockUserKey_enforcePermission();
+ public void lockCeStorage(int userId) {
+ super.lockCeStorage_enforcePermission();
+ // Never lock the CE storage of a headless system user.
if (userId == UserHandle.USER_SYSTEM
&& UserManager.isHeadlessSystemUserMode()) {
throw new IllegalArgumentException("Headless system user data cannot be locked..");
}
-
- if (!isUserKeyUnlocked(userId)) {
+ if (!isCeStorageUnlocked(userId)) {
Slog.d(TAG, "User " + userId + "'s CE storage is already locked");
return;
}
try {
- mVold.lockUserKey(userId);
+ mVold.lockCeStorage(userId);
} catch (Exception e) {
Slog.wtf(TAG, e);
return;
}
synchronized (mLock) {
- mLocalUnlockedUsers.remove(userId);
+ mCeUnlockedUsers.remove(userId);
}
}
@Override
- public boolean isUserKeyUnlocked(int userId) {
+ public boolean isCeStorageUnlocked(int userId) {
synchronized (mLock) {
- return mLocalUnlockedUsers.contains(userId);
+ return mCeUnlockedUsers.contains(userId);
}
}
@@ -3679,8 +3681,8 @@ class StorageManagerService extends IStorageManager.Stub
final int userId = UserHandle.getUserId(callingUid);
final String propertyName = "sys.user." + userId + ".ce_available";
- // Ignore requests to create directories while storage is locked
- if (!isUserKeyUnlocked(userId)) {
+ // Ignore requests to create directories while CE storage is locked
+ if (!isCeStorageUnlocked(userId)) {
throw new IllegalStateException("Failed to prepare " + appPath);
}
@@ -3806,15 +3808,15 @@ class StorageManagerService extends IStorageManager.Stub
final boolean systemUserUnlocked = isSystemUnlocked(UserHandle.USER_SYSTEM);
final boolean userIsDemo;
- final boolean userKeyUnlocked;
final boolean storagePermission;
+ final boolean ceStorageUnlocked;
final long token = Binder.clearCallingIdentity();
try {
userIsDemo = LocalServices.getService(UserManagerInternal.class)
.getUserInfo(userId).isDemo();
storagePermission = mStorageManagerInternal.hasExternalStorage(callingUid,
callingPackage);
- userKeyUnlocked = isUserKeyUnlocked(userId);
+ ceStorageUnlocked = isCeStorageUnlocked(userId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3874,7 +3876,7 @@ class StorageManagerService extends IStorageManager.Stub
} else if (!systemUserUnlocked) {
reportUnmounted = true;
Slog.w(TAG, "Reporting " + volId + " unmounted due to system locked");
- } else if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !userKeyUnlocked) {
+ } else if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !ceStorageUnlocked) {
reportUnmounted = true;
Slog.w(TAG, "Reporting " + volId + "unmounted due to " + userId + " locked");
} else if (!storagePermission && !realState) {
@@ -4678,7 +4680,7 @@ class StorageManagerService extends IStorageManager.Stub
}
pw.println();
- pw.println("Local unlocked users: " + mLocalUnlockedUsers);
+ pw.println("CE unlocked users: " + mCeUnlockedUsers);
pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e6df1d91861a..c0da30d0e153 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2249,7 +2249,7 @@ public class ActivityManagerService extends IActivityManager.Stub
return;
}
// TODO(b/148767783): should we check all profiles under user0?
- UserspaceRebootLogger.logEventAsync(StorageManager.isUserKeyUnlocked(userId),
+ UserspaceRebootLogger.logEventAsync(StorageManager.isCeStorageUnlocked(userId),
BackgroundThread.getExecutor());
}
@@ -3468,6 +3468,7 @@ public class ActivityManagerService extends IActivityManager.Stub
enforceNotIsolatedCaller("clearApplicationUserData");
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
+ EventLog.writeEvent(EventLogTags.AM_CLEAR_APP_DATA_CALLER, pid, uid, packageName);
final int resolvedUserId = mUserController.handleIncomingUser(pid, uid, userId, false,
ALLOW_FULL_ONLY, "clearApplicationUserData", null);
@@ -4564,7 +4565,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// We carefully use the same state that PackageManager uses for
// filtering, since we use this flag to decide if we need to install
// providers when user is unlocked later
- app.setUnlocked(StorageManager.isUserKeyUnlocked(app.userId));
+ app.setUnlocked(StorageManager.isCeStorageUnlocked(app.userId));
}
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 2e45da3c8d42..d56448d43e43 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1174,7 +1174,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
synchronized (mInternal.mOomAdjuster.mCachedAppOptimizer.mFreezerLock) {
app.mOptRecord.setFreezeSticky(isSticky);
mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppInternalLSP(app, 0,
- false);
+ true);
}
}
}
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 9e9db6aff699..2aed8476d031 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -129,3 +129,6 @@ option java_package com.android.server.am
# Intent Sender redirect for UserHandle.USER_CURRENT
30110 am_intent_sender_redirect_user (userId|1|5)
+
+# Caller information to clear application data
+30120 am_clear_app_data_caller (pid|1),(uid|1),(package|3)
diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java
index 4380b42ee54c..507fd9efaffd 100644
--- a/services/core/java/com/android/server/am/LmkdStatsReporter.java
+++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java
@@ -44,6 +44,7 @@ public final class LmkdStatsReporter {
private static final int DIRECT_RECL_AND_THRASHING = 5;
private static final int LOW_MEM_AND_SWAP_UTIL = 6;
private static final int LOW_FILECACHE_AFTER_THRASHING = 7;
+ private static final int LOW_MEM = 8;
/**
* Processes the LMK_KILL_OCCURRED packet data
@@ -106,6 +107,8 @@ public final class LmkdStatsReporter {
return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP_UTIL;
case LOW_FILECACHE_AFTER_THRASHING:
return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_FILECACHE_AFTER_THRASHING;
+ case LOW_MEM:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM;
default:
return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__UNKNOWN;
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index f21ad2204d74..579d35ccc9d0 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2712,8 +2712,7 @@ public class OomAdjuster {
}
}
- if (ppr.getLastProviderTime() > 0
- && (ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
+ if ((ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
if (adj > PREVIOUS_APP_ADJ) {
adj = PREVIOUS_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
diff --git a/services/core/java/com/android/server/am/ProcessProviderRecord.java b/services/core/java/com/android/server/am/ProcessProviderRecord.java
index 751e8a821ab1..9b72a3afcfd9 100644
--- a/services/core/java/com/android/server/am/ProcessProviderRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProviderRecord.java
@@ -34,7 +34,7 @@ final class ProcessProviderRecord {
/**
* The last time someone else was using a provider in this process.
*/
- private long mLastProviderTime;
+ private long mLastProviderTime = Long.MIN_VALUE;
/**
* class (String) -> ContentProviderRecord.
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index db341d253818..265ab0496f4d 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -280,7 +280,7 @@ final class ProcessStateRecord {
* The last time the process was in the TOP state or greater.
*/
@GuardedBy("mService")
- private long mLastTopTime;
+ private long mLastTopTime = Long.MIN_VALUE;
/**
* Is this an empty background process?
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 76a994ec63e9..6eb2961fcf86 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -639,8 +639,8 @@ class UserController implements Handler.Callback {
mInjector.getUserJourneyLogger()
.logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_UNLOCKING_USER,
EVENT_STATE_BEGIN);
- // If the user key hasn't been unlocked yet, we cannot proceed.
- if (!StorageManager.isUserKeyUnlocked(userId)) return false;
+ // If the user's CE storage hasn't been unlocked yet, we cannot proceed.
+ if (!StorageManager.isCeStorageUnlocked(userId)) return false;
synchronized (mLock) {
// Do not proceed if unexpected state or a stale user
if (mStartedUsers.get(userId) != uss || uss.state != STATE_RUNNING_LOCKED) {
@@ -655,8 +655,8 @@ class UserController implements Handler.Callback {
// Call onBeforeUnlockUser on a worker thread that allows disk I/O
FgThread.getHandler().post(() -> {
- if (!StorageManager.isUserKeyUnlocked(userId)) {
- Slogf.w(TAG, "User key got locked unexpectedly, leaving user locked.");
+ if (!StorageManager.isCeStorageUnlocked(userId)) {
+ Slogf.w(TAG, "User's CE storage got locked unexpectedly, leaving user locked.");
return;
}
@@ -690,8 +690,8 @@ class UserController implements Handler.Callback {
private void finishUserUnlocked(final UserState uss) {
final int userId = uss.mHandle.getIdentifier();
EventLog.writeEvent(EventLogTags.UC_FINISH_USER_UNLOCKED, userId);
- // Only keep marching forward if user is actually unlocked
- if (!StorageManager.isUserKeyUnlocked(userId)) return;
+ // Only keep marching forward if the user's CE storage is unlocked.
+ if (!StorageManager.isCeStorageUnlocked(userId)) return;
synchronized (mLock) {
// Bail if we ended up with a stale user
if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return;
@@ -777,8 +777,8 @@ class UserController implements Handler.Callback {
if (userInfo == null) {
return;
}
- // Only keep marching forward if user is actually unlocked
- if (!StorageManager.isUserKeyUnlocked(userId)) return;
+ // Only keep marching forward if the user's CE storage is unlocked.
+ if (!StorageManager.isCeStorageUnlocked(userId)) return;
// Remember that we logged in
mInjector.getUserManager().onUserLoggedIn(userId);
@@ -1256,7 +1256,7 @@ class UserController implements Handler.Callback {
}
try {
Slogf.i(TAG, "Locking CE storage for user #" + userId);
- mInjector.getStorageManager().lockUserKey(userId);
+ mInjector.getStorageManager().lockCeStorage(userId);
} catch (RemoteException re) {
throw re.rethrowAsRuntimeException();
}
@@ -1874,8 +1874,8 @@ class UserController implements Handler.Callback {
}
UserState uss;
- if (!StorageManager.isUserKeyUnlocked(userId)) {
- // We always want to try to unlock the user key, even if the user is not started yet.
+ if (!StorageManager.isCeStorageUnlocked(userId)) {
+ // We always want to try to unlock CE storage, even if the user is not started yet.
mLockPatternUtils.unlockUserKeyIfUnsecured(userId);
}
synchronized (mLock) {
@@ -2662,10 +2662,10 @@ class UserController implements Handler.Callback {
case UserState.STATE_RUNNING_UNLOCKING:
case UserState.STATE_RUNNING_UNLOCKED:
return true;
- // In the stopping/shutdown state return unlock state of the user key
+ // In the stopping/shutdown state, return unlock state of the user's CE storage.
case UserState.STATE_STOPPING:
case UserState.STATE_SHUTDOWN:
- return StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isCeStorageUnlocked(userId);
default:
return false;
}
@@ -2674,10 +2674,10 @@ class UserController implements Handler.Callback {
switch (state.state) {
case UserState.STATE_RUNNING_UNLOCKED:
return true;
- // In the stopping/shutdown state return unlock state of the user key
+ // In the stopping/shutdown state, return unlock state of the user's CE storage.
case UserState.STATE_STOPPING:
case UserState.STATE_SHUTDOWN:
- return StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isCeStorageUnlocked(userId);
default:
return false;
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index e4a5a3e0ed00..235843643a0d 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -399,59 +399,6 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
}
- public enum FrameRate {
- FPS_DEFAULT(0),
- FPS_30(30),
- FPS_36(36),
- FPS_40(40),
- FPS_45(45),
- FPS_48(48),
- FPS_60(60),
- FPS_72(72),
- FPS_90(90),
- FPS_120(120),
- FPS_144(144),
- FPS_INVALID(-1);
-
- public final int fps;
-
- FrameRate(int fps) {
- this.fps = fps;
- }
- }
-
- // Turn the raw string to the corresponding fps int.
- // Return 0 when disabling, -1 for invalid fps.
- static int getFpsInt(String raw) {
- // TODO(b/243448953): make sure this translates to proper values based on current display
- switch (raw) {
- case "30":
- return FrameRate.FPS_30.fps;
- case "36":
- return FrameRate.FPS_36.fps;
- case "40":
- return FrameRate.FPS_40.fps;
- case "45":
- return FrameRate.FPS_45.fps;
- case "48":
- return FrameRate.FPS_48.fps;
- case "60":
- return FrameRate.FPS_60.fps;
- case "72":
- return FrameRate.FPS_72.fps;
- case "90":
- return FrameRate.FPS_90.fps;
- case "120":
- return FrameRate.FPS_120.fps;
- case "144":
- return FrameRate.FPS_144.fps;
- case "disable":
- case "":
- return FrameRate.FPS_DEFAULT.fps;
- }
- return FrameRate.FPS_INVALID.fps;
- }
-
/**
* Called by games to communicate the current state to the platform.
*
@@ -699,7 +646,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
public synchronized int getFps() {
- return GameManagerService.getFpsInt(mFps);
+ try {
+ final int fpsInt = Integer.parseInt(mFps);
+ return fpsInt;
+ } catch (NumberFormatException e) {
+ return 0;
+ }
}
synchronized String getFpsStr() {
@@ -739,7 +691,12 @@ public final class GameManagerService extends IGameManagerService.Stub {
}
android.app.GameModeConfiguration toPublicGameModeConfig() {
- int fpsOverride = getFpsInt(mFps);
+ int fpsOverride;
+ try {
+ fpsOverride = Integer.parseInt(mFps);
+ } catch (NumberFormatException e) {
+ fpsOverride = 0;
+ }
// TODO(b/243448953): match to proper value in case of display change?
fpsOverride = fpsOverride > 0 ? fpsOverride
: android.app.GameModeConfiguration.FPS_OVERRIDE_NONE;
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 00ff489ee0ff..ab57c4fe837e 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -241,8 +241,10 @@ public class GameManagerShellCommand extends ShellCommand {
case "--fps":
if (fpsStr == null) {
fpsStr = getNextArgRequired();
- if (fpsStr != null && GameManagerService.getFpsInt(fpsStr) == -1) {
- pw.println("Invalid frame rate '" + fpsStr + "'");
+ try {
+ Integer.parseInt(fpsStr);
+ } catch (NumberFormatException e) {
+ pw.println("Invalid frame rate: '" + fpsStr + "'");
return -1;
}
} else {
@@ -375,8 +377,8 @@ public class GameManagerShellCommand extends ShellCommand {
pw.println(" --downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65");
pw.println(" |0.7|0.75|0.8|0.85|0.9|disable]: Set app to run at the");
pw.println(" specified scaling ratio.");
- pw.println(" --fps [30|45|60|90|120|disable]: Set app to run at the specified fps,");
- pw.println(" if supported.");
+ pw.println(" --fps: Integer value to set app to run at the specified fps,");
+ pw.println(" if supported. 0 to disable.");
pw.println(" reset [--mode [2|3|performance|battery] --user <USER_ID>] <PACKAGE_NAME>");
pw.println(" Resets the game mode of the app to device configuration.");
pw.println(" This should only be used to reset any override to non custom game mode");
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
new file mode 100644
index 000000000000..a466235d450b
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AdiDeviceState.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import static android.media.AudioSystem.DEVICE_NONE;
+import static android.media.AudioSystem.isBluetoothDevice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Class representing all devices that were previously or are currently connected. Data is
+ * persisted in {@link android.provider.Settings.Secure}
+ */
+/*package*/ final class AdiDeviceState {
+ private static final String TAG = "AS.AdiDeviceState";
+
+ private static final String SETTING_FIELD_SEPARATOR = ",";
+
+ @AudioDeviceInfo.AudioDeviceType
+ private final int mDeviceType;
+
+ private final int mInternalDeviceType;
+ @NonNull
+ private final String mDeviceAddress;
+ private boolean mSAEnabled;
+ private boolean mHasHeadTracker = false;
+ private boolean mHeadTrackerEnabled;
+
+ /**
+ * Constructor
+ *
+ * @param deviceType external audio device type
+ * @param internalDeviceType if not set pass {@link DEVICE_NONE}, in this case the
+ * default conversion of the external type will be used
+ * @param address must be non-null for wireless devices
+ * @throws NullPointerException if a null address is passed for a wireless device
+ */
+ AdiDeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType,
+ int internalDeviceType,
+ @Nullable String address) {
+ mDeviceType = deviceType;
+ if (internalDeviceType != DEVICE_NONE) {
+ mInternalDeviceType = internalDeviceType;
+ } else {
+ mInternalDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType);
+
+ }
+ mDeviceAddress = isBluetoothDevice(mInternalDeviceType) ? Objects.requireNonNull(
+ address) : "";
+ }
+
+ @AudioDeviceInfo.AudioDeviceType
+ public int getDeviceType() {
+ return mDeviceType;
+ }
+
+ public int getInternalDeviceType() {
+ return mInternalDeviceType;
+ }
+
+ @NonNull
+ public String getDeviceAddress() {
+ return mDeviceAddress;
+ }
+
+ public void setSAEnabled(boolean sAEnabled) {
+ mSAEnabled = sAEnabled;
+ }
+
+ public boolean isSAEnabled() {
+ return mSAEnabled;
+ }
+
+ public void setHeadTrackerEnabled(boolean headTrackerEnabled) {
+ mHeadTrackerEnabled = headTrackerEnabled;
+ }
+
+ public boolean isHeadTrackerEnabled() {
+ return mHeadTrackerEnabled;
+ }
+
+ public void setHasHeadTracker(boolean hasHeadTracker) {
+ mHasHeadTracker = hasHeadTracker;
+ }
+
+
+ public boolean hasHeadTracker() {
+ return mHasHeadTracker;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ // type check and cast
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final AdiDeviceState sads = (AdiDeviceState) obj;
+ return mDeviceType == sads.mDeviceType
+ && mInternalDeviceType == sads.mInternalDeviceType
+ && mDeviceAddress.equals(sads.mDeviceAddress) // NonNull
+ && mSAEnabled == sads.mSAEnabled
+ && mHasHeadTracker == sads.mHasHeadTracker
+ && mHeadTrackerEnabled == sads.mHeadTrackerEnabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDeviceType, mInternalDeviceType, mDeviceAddress, mSAEnabled,
+ mHasHeadTracker, mHeadTrackerEnabled);
+ }
+
+ @Override
+ public String toString() {
+ return "type: " + mDeviceType + "internal type: " + mInternalDeviceType
+ + " addr: " + mDeviceAddress + " enabled: " + mSAEnabled
+ + " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled;
+ }
+
+ public String toPersistableString() {
+ return (new StringBuilder().append(mDeviceType)
+ .append(SETTING_FIELD_SEPARATOR).append(mDeviceAddress)
+ .append(SETTING_FIELD_SEPARATOR).append(mSAEnabled ? "1" : "0")
+ .append(SETTING_FIELD_SEPARATOR).append(mHasHeadTracker ? "1" : "0")
+ .append(SETTING_FIELD_SEPARATOR).append(mHeadTrackerEnabled ? "1" : "0")
+ .append(SETTING_FIELD_SEPARATOR).append(mInternalDeviceType)
+ .toString());
+ }
+
+ /**
+ * Gets the max size (including separators) when persisting the elements with
+ * {@link AdiDeviceState#toPersistableString()}.
+ */
+ public static int getPeristedMaxSize() {
+ return 36; /* (mDeviceType)2 + (mDeviceAddress)17 + (mInternalDeviceType)9 + (mSAEnabled)1
+ + (mHasHeadTracker)1 + (mHasHeadTrackerEnabled)1
+ + (SETTINGS_FIELD_SEPARATOR)5 */
+ }
+
+ @Nullable
+ public static AdiDeviceState fromPersistedString(@Nullable String persistedString) {
+ if (persistedString == null) {
+ return null;
+ }
+ if (persistedString.isEmpty()) {
+ return null;
+ }
+ String[] fields = TextUtils.split(persistedString, SETTING_FIELD_SEPARATOR);
+ // we may have 5 fields for the legacy AdiDeviceState and 6 containing the internal
+ // device type
+ if (fields.length != 5 && fields.length != 6) {
+ // expecting all fields, fewer may mean corruption, ignore those settings
+ return null;
+ }
+ try {
+ final int deviceType = Integer.parseInt(fields[0]);
+ int internalDeviceType = -1;
+ if (fields.length == 6) {
+ internalDeviceType = Integer.parseInt(fields[5]);
+ }
+ final AdiDeviceState deviceState = new AdiDeviceState(deviceType,
+ internalDeviceType, fields[1]);
+ deviceState.setHasHeadTracker(Integer.parseInt(fields[2]) == 1);
+ deviceState.setHasHeadTracker(Integer.parseInt(fields[3]) == 1);
+ deviceState.setHeadTrackerEnabled(Integer.parseInt(fields[4]) == 1);
+ return deviceState;
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "unable to parse setting for AdiDeviceState: " + persistedString, e);
+ return null;
+ }
+ }
+
+ public AudioDeviceAttributes getAudioDeviceAttributes() {
+ return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+ mDeviceType, mDeviceAddress);
+ }
+
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 4f45c0dd2f14..85fc8036bc3e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -50,6 +50,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -69,8 +70,11 @@ import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
-/** @hide */
-/*package*/ final class AudioDeviceBroker {
+/**
+ * @hide
+ * (non final for mocking/spying)
+ */
+public class AudioDeviceBroker {
private static final String TAG = "AS.AudioDeviceBroker";
@@ -1850,6 +1854,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
BtHelper.onNotifyPreferredAudioProfileApplied(btDevice);
} break;
+ case MSG_PERSIST_AUDIO_DEVICE_SETTINGS:
+ onPersistAudioDeviceSettings();
+ break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -1927,6 +1934,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED = 52;
+ private static final int MSG_PERSIST_AUDIO_DEVICE_SETTINGS = 54;
+
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
@@ -2344,4 +2353,95 @@ import java.util.concurrent.atomic.AtomicBoolean;
info.getId(),
null /*mixerAttributes*/);
}
+
+ /**
+ * post a message to persist the audio device settings.
+ * Message is delayed by 1s on purpose in case of successive changes in quick succession (at
+ * init time for instance)
+ * Note this method is made public to work around a Mockito bug where it needs to be public
+ * in order to be mocked by a test a the same package
+ * (see https://code.google.com/archive/p/mockito/issues/127)
+ */
+ public void persistAudioDeviceSettings() {
+ sendMsg(MSG_PERSIST_AUDIO_DEVICE_SETTINGS, SENDMSG_REPLACE, /*delay*/ 1000);
+ }
+
+ void onPersistAudioDeviceSettings() {
+ final String deviceSettings = mDeviceInventory.getDeviceSettings();
+ Log.v(TAG, "saving audio device settings: " + deviceSettings);
+ final SettingsAdapter settings = mAudioService.getSettings();
+ boolean res = settings.putSecureStringForUser(mAudioService.getContentResolver(),
+ Settings.Secure.AUDIO_DEVICE_INVENTORY,
+ deviceSettings, UserHandle.USER_CURRENT);
+ if (!res) {
+ Log.e(TAG, "error saving audio device settings: " + deviceSettings);
+ }
+ }
+
+ void onReadAudioDeviceSettings() {
+ final SettingsAdapter settingsAdapter = mAudioService.getSettings();
+ final ContentResolver contentResolver = mAudioService.getContentResolver();
+ String settings = settingsAdapter.getSecureStringForUser(contentResolver,
+ Settings.Secure.AUDIO_DEVICE_INVENTORY, UserHandle.USER_CURRENT);
+ if (settings == null) {
+ Log.i(TAG, "reading spatial audio device settings from legacy key"
+ + Settings.Secure.SPATIAL_AUDIO_ENABLED);
+ // legacy string format for key SPATIAL_AUDIO_ENABLED has the same order of fields like
+ // the strings for key AUDIO_DEVICE_INVENTORY. This will ensure to construct valid
+ // device settings when calling {@link #setDeviceSettings()}
+ settings = settingsAdapter.getSecureStringForUser(contentResolver,
+ Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
+ if (settings == null) {
+ Log.i(TAG, "no spatial audio device settings stored with legacy key");
+ } else if (!settings.equals("")) {
+ // Delete old key value and update the new key
+ if (!settingsAdapter.putSecureStringForUser(contentResolver,
+ Settings.Secure.SPATIAL_AUDIO_ENABLED,
+ /*value=*/"",
+ UserHandle.USER_CURRENT)) {
+ Log.w(TAG, "cannot erase the legacy audio device settings with key "
+ + Settings.Secure.SPATIAL_AUDIO_ENABLED);
+ }
+ if (!settingsAdapter.putSecureStringForUser(contentResolver,
+ Settings.Secure.AUDIO_DEVICE_INVENTORY,
+ settings,
+ UserHandle.USER_CURRENT)) {
+ Log.e(TAG, "error updating the new audio device settings with key "
+ + Settings.Secure.AUDIO_DEVICE_INVENTORY);
+ }
+ }
+ }
+
+ if (settings != null && !settings.equals("")) {
+ setDeviceSettings(settings);
+ }
+ }
+
+ void setDeviceSettings(String settings) {
+ mDeviceInventory.setDeviceSettings(settings);
+ }
+
+ /** Test only method. */
+ String getDeviceSettings() {
+ return mDeviceInventory.getDeviceSettings();
+ }
+
+ List<AdiDeviceState> getImmutableDeviceInventory() {
+ return mDeviceInventory.getImmutableDeviceInventory();
+ }
+
+ void addDeviceStateToInventory(AdiDeviceState deviceState) {
+ mDeviceInventory.addDeviceStateToInventory(deviceState);
+ }
+
+ AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada,
+ int canonicalType) {
+ return mDeviceInventory.findDeviceStateForAudioDeviceAttributes(ada, canonicalType);
+ }
+
+ //------------------------------------------------
+ // for testing purposes only
+ void clearDeviceInventory() {
+ mDeviceInventory.clearDeviceInventory();
+ }
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 0c7f11f98809..b4ab14f7c831 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -15,6 +15,8 @@
*/
package com.android.server.audio;
+import static android.media.AudioSystem.isBluetoothDevice;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
@@ -78,12 +80,51 @@ public class AudioDeviceInventory {
private static final String TAG = "AS.AudioDeviceInventory";
+ private static final String SETTING_DEVICE_SEPARATOR_CHAR = "|";
+ private static final String SETTING_DEVICE_SEPARATOR = "\\|";
+
// lock to synchronize all access to mConnectedDevices and mApmConnectedDevices
private final Object mDevicesLock = new Object();
//Audio Analytics ids.
private static final String mMetricsId = "audio.device.";
+ private final Object mDeviceInventoryLock = new Object();
+ @GuardedBy("mDeviceCatalogLock")
+ private final ArrayList<AdiDeviceState> mDeviceInventory = new ArrayList<>(0);
+ List<AdiDeviceState> getImmutableDeviceInventory() {
+ synchronized (mDeviceInventoryLock) {
+ return List.copyOf(mDeviceInventory);
+ }
+ }
+
+ void addDeviceStateToInventory(AdiDeviceState deviceState) {
+ synchronized (mDeviceInventoryLock) {
+ mDeviceInventory.add(deviceState);
+ }
+ }
+
+ AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada,
+ int canonicalDeviceType) {
+ final boolean isWireless = isBluetoothDevice(ada.getInternalType());
+ synchronized (mDeviceInventoryLock) {
+ for (AdiDeviceState deviceSetting : mDeviceInventory) {
+ if (deviceSetting.getDeviceType() == canonicalDeviceType
+ && (!isWireless || ada.getAddress().equals(
+ deviceSetting.getDeviceAddress()))) {
+ return deviceSetting;
+ }
+ }
+ }
+ return null;
+ }
+
+ void clearDeviceInventory() {
+ synchronized (mDeviceInventoryLock) {
+ mDeviceInventory.clear();
+ }
+ }
+
// List of connected devices
// Key for map created from DeviceInfo.makeDeviceListKey()
@GuardedBy("mDevicesLock")
@@ -341,6 +382,12 @@ public class AudioDeviceInventory {
mAppliedPresetRolesInt.forEach((key, devices) -> {
pw.println(" " + prefix + "preset: " + key.first
+ " role:" + key.second + " devices:" + devices); });
+ pw.println("\ndevices:\n");
+ synchronized (mDeviceInventoryLock) {
+ for (AdiDeviceState device : mDeviceInventory) {
+ pw.println("\t" + device + "\n");
+ }
+ }
}
//------------------------------------------------------------
@@ -1198,7 +1245,7 @@ public class AudioDeviceInventory {
AudioDeviceInfo device = Stream.of(connectedDevices)
.filter(d -> d.getInternalType() == ada.getInternalType())
- .filter(d -> (!AudioSystem.isBluetoothDevice(d.getInternalType())
+ .filter(d -> (!isBluetoothDevice(d.getInternalType())
|| (d.getAddress().equals(ada.getAddress()))))
.findFirst()
.orElse(null);
@@ -1619,7 +1666,7 @@ public class AudioDeviceInventory {
}
for (DeviceInfo di : mConnectedDevices.values()) {
- if (!AudioSystem.isBluetoothDevice(di.mDeviceType)) {
+ if (!isBluetoothDevice(di.mDeviceType)) {
continue;
}
AudioDeviceAttributes ada =
@@ -1733,7 +1780,7 @@ public class AudioDeviceInventory {
}
HashSet<String> processedAddresses = new HashSet<>(0);
for (DeviceInfo di : mConnectedDevices.values()) {
- if (!AudioSystem.isBluetoothDevice(di.mDeviceType)
+ if (!isBluetoothDevice(di.mDeviceType)
|| processedAddresses.contains(di.mDeviceAddress)) {
continue;
}
@@ -1743,7 +1790,7 @@ public class AudioDeviceInventory {
+ di.mDeviceAddress + ", preferredProfiles: " + preferredProfiles);
}
for (DeviceInfo di2 : mConnectedDevices.values()) {
- if (!AudioSystem.isBluetoothDevice(di2.mDeviceType)
+ if (!isBluetoothDevice(di2.mDeviceType)
|| !di.mDeviceAddress.equals(di2.mDeviceAddress)) {
continue;
}
@@ -2359,6 +2406,40 @@ public class AudioDeviceInventory {
}
}
+ /*package*/ String getDeviceSettings() {
+ int deviceCatalogSize = 0;
+ synchronized (mDeviceInventoryLock) {
+ deviceCatalogSize = mDeviceInventory.size();
+ }
+ final StringBuilder settingsBuilder = new StringBuilder(
+ deviceCatalogSize * AdiDeviceState.getPeristedMaxSize());
+
+ synchronized (mDeviceInventoryLock) {
+ for (int i = 0; i < mDeviceInventory.size(); i++) {
+ settingsBuilder.append(mDeviceInventory.get(i).toPersistableString());
+ if (i != mDeviceInventory.size() - 1) {
+ settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR);
+ }
+ }
+ }
+ return settingsBuilder.toString();
+ }
+
+ /*package*/ void setDeviceSettings(String settings) {
+ clearDeviceInventory();
+ String[] devSettings = TextUtils.split(Objects.requireNonNull(settings),
+ SETTING_DEVICE_SEPARATOR);
+ // small list, not worth overhead of Arrays.stream(devSettings)
+ for (String setting : devSettings) {
+ AdiDeviceState devState = AdiDeviceState.fromPersistedString(setting);
+ // Note if the device is not compatible with spatialization mode or the device
+ // type is not canonical, it will be ignored in {@link SpatializerHelper}.
+ if (devState != null) {
+ addDeviceStateToInventory(devState);
+ }
+ }
+ }
+
//----------------------------------------------------------
// For tests only
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f313e168c62d..71424b14373b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -382,7 +382,6 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_DISPATCH_AUDIO_MODE = 40;
private static final int MSG_ROUTING_UPDATED = 41;
private static final int MSG_INIT_HEADTRACKING_SENSORS = 42;
- private static final int MSG_PERSIST_SPATIAL_AUDIO_DEVICE_SETTINGS = 43;
private static final int MSG_ADD_ASSISTANT_SERVICE_UID = 44;
private static final int MSG_REMOVE_ASSISTANT_SERVICE_UID = 45;
private static final int MSG_UPDATE_ACTIVE_ASSISTANT_SERVICE_UID = 46;
@@ -1022,6 +1021,8 @@ public class AudioService extends IAudioService.Stub
mAudioPolicy = audioPolicy;
mPlatformType = AudioSystem.getPlatformType(context);
+ mDeviceBroker = new AudioDeviceBroker(mContext, this, mAudioSystem);
+
mIsSingleVolume = AudioSystem.isSingleVolume(context);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -1034,13 +1035,14 @@ public class AudioService extends IAudioService.Stub
mSfxHelper = new SoundEffectsHelper(mContext, playerBase -> ignorePlayerLogs(playerBase));
- final boolean binauralEnabledDefault = SystemProperties.getBoolean(
+ boolean binauralEnabledDefault = SystemProperties.getBoolean(
"ro.audio.spatializer_binaural_enabled_default", true);
- final boolean transauralEnabledDefault = SystemProperties.getBoolean(
+ boolean transauralEnabledDefault = SystemProperties.getBoolean(
"ro.audio.spatializer_transaural_enabled_default", true);
- final boolean headTrackingEnabledDefault = mContext.getResources().getBoolean(
+ boolean headTrackingEnabledDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_spatial_audio_head_tracking_enabled_default);
- mSpatializerHelper = new SpatializerHelper(this, mAudioSystem,
+
+ mSpatializerHelper = new SpatializerHelper(this, mAudioSystem, mDeviceBroker,
binauralEnabledDefault, transauralEnabledDefault, headTrackingEnabledDefault);
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
@@ -1208,8 +1210,6 @@ public class AudioService extends IAudioService.Stub
mUseFixedVolume = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
- mDeviceBroker = new AudioDeviceBroker(mContext, this, mAudioSystem);
-
mRecordMonitor = new RecordingActivityMonitor(mContext);
mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true);
@@ -6573,6 +6573,10 @@ public class AudioService extends IAudioService.Stub
return mContentResolver;
}
+ /*package*/ SettingsAdapter getSettings() {
+ return mSettings;
+ }
+
///////////////////////////////////////////////////////////////////////////
// Internal methods
///////////////////////////////////////////////////////////////////////////
@@ -9215,10 +9219,6 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.onInitSensors();
break;
- case MSG_PERSIST_SPATIAL_AUDIO_DEVICE_SETTINGS:
- onPersistSpatialAudioDeviceSettings();
- break;
-
case MSG_RESET_SPATIALIZER:
mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
break;
@@ -10275,41 +10275,11 @@ public class AudioService extends IAudioService.Stub
}
void onInitSpatializer() {
- final String settings = mSettings.getSecureStringForUser(mContentResolver,
- Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
- if (settings == null) {
- Log.e(TAG, "error reading spatial audio device settings");
- }
- mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect, settings);
+ mDeviceBroker.onReadAudioDeviceSettings();
+ mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect);
mSpatializerHelper.setFeatureEnabled(mHasSpatializerEffect);
}
- /**
- * post a message to persist the spatial audio device settings.
- * Message is delayed by 1s on purpose in case of successive changes in quick succession (at
- * init time for instance)
- * Note this method is made public to work around a Mockito bug where it needs to be public
- * in order to be mocked by a test a the same package
- * (see https://code.google.com/archive/p/mockito/issues/127)
- */
- public void persistSpatialAudioDeviceSettings() {
- sendMsg(mAudioHandler,
- MSG_PERSIST_SPATIAL_AUDIO_DEVICE_SETTINGS,
- SENDMSG_REPLACE, /*arg1*/ 0, /*arg2*/ 0, TAG,
- /*delay*/ 1000);
- }
-
- void onPersistSpatialAudioDeviceSettings() {
- final String settings = mSpatializerHelper.getSADeviceSettings();
- Log.v(TAG, "saving spatial audio device settings: " + settings);
- boolean res = mSettings.putSecureStringForUser(mContentResolver,
- Settings.Secure.SPATIAL_AUDIO_ENABLED,
- settings, UserHandle.USER_CURRENT);
- if (!res) {
- Log.e(TAG, "error saving spatial audio device settings: " + settings);
- }
- }
-
//==========================================================================================
// camera sound is forced if any of the resources corresponding to one active SIM
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 462c9381b904..18f24db8b307 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -16,6 +16,8 @@
package com.android.server.audio;
+import static android.media.AudioSystem.isBluetoothDevice;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -35,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;
@@ -47,13 +48,13 @@ import android.util.Pair;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
-import java.util.Objects;
import java.util.UUID;
/**
@@ -73,38 +74,32 @@ public class SpatializerHelper {
private final @NonNull AudioSystemAdapter mASA;
private final @NonNull AudioService mAudioService;
+ private final @NonNull AudioDeviceBroker mDeviceBroker;
private @Nullable SensorManager mSensorManager;
//------------------------------------------------------------
- private static final SparseIntArray SPAT_MODE_FOR_DEVICE_TYPE = new SparseIntArray(14) {
+ /*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);
}
};
- private static final int[] WIRELESS_TYPES = { AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
- AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
- AudioDeviceInfo.TYPE_BLE_HEADSET,
- AudioDeviceInfo.TYPE_BLE_SPEAKER,
- AudioDeviceInfo.TYPE_BLE_BROADCAST
- };
-
// Spatializer state machine
/*package*/ static final int STATE_UNINITIALIZED = 0;
/*package*/ static final int STATE_NOT_SUPPORTED = 1;
@@ -114,10 +109,15 @@ public class SpatializerHelper {
/*package*/ static final int STATE_DISABLED_AVAILABLE = 6;
private int mState = STATE_UNINITIALIZED;
+ @VisibleForTesting boolean mBinauralEnabledDefault;
+ @VisibleForTesting boolean mTransauralEnabledDefault;
+ @VisibleForTesting boolean mHeadTrackingEnabledDefault;
+
private boolean mFeatureEnabled = false;
/** current level as reported by native Spatializer in callback */
private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+
private boolean mTransauralSupported = false;
private boolean mBinauralSupported = false;
private boolean mIsHeadTrackingSupported = false;
@@ -160,31 +160,21 @@ public class SpatializerHelper {
*/
private final ArrayList<Integer> mSACapableDeviceTypes = new ArrayList<>(0);
- /**
- * List of devices where Spatial Audio is possible. Each device can be enabled or disabled
- * (== user choice to use or not)
- */
- @GuardedBy("this")
- private final ArrayList<SADeviceState> mSADevices = new ArrayList<>(0);
-
//------------------------------------------------------
// initialization
- @SuppressWarnings("StaticAssignmentInConstructor")
SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa,
- boolean binauralEnabledDefault,
- boolean transauralEnabledDefault,
- boolean headTrackingEnabledDefault) {
+ @NonNull AudioDeviceBroker deviceBroker, boolean binauralEnabledDefault,
+ boolean transauralEnabledDefault, boolean headTrackingEnabledDefault) {
mAudioService = mother;
mASA = asa;
- // "StaticAssignmentInConstructor" warning is suppressed as the SpatializerHelper being
- // constructed here is the factory for SADeviceState, thus SADeviceState and its
- // private static field sHeadTrackingEnabledDefault should never be accessed directly.
- SADeviceState.sBinauralEnabledDefault = binauralEnabledDefault;
- SADeviceState.sTransauralEnabledDefault = transauralEnabledDefault;
- SADeviceState.sHeadTrackingEnabledDefault = headTrackingEnabledDefault;
+ mDeviceBroker = deviceBroker;
+
+ mBinauralEnabledDefault = binauralEnabledDefault;
+ mTransauralEnabledDefault = transauralEnabledDefault;
+ mHeadTrackingEnabledDefault = headTrackingEnabledDefault;
}
- synchronized void init(boolean effectExpected, @Nullable String settings) {
+ synchronized void init(boolean effectExpected) {
loglogi("init effectExpected=" + effectExpected);
if (!effectExpected) {
loglogi("init(): setting state to STATE_NOT_SUPPORTED due to effect not expected");
@@ -233,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:
@@ -261,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:
@@ -281,17 +271,18 @@ 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));
}
}
- // When initialized from AudioService, the settings string will be non-null.
- // Saved settings need to be applied after spatialization support is initialized above.
- if (settings != null) {
- setSADeviceSettings(settings);
+ // Log the saved device states that are compatible with SA
+ for (AdiDeviceState deviceState : mDeviceBroker.getImmutableDeviceInventory()) {
+ if (isSADevice(deviceState)) {
+ logDeviceState(deviceState, "setSADeviceSettings");
+ }
}
// for both transaural / binaural, we are not forcing enablement as the init() method
@@ -331,7 +322,7 @@ public class SpatializerHelper {
mState = STATE_UNINITIALIZED;
mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
- init(true, null /* settings */);
+ init(/*effectExpected=*/true);
setSpatializerEnabledInt(featureEnabled);
}
@@ -372,7 +363,7 @@ public class SpatializerHelper {
final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0);
// is media routed to a new device?
- if (isWireless(currentDevice.getType())) {
+ if (isBluetoothDevice(currentDevice.getInternalType())) {
addWirelessDeviceIfNew(currentDevice);
}
@@ -520,8 +511,8 @@ public class SpatializerHelper {
synchronized @NonNull List<AudioDeviceAttributes> getCompatibleAudioDevices() {
// build unionOf(mCompatibleAudioDevices, mEnabledDevice) - mDisabledAudioDevices
ArrayList<AudioDeviceAttributes> compatList = new ArrayList<>();
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.mEnabled) {
+ for (AdiDeviceState deviceState : mDeviceBroker.getImmutableDeviceInventory()) {
+ if (deviceState.isSAEnabled() && isSADevice(deviceState)) {
compatList.add(deviceState.getAudioDeviceAttributes());
}
}
@@ -548,29 +539,48 @@ public class SpatializerHelper {
return;
}
loglogi("addCompatibleAudioDevice: dev=" + ada);
- final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
- SADeviceState deviceUpdated = null; // non-null on update.
+ final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ initSAState(deviceState);
+ AdiDeviceState updatedDevice = null; // non-null on update.
if (deviceState != null) {
- if (forceEnable && !deviceState.mEnabled) {
- deviceUpdated = deviceState;
- deviceUpdated.mEnabled = true;
+ if (forceEnable && !deviceState.isSAEnabled()) {
+ updatedDevice = deviceState;
+ updatedDevice.setSAEnabled(true);
}
} else {
// When adding, force the device type to be a canonical one.
- final int canonicalDeviceType = getCanonicalDeviceType(ada.getType());
+ final int canonicalDeviceType = getCanonicalDeviceType(ada.getType(),
+ ada.getInternalType());
if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) {
Log.e(TAG, "addCompatibleAudioDevice with incompatible AudioDeviceAttributes "
+ ada);
return;
}
- deviceUpdated = new SADeviceState(canonicalDeviceType, ada.getAddress());
- mSADevices.add(deviceUpdated);
+ updatedDevice = new AdiDeviceState(canonicalDeviceType, ada.getInternalType(),
+ ada.getAddress());
+ initSAState(updatedDevice);
+ mDeviceBroker.addDeviceStateToInventory(updatedDevice);
}
- if (deviceUpdated != null) {
+ if (updatedDevice != null) {
onRoutingUpdated();
- mAudioService.persistSpatialAudioDeviceSettings();
- logDeviceState(deviceUpdated, "addCompatibleAudioDevice");
+ mDeviceBroker.persistAudioDeviceSettings();
+ logDeviceState(updatedDevice, "addCompatibleAudioDevice");
+ }
+ }
+
+ private void initSAState(AdiDeviceState device) {
+ if (device == null) {
+ return;
}
+
+ int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(device.getDeviceType(),
+ Integer.MIN_VALUE);
+ device.setSAEnabled(spatMode == Spatialization.Mode.BINAURAL
+ ? mBinauralEnabledDefault
+ : spatMode == Spatialization.Mode.TRANSAURAL
+ ? mTransauralEnabledDefault
+ : false);
+ device.setHeadTrackerEnabled(mHeadTrackingEnabledDefault);
}
private static final String METRICS_DEVICE_PREFIX = "audio.spatializer.device.";
@@ -580,29 +590,30 @@ public class SpatializerHelper {
//
// There may be different devices with the same device type (aliasing).
// We always send the full device state info on each change.
- private void logDeviceState(SADeviceState deviceState, String event) {
+ static void logDeviceState(AdiDeviceState deviceState, String event) {
final int deviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(
- deviceState.mDeviceType);
+ deviceState.getDeviceType());
final String deviceName = AudioSystem.getDeviceName(deviceType);
new MediaMetrics.Item(METRICS_DEVICE_PREFIX + deviceName)
- .set(MediaMetrics.Property.ADDRESS, deviceState.mDeviceAddress)
- .set(MediaMetrics.Property.ENABLED, deviceState.mEnabled ? "true" : "false")
- .set(MediaMetrics.Property.EVENT, TextUtils.emptyIfNull(event))
- .set(MediaMetrics.Property.HAS_HEAD_TRACKER,
- deviceState.mHasHeadTracker ? "true" : "false") // this may be updated later.
- .set(MediaMetrics.Property.HEAD_TRACKER_ENABLED,
- deviceState.mHeadTrackerEnabled ? "true" : "false")
- .record();
+ .set(MediaMetrics.Property.ADDRESS, deviceState.getDeviceAddress())
+ .set(MediaMetrics.Property.ENABLED, deviceState.isSAEnabled() ? "true" : "false")
+ .set(MediaMetrics.Property.EVENT, TextUtils.emptyIfNull(event))
+ .set(MediaMetrics.Property.HAS_HEAD_TRACKER,
+ deviceState.hasHeadTracker() ? "true"
+ : "false") // this may be updated later.
+ .set(MediaMetrics.Property.HEAD_TRACKER_ENABLED,
+ deviceState.isHeadTrackerEnabled() ? "true" : "false")
+ .record();
}
synchronized void removeCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
loglogi("removeCompatibleAudioDevice: dev=" + ada);
- final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
- if (deviceState != null && deviceState.mEnabled) {
- deviceState.mEnabled = false;
+ final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ if (deviceState != null && deviceState.isSAEnabled()) {
+ deviceState.setSAEnabled(false);
onRoutingUpdated();
- mAudioService.persistSpatialAudioDeviceSettings();
+ mDeviceBroker.persistAudioDeviceSettings();
logDeviceState(deviceState, "removeCompatibleAudioDevice");
}
}
@@ -611,13 +622,14 @@ public class SpatializerHelper {
* Returns a possibly aliased device type which is used
* for spatial audio settings (or TYPE_UNKNOWN if it doesn't exist).
*/
- private static @AudioDeviceInfo.AudioDeviceType int getCanonicalDeviceType(int deviceType) {
- if (isWireless(deviceType)) return deviceType;
+ @AudioDeviceInfo.AudioDeviceType
+ private static int getCanonicalDeviceType(int deviceType, int internalDeviceType) {
+ 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;
@@ -629,18 +641,9 @@ public class SpatializerHelper {
*/
@GuardedBy("this")
@Nullable
- private SADeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada) {
- final int deviceType = ada.getType();
- final boolean isWireless = isWireless(deviceType);
- final int canonicalDeviceType = getCanonicalDeviceType(deviceType);
-
- for (SADeviceState deviceState : mSADevices) {
- if (deviceState.mDeviceType == canonicalDeviceType
- && (!isWireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
- return deviceState;
- }
- }
- return null;
+ private AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada) {
+ return mDeviceBroker.findDeviceStateForAudioDeviceAttributes(ada,
+ getCanonicalDeviceType(ada.getType(), ada.getInternalType()));
}
/**
@@ -662,14 +665,14 @@ public class SpatializerHelper {
Log.e(TAG, "no spatialization mode found for device type:" + deviceType);
return new Pair<>(false, false);
}
- final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
if (deviceState == null) {
// no matching device state?
Log.i(TAG, "no spatialization device state found for Spatial Audio device:" + ada);
return new Pair<>(false, false);
}
// found the matching device state.
- return new Pair<>(deviceState.mEnabled, true /* available */);
+ return new Pair<>(deviceState.isSAEnabled(), true /* available */);
}
private synchronized void addWirelessDeviceIfNew(@NonNull AudioDeviceAttributes ada) {
@@ -678,16 +681,19 @@ public class SpatializerHelper {
}
if (findDeviceStateForAudioDeviceAttributes(ada) == null) {
// wireless device types should be canonical, but we translate to be sure.
- final int canonicalDeviceType = getCanonicalDeviceType((ada.getType()));
+ final int canonicalDeviceType = getCanonicalDeviceType(ada.getType(),
+ ada.getInternalType());
if (canonicalDeviceType == AudioDeviceInfo.TYPE_UNKNOWN) {
Log.e(TAG, "addWirelessDeviceIfNew with incompatible AudioDeviceAttributes "
+ ada);
return;
}
- final SADeviceState deviceState =
- new SADeviceState(canonicalDeviceType, ada.getAddress());
- mSADevices.add(deviceState);
- mAudioService.persistSpatialAudioDeviceSettings();
+ final AdiDeviceState deviceState =
+ new AdiDeviceState(canonicalDeviceType, ada.getInternalType(),
+ ada.getAddress());
+ initSAState(deviceState);
+ mDeviceBroker.addDeviceStateToInventory(deviceState);
+ mDeviceBroker.persistAudioDeviceSettings();
logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later.
}
}
@@ -748,14 +754,20 @@ 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;
}
return false;
}
+ private boolean isSADevice(AdiDeviceState deviceState) {
+ return deviceState.getDeviceType() == getCanonicalDeviceType(deviceState.getDeviceType(),
+ deviceState.getInternalDeviceType()) && isDeviceCompatibleWithSpatializationModes(
+ deviceState.getAudioDeviceAttributes());
+ }
+
synchronized void setFeatureEnabled(boolean enabled) {
loglogi("setFeatureEnabled(" + enabled + ") was featureEnabled:" + mFeatureEnabled);
if (mFeatureEnabled == enabled) {
@@ -768,7 +780,7 @@ public class SpatializerHelper {
return;
}
if (mState == STATE_UNINITIALIZED) {
- init(true, null /* settings */);
+ init(true);
}
setSpatializerEnabledInt(true);
} else {
@@ -1137,16 +1149,16 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, ignoring setHeadTrackerEnabled to " + enabled
+ " for " + ada);
}
- final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
if (deviceState == null) return;
- if (!deviceState.mHasHeadTracker) {
+ if (!deviceState.hasHeadTracker()) {
Log.e(TAG, "Called setHeadTrackerEnabled enabled:" + enabled
+ " device:" + ada + " on a device without headtracker");
return;
}
Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
- deviceState.mHeadTrackerEnabled = enabled;
- mAudioService.persistSpatialAudioDeviceSettings();
+ deviceState.setHeadTrackerEnabled(enabled);
+ mDeviceBroker.persistAudioDeviceSettings();
logDeviceState(deviceState, "setHeadTrackerEnabled");
// check current routing to see if it affects the headtracking mode
@@ -1170,8 +1182,8 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, hasHeadTracker always false for " + ada);
return false;
}
- final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
- return deviceState != null && deviceState.mHasHeadTracker;
+ final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ return deviceState != null && deviceState.hasHeadTracker();
}
/**
@@ -1184,14 +1196,14 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, setHasHeadTracker always false for " + ada);
return false;
}
- final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
if (deviceState != null) {
- if (!deviceState.mHasHeadTracker) {
- deviceState.mHasHeadTracker = true;
- mAudioService.persistSpatialAudioDeviceSettings();
+ if (!deviceState.hasHeadTracker()) {
+ deviceState.setHasHeadTracker(true);
+ mDeviceBroker.persistAudioDeviceSettings();
logDeviceState(deviceState, "setHasHeadTracker");
}
- return deviceState.mHeadTrackerEnabled;
+ return deviceState.isHeadTrackerEnabled();
}
Log.e(TAG, "setHasHeadTracker: device not found for:" + ada);
return false;
@@ -1202,9 +1214,9 @@ public class SpatializerHelper {
Log.v(TAG, "no headtracking support, isHeadTrackerEnabled always false for " + ada);
return false;
}
- final SADeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
+ final AdiDeviceState deviceState = findDeviceStateForAudioDeviceAttributes(ada);
return deviceState != null
- && deviceState.mHasHeadTracker && deviceState.mHeadTrackerEnabled;
+ && deviceState.hasHeadTracker() && deviceState.isHeadTrackerEnabled();
}
synchronized boolean isHeadTrackerAvailable() {
@@ -1417,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");
@@ -1483,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));
@@ -1499,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));
}
@@ -1513,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));
@@ -1543,144 +1555,6 @@ public class SpatializerHelper {
pw.println("\tsupports binaural:" + mBinauralSupported + " / transaural:"
+ mTransauralSupported);
pw.println("\tmSpatOutput:" + mSpatOutput);
- pw.println("\tdevices:");
- for (SADeviceState device : mSADevices) {
- pw.println("\t\t" + device);
- }
- }
-
- /*package*/ static final class SADeviceState {
- private static boolean sBinauralEnabledDefault = true;
- private static boolean sTransauralEnabledDefault = true;
- private static boolean sHeadTrackingEnabledDefault = false;
- final @AudioDeviceInfo.AudioDeviceType int mDeviceType;
- final @NonNull String mDeviceAddress;
- boolean mEnabled;
- boolean mHasHeadTracker = false;
- boolean mHeadTrackerEnabled;
- static final String SETTING_FIELD_SEPARATOR = ",";
- static final String SETTING_DEVICE_SEPARATOR_CHAR = "|";
- static final String SETTING_DEVICE_SEPARATOR = "\\|";
-
- /**
- * Constructor
- * @param deviceType
- * @param address must be non-null for wireless devices
- * @throws NullPointerException if a null address is passed for a wireless device
- */
- SADeviceState(@AudioDeviceInfo.AudioDeviceType int deviceType, @Nullable String address) {
- mDeviceType = deviceType;
- mDeviceAddress = isWireless(deviceType) ? Objects.requireNonNull(address) : "";
- final int spatMode = SPAT_MODE_FOR_DEVICE_TYPE.get(deviceType, Integer.MIN_VALUE);
- mEnabled = spatMode == SpatializationMode.SPATIALIZER_BINAURAL
- ? sBinauralEnabledDefault
- : spatMode == SpatializationMode.SPATIALIZER_TRANSAURAL
- ? sTransauralEnabledDefault
- : false;
- mHeadTrackerEnabled = sHeadTrackingEnabledDefault;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- // type check and cast
- if (getClass() != obj.getClass()) {
- return false;
- }
- final SADeviceState sads = (SADeviceState) obj;
- return mDeviceType == sads.mDeviceType
- && mDeviceAddress.equals(sads.mDeviceAddress)
- && mEnabled == sads.mEnabled
- && mHasHeadTracker == sads.mHasHeadTracker
- && mHeadTrackerEnabled == sads.mHeadTrackerEnabled;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mDeviceType, mDeviceAddress, mEnabled, mHasHeadTracker,
- mHeadTrackerEnabled);
- }
-
- @Override
- public String toString() {
- return "type: " + mDeviceType + " addr: " + mDeviceAddress + " enabled: " + mEnabled
- + " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled;
- }
-
- String toPersistableString() {
- return (new StringBuilder().append(mDeviceType)
- .append(SETTING_FIELD_SEPARATOR).append(mDeviceAddress)
- .append(SETTING_FIELD_SEPARATOR).append(mEnabled ? "1" : "0")
- .append(SETTING_FIELD_SEPARATOR).append(mHasHeadTracker ? "1" : "0")
- .append(SETTING_FIELD_SEPARATOR).append(mHeadTrackerEnabled ? "1" : "0")
- .toString());
- }
-
- static @Nullable SADeviceState fromPersistedString(@Nullable String persistedString) {
- if (persistedString == null) {
- return null;
- }
- if (persistedString.isEmpty()) {
- return null;
- }
- String[] fields = TextUtils.split(persistedString, SETTING_FIELD_SEPARATOR);
- if (fields.length != 5) {
- // expecting all fields, fewer may mean corruption, ignore those settings
- return null;
- }
- try {
- final int deviceType = Integer.parseInt(fields[0]);
- final SADeviceState deviceState = new SADeviceState(deviceType, fields[1]);
- deviceState.mEnabled = Integer.parseInt(fields[2]) == 1;
- deviceState.mHasHeadTracker = Integer.parseInt(fields[3]) == 1;
- deviceState.mHeadTrackerEnabled = Integer.parseInt(fields[4]) == 1;
- return deviceState;
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse setting for SADeviceState: " + persistedString, e);
- return null;
- }
- }
-
- public AudioDeviceAttributes getAudioDeviceAttributes() {
- return new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
- mDeviceType, mDeviceAddress == null ? "" : mDeviceAddress);
- }
-
- }
-
- /*package*/ synchronized String getSADeviceSettings() {
- // expected max size of each String for each SADeviceState is 25 (accounting for separator)
- final StringBuilder settingsBuilder = new StringBuilder(mSADevices.size() * 25);
- for (int i = 0; i < mSADevices.size(); i++) {
- settingsBuilder.append(mSADevices.get(i).toPersistableString());
- if (i != mSADevices.size() - 1) {
- settingsBuilder.append(SADeviceState.SETTING_DEVICE_SEPARATOR_CHAR);
- }
- }
- return settingsBuilder.toString();
- }
-
- /*package*/ synchronized void setSADeviceSettings(@NonNull String persistedSettings) {
- String[] devSettings = TextUtils.split(Objects.requireNonNull(persistedSettings),
- SADeviceState.SETTING_DEVICE_SEPARATOR);
- // small list, not worth overhead of Arrays.stream(devSettings)
- for (String setting : devSettings) {
- SADeviceState devState = SADeviceState.fromPersistedString(setting);
- // Note if the device is not compatible with spatialization mode
- // or the device type is not canonical, it is ignored.
- if (devState != null
- && devState.mDeviceType == getCanonicalDeviceType(devState.mDeviceType)
- && isDeviceCompatibleWithSpatializationModes(
- devState.getAudioDeviceAttributes())) {
- mSADevices.add(devState);
- logDeviceState(devState, "setSADeviceSettings");
- }
- }
}
private static String spatStateString(int state) {
@@ -1702,15 +1576,6 @@ public class SpatializerHelper {
}
}
- private static boolean isWireless(@AudioDeviceInfo.AudioDeviceType int deviceType) {
- for (int type : WIRELESS_TYPES) {
- if (type == deviceType) {
- return true;
- }
- }
- return false;
- }
-
private int getHeadSensorHandleUpdateTracker() {
int headHandle = -1;
if (sRoutingDevices.isEmpty()) {
@@ -1780,11 +1645,6 @@ public class SpatializerHelper {
//------------------------------------------------
// for testing purposes only
-
- /*package*/ void clearSADevices() {
- mSADevices.clear();
- }
-
/*package*/ synchronized void forceStateForTest(int state) {
mState = state;
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 4538cad513d6..0629e6373b6e 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -39,6 +39,7 @@ import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.ComponentInfoInternal;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IAuthService;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
@@ -333,6 +334,33 @@ public class AuthService extends SystemService {
}
@Override
+ public long getLastAuthenticationTime(int userId,
+ @Authenticators.Types int authenticators) throws RemoteException {
+ // Only allow internal clients to call getLastAuthenticationTime with a different
+ // userId.
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ if (userId != callingUserId) {
+ checkInternalPermission();
+ } else {
+ checkPermission();
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // We can't do this above because we need the READ_DEVICE_CONFIG permission, which
+ // the calling user may not possess.
+ if (!Flags.lastAuthenticationTime()) {
+ throw new UnsupportedOperationException();
+ }
+
+ return mBiometricService.getLastAuthenticationTime(userId, authenticators);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public boolean hasEnrolledBiometrics(int userId, String opPackageName)
throws RemoteException {
checkInternalPermission();
diff --git a/services/core/java/com/android/server/BrickReceiver.java b/services/core/java/com/android/server/biometrics/BiometricCameraManager.java
index cff3805b68d2..058ea6bbb696 100644
--- a/services/core/java/com/android/server/BrickReceiver.java
+++ b/services/core/java/com/android/server/biometrics/BiometricCameraManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-package com.android.server;
+package com.android.server.biometrics;
-import android.content.Context;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
-import android.os.SystemService;
-import android.util.Slog;
+/**
+ * Interface for biometrics to get camera status.
+ */
+public interface BiometricCameraManager {
+ /**
+ * Returns true if any camera is in use.
+ */
+ boolean isAnyCameraUnavailable();
-public class BrickReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Slog.w("BrickReceiver", "!!! BRICKING DEVICE !!!");
- SystemService.start("brick");
- }
+ /**
+ * Returns true if privacy is enabled and camera access is disabled.
+ */
+ boolean isCameraPrivacyEnabled();
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java b/services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java
new file mode 100644
index 000000000000..000ee5446962
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/BiometricCameraManagerImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+
+import android.annotation.NonNull;
+import android.hardware.SensorPrivacyManager;
+import android.hardware.camera2.CameraManager;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+public class BiometricCameraManagerImpl implements BiometricCameraManager {
+
+ private final CameraManager mCameraManager;
+ private final SensorPrivacyManager mSensorPrivacyManager;
+ private final ConcurrentHashMap<String, Boolean> mIsCameraAvailable = new ConcurrentHashMap<>();
+
+ private final CameraManager.AvailabilityCallback mCameraAvailabilityCallback =
+ new CameraManager.AvailabilityCallback() {
+ @Override
+ public void onCameraAvailable(@NonNull String cameraId) {
+ mIsCameraAvailable.put(cameraId, true);
+ }
+
+ @Override
+ public void onCameraUnavailable(@NonNull String cameraId) {
+ mIsCameraAvailable.put(cameraId, false);
+ }
+ };
+
+ public BiometricCameraManagerImpl(@NonNull CameraManager cameraManager,
+ @NonNull SensorPrivacyManager sensorPrivacyManager) {
+ mCameraManager = cameraManager;
+ mSensorPrivacyManager = sensorPrivacyManager;
+ mCameraManager.registerAvailabilityCallback(mCameraAvailabilityCallback, null);
+ }
+
+ @Override
+ public boolean isAnyCameraUnavailable() {
+ for (String cameraId : mIsCameraAvailable.keySet()) {
+ if (!mIsCameraAvailable.get(cameraId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isCameraPrivacyEnabled() {
+ return mSensorPrivacyManager != null && mSensorPrivacyManager
+ .isSensorPrivacyEnabled(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, CAMERA);
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 0942d8527565..9c454ae3b867 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -18,6 +18,7 @@ package com.android.server.biometrics;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import static android.hardware.biometrics.BiometricManager.BIOMETRIC_NO_AUTHENTICATION;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_IDLE;
@@ -33,9 +34,11 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
+import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -47,8 +50,10 @@ import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
+import android.hardware.camera2.CameraManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -58,10 +63,16 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.security.Authorization;
+import android.security.GateKeeper;
import android.security.KeyStore;
+import android.security.authorization.IKeystoreAuthorization;
+import android.security.authorization.ResponseCode;
+import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Pair;
@@ -75,6 +86,7 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.biometrics.log.BiometricContext;
+import com.android.server.utils.Slogf;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -112,6 +124,10 @@ public class BiometricService extends SystemService {
KeyStore mKeyStore;
@VisibleForTesting
ITrustManager mTrustManager;
+ @VisibleForTesting
+ IKeystoreAuthorization mKeystoreAuthorization;
+ @VisibleForTesting
+ IGateKeeperService mGateKeeper;
// Get and cache the available biometric authenticators and their associated info.
final ArrayList<BiometricSensor> mSensors = new ArrayList<>();
@@ -124,6 +140,8 @@ public class BiometricService extends SystemService {
AuthSession mAuthSession;
private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final BiometricCameraManager mBiometricCameraManager;
+
/**
* Tracks authenticatorId invalidation. For more details, see
* {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}.
@@ -626,6 +644,64 @@ public class BiometricService extends SystemService {
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
+ @Override // Binder call
+ public long getLastAuthenticationTime(
+ int userId, @Authenticators.Types int authenticators) {
+ super.getLastAuthenticationTime_enforcePermission();
+
+ if (!Flags.lastAuthenticationTime()) {
+ throw new UnsupportedOperationException();
+ }
+
+ Slogf.d(TAG, "getLastAuthenticationTime(userId=%d, authenticators=0x%x)",
+ userId, authenticators);
+
+ final long secureUserId;
+ try {
+ secureUserId = mGateKeeper.getSecureUserId(userId);
+ } catch (RemoteException e) {
+ Slogf.w(TAG, "Failed to get secure user id for " + userId, e);
+ return BIOMETRIC_NO_AUTHENTICATION;
+ }
+
+ if (secureUserId == GateKeeper.INVALID_SECURE_USER_ID) {
+ Slogf.w(TAG, "No secure user id for " + userId);
+ return BIOMETRIC_NO_AUTHENTICATION;
+ }
+
+ ArrayList<Integer> hardwareAuthenticators = new ArrayList<>(2);
+
+ if ((authenticators & Authenticators.DEVICE_CREDENTIAL) != 0) {
+ hardwareAuthenticators.add(HardwareAuthenticatorType.PASSWORD);
+ }
+
+ if ((authenticators & Authenticators.BIOMETRIC_STRONG) != 0) {
+ hardwareAuthenticators.add(HardwareAuthenticatorType.FINGERPRINT);
+ }
+
+ if (hardwareAuthenticators.isEmpty()) {
+ throw new IllegalArgumentException("authenticators must not be empty");
+ }
+
+ int[] authTypesArray = hardwareAuthenticators.stream()
+ .mapToInt(Integer::intValue)
+ .toArray();
+ try {
+ return mKeystoreAuthorization.getLastAuthTime(secureUserId, authTypesArray);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error getting last auth time: " + e);
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ } catch (ServiceSpecificException e) {
+ // This is returned when the feature flag test fails in keystore2
+ if (e.errorCode == ResponseCode.PERMISSION_DENIED) {
+ throw new UnsupportedOperationException();
+ }
+
+ return BiometricConstants.BIOMETRIC_NO_AUTHENTICATION;
+ }
+ }
+
+ @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
public boolean hasEnrolledBiometrics(int userId, String opPackageName) {
@@ -933,7 +1009,7 @@ public class BiometricService extends SystemService {
return PreAuthInfo.create(mTrustManager, mDevicePolicyManager, mSettingObserver, mSensors,
userId, promptInfo, opPackageName, false /* checkDevicePolicyManager */,
- getContext());
+ getContext(), mBiometricCameraManager);
}
/**
@@ -947,6 +1023,14 @@ public class BiometricService extends SystemService {
return ActivityManager.getService();
}
+ public IKeystoreAuthorization getKeystoreAuthorizationService() {
+ return Authorization.getService();
+ }
+
+ public IGateKeeperService getGateKeeperService() {
+ return GateKeeper.getService();
+ }
+
public ITrustManager getTrustManager() {
return ITrustManager.Stub.asInterface(ServiceManager.getService(Context.TRUST_SERVICE));
}
@@ -1026,6 +1110,11 @@ public class BiometricService extends SystemService {
public UserManager getUserManager(Context context) {
return context.getSystemService(UserManager.class);
}
+
+ public BiometricCameraManager getBiometricCameraManager(Context context) {
+ return new BiometricCameraManagerImpl(context.getSystemService(CameraManager.class),
+ context.getSystemService(SensorPrivacyManager.class));
+ }
}
/**
@@ -1054,6 +1143,9 @@ public class BiometricService extends SystemService {
mRequestCounter = mInjector.getRequestGenerator();
mBiometricContext = injector.getBiometricContext(context);
mUserManager = injector.getUserManager(context);
+ mBiometricCameraManager = injector.getBiometricCameraManager(context);
+ mKeystoreAuthorization = injector.getKeystoreAuthorizationService();
+ mGateKeeper = injector.getGateKeeperService();
try {
injector.getActivityManagerService().registerUserSwitchObserver(
@@ -1290,7 +1382,7 @@ public class BiometricService extends SystemService {
final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager,
mDevicePolicyManager, mSettingObserver, mSensors, userId, promptInfo,
opPackageName, promptInfo.isDisallowBiometricsIfPolicyExists(),
- getContext());
+ getContext(), mBiometricCameraManager);
final Pair<Integer, Integer> preAuthStatus = preAuthInfo.getPreAuthenticateStatus();
@@ -1300,9 +1392,7 @@ public class BiometricService extends SystemService {
+ promptInfo.isIgnoreEnrollmentState());
// BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED is added so that BiometricPrompt can
// be shown for this case.
- if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS
- || preAuthStatus.second
- == BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED) {
+ if (preAuthStatus.second == BiometricConstants.BIOMETRIC_SUCCESS) {
// If BIOMETRIC_WEAK or BIOMETRIC_STRONG are allowed, but not enrolled, but
// CREDENTIAL is requested and available, set the bundle to only request
// CREDENTIAL.
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 3813fd1971a6..b603fcb498be 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -27,7 +27,6 @@ import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
import android.content.Context;
-import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.PromptInfo;
@@ -73,13 +72,16 @@ class PreAuthInfo {
final Context context;
private final boolean mBiometricRequested;
private final int mBiometricStrengthRequested;
+ private final BiometricCameraManager mBiometricCameraManager;
+
private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
boolean credentialRequested, List<BiometricSensor> eligibleSensors,
List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
boolean confirmationRequested, boolean ignoreEnrollmentState, int userId,
- Context context) {
+ Context context, BiometricCameraManager biometricCameraManager) {
mBiometricRequested = biometricRequested;
mBiometricStrengthRequested = biometricStrengthRequested;
+ mBiometricCameraManager = biometricCameraManager;
this.credentialRequested = credentialRequested;
this.eligibleSensors = eligibleSensors;
@@ -96,7 +98,8 @@ class PreAuthInfo {
BiometricService.SettingObserver settingObserver,
List<BiometricSensor> sensors,
int userId, PromptInfo promptInfo, String opPackageName,
- boolean checkDevicePolicyManager, Context context)
+ boolean checkDevicePolicyManager, Context context,
+ BiometricCameraManager biometricCameraManager)
throws RemoteException {
final boolean confirmationRequested = promptInfo.isConfirmationRequested();
@@ -124,7 +127,7 @@ class PreAuthInfo {
checkDevicePolicyManager, requestedStrength,
promptInfo.getAllowedSensorIds(),
promptInfo.isIgnoreEnrollmentState(),
- context);
+ biometricCameraManager);
Slog.d(TAG, "Package: " + opPackageName
+ " Sensor ID: " + sensor.id
@@ -138,7 +141,7 @@ class PreAuthInfo {
//
// Note: if only a certain sensor is required and the privacy is enabled,
// canAuthenticate() will return false.
- if (status == AUTHENTICATOR_OK || status == BIOMETRIC_SENSOR_PRIVACY_ENABLED) {
+ if (status == AUTHENTICATOR_OK) {
eligibleSensors.add(sensor);
} else {
ineligibleSensors.add(new Pair<>(sensor, status));
@@ -148,7 +151,7 @@ class PreAuthInfo {
return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
- promptInfo.isIgnoreEnrollmentState(), userId, context);
+ promptInfo.isIgnoreEnrollmentState(), userId, context, biometricCameraManager);
}
/**
@@ -165,12 +168,16 @@ class PreAuthInfo {
BiometricSensor sensor, int userId, String opPackageName,
boolean checkDevicePolicyManager, int requestedStrength,
@NonNull List<Integer> requestedSensorIds,
- boolean ignoreEnrollmentState, Context context) {
+ boolean ignoreEnrollmentState, BiometricCameraManager biometricCameraManager) {
if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
return BIOMETRIC_NO_HARDWARE;
}
+ if (sensor.modality == TYPE_FACE && biometricCameraManager.isAnyCameraUnavailable()) {
+ return BIOMETRIC_HARDWARE_NOT_DETECTED;
+ }
+
final boolean wasStrongEnough =
Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength);
final boolean isStrongEnough =
@@ -191,12 +198,10 @@ class PreAuthInfo {
&& !ignoreEnrollmentState) {
return BIOMETRIC_NOT_ENROLLED;
}
- final SensorPrivacyManager sensorPrivacyManager = context
- .getSystemService(SensorPrivacyManager.class);
- if (sensorPrivacyManager != null && sensor.modality == TYPE_FACE) {
- if (sensorPrivacyManager
- .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId)) {
+ if (biometricCameraManager != null && sensor.modality == TYPE_FACE) {
+ if (biometricCameraManager.isCameraPrivacyEnabled()) {
+ //Camera privacy is enabled as the access is disabled
return BIOMETRIC_SENSOR_PRIVACY_ENABLED;
}
}
@@ -292,13 +297,9 @@ class PreAuthInfo {
@AuthenticatorStatus final int status;
@BiometricAuthenticator.Modality int modality = TYPE_NONE;
- final SensorPrivacyManager sensorPrivacyManager = context
- .getSystemService(SensorPrivacyManager.class);
-
boolean cameraPrivacyEnabled = false;
- if (sensorPrivacyManager != null) {
- cameraPrivacyEnabled = sensorPrivacyManager
- .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId);
+ if (mBiometricCameraManager != null) {
+ cameraPrivacyEnabled = mBiometricCameraManager.isCameraPrivacyEnabled();
}
if (mBiometricRequested && credentialRequested) {
@@ -315,7 +316,7 @@ class PreAuthInfo {
// and the face sensor privacy is enabled then return
// BIOMETRIC_SENSOR_PRIVACY_ENABLED.
//
- // Note: This sensor will still be eligible for calls to authenticate.
+ // Note: This sensor will not be eligible for calls to authenticate.
status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
} else {
status = AUTHENTICATOR_OK;
@@ -340,7 +341,7 @@ class PreAuthInfo {
// If the only modality requested is face and the privacy is enabled
// then return BIOMETRIC_SENSOR_PRIVACY_ENABLED.
//
- // Note: This sensor will still be eligible for calls to authenticate.
+ // Note: This sensor will not be eligible for calls to authenticate.
status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
} else {
status = AUTHENTICATOR_OK;
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index f8f0088ac047..3c885a145315 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -53,6 +53,8 @@ import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.media.AudioManager;
import android.nfc.INfcAdapter;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcManager;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -162,10 +164,6 @@ public class CameraServiceProxy extends SystemService
* SCALER_ROTATE_AND_CROP_NONE -> Always return CaptureRequest.SCALER_ROTATE_AND_CROP_NONE
*/
- // Flags arguments to NFC adapter to enable/disable NFC
- public static final int DISABLE_POLLING_FLAGS = 0x1000;
- public static final int ENABLE_POLLING_FLAGS = 0x0000;
-
// Handler message codes
private static final int MSG_SWITCH_USER = 1;
private static final int MSG_NOTIFY_DEVICE_STATE = 2;
@@ -215,7 +213,6 @@ public class CameraServiceProxy extends SystemService
private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
- private static final String NFC_SERVICE_BINDER_NAME = "nfc";
private static final IBinder nfcInterfaceToken = new Binder();
private final boolean mNotifyNfc;
@@ -1255,8 +1252,13 @@ public class CameraServiceProxy extends SystemService
}
}
- private void notifyNfcService(boolean enablePolling) {
-
+ // TODO(b/303286040): Remove the raw INfcAdapter usage once |ENABLE_NFC_MAINLINE_FLAG| is
+ // rolled out.
+ private static final String NFC_SERVICE_BINDER_NAME = "nfc";
+ // Flags arguments to NFC adapter to enable/disable NFC
+ public static final int DISABLE_POLLING_FLAGS = 0x1000;
+ public static final int ENABLE_POLLING_FLAGS = 0x0000;
+ private void setNfcReaderModeUsingINfcAdapter(boolean enablePolling) {
IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
if (nfcServiceBinder == null) {
Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
@@ -1272,6 +1274,25 @@ public class CameraServiceProxy extends SystemService
}
}
+ private void notifyNfcService(boolean enablePolling) {
+ if (android.nfc.Flags.enableNfcMainline()) {
+ NfcManager nfcManager = mContext.getSystemService(NfcManager.class);
+ if (nfcManager == null) {
+ Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
+ return;
+ }
+ NfcAdapter nfcAdapter = nfcManager.getDefaultAdapter();
+ if (nfcAdapter == null) {
+ Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
+ return;
+ }
+ if (DEBUG) Slog.v(TAG, "Setting NFC reader mode. enablePolling: " + enablePolling);
+ nfcAdapter.setReaderMode(enablePolling);
+ } else {
+ setNfcReaderModeUsingINfcAdapter(enablePolling);
+ }
+ }
+
private static int[] toArray(Collection<Integer> c) {
int len = c.size();
int[] ret = new int[len];
diff --git a/services/core/java/com/android/server/compat/overrides/OWNERS b/services/core/java/com/android/server/compat/overrides/OWNERS
index b80f3402c19d..6ca7803a455c 100644
--- a/services/core/java/com/android/server/compat/overrides/OWNERS
+++ b/services/core/java/com/android/server/compat/overrides/OWNERS
@@ -1,2 +1,2 @@
-tomnatan@google.com
+mcarli@google.com
mariiasand@google.com
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 53fbe8f37046..a12243b8e4fa 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 {
@@ -2665,10 +2629,6 @@ public class Vpn {
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 +2660,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 +2689,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 +2709,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.
- }
- }
}
/**
@@ -4143,9 +3991,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,
@@ -4256,343 +4102,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 +4348,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 +4391,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/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 09d0bc76466c..fa95a348d8d3 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -31,7 +31,6 @@ import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
-import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.CURRENT_LSKF_BASED_PROTECTOR_ID_KEY;
@@ -40,8 +39,12 @@ import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABL
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.internal.widget.LockPatternUtils.USER_FRP;
+import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE;
import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
+import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
+import static com.android.internal.widget.LockPatternUtils.isSpecialUserId;
+import static com.android.internal.widget.LockPatternUtils.pinOrPasswordQualityToCredentialType;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_STRONG;
import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_WEAK;
@@ -237,6 +240,10 @@ public class LockSettingsService extends ILockSettings.Stub {
private static final String LSKF_LAST_CHANGED_TIME_KEY = "sp-handle-ts";
private static final String USER_SERIAL_NUMBER_KEY = "serial-number";
+ 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";
+
// Duration that LockSettingsService will store the gatekeeper password for. This allows
// multiple biometric enrollments without prompting the user to enter their password via
// ConfirmLockPassword/ConfirmLockPattern multiple times. This needs to be at least the duration
@@ -308,8 +315,6 @@ public class LockSettingsService extends ILockSettings.Stub {
protected IGateKeeperService mGateKeeperService;
protected IAuthSecret mAuthSecretService;
- private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
-
/**
* The UIDs that are used for system credential storage in keystore.
*/
@@ -340,6 +345,8 @@ public class LockSettingsService extends ILockSettings.Stub {
super.onBootPhase(phase);
if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mLockSettingsService.migrateOldDataAfterSystemReady();
+ mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
+ } else if (phase == PHASE_BOOT_COMPLETED) {
mLockSettingsService.loadEscrowData();
}
}
@@ -571,7 +578,7 @@ public class LockSettingsService extends ILockSettings.Stub {
}
public boolean isGsiRunning() {
- return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
+ return LockPatternUtils.isGsiRunning();
}
public FingerprintManager getFingerprintManager() {
@@ -687,9 +694,9 @@ public class LockSettingsService extends ILockSettings.Stub {
return;
}
- if (isUserKeyUnlocked(userId)) {
- // If storage is not locked, the user will be automatically unlocked so there is
- // no need to show the notification.
+ if (isCeStorageUnlocked(userId)) {
+ // If the user's CE storage is already unlocked, then the user will be automatically
+ // unlocked, so there is no need to show the notification.
return;
}
@@ -903,14 +910,14 @@ public class LockSettingsService extends ILockSettings.Stub {
}
private void migrateOldData() {
- if (getString("migrated_keystore_namespace", null, 0) == null) {
+ if (getString(MIGRATED_KEYSTORE_NS, null, 0) == null) {
boolean success = true;
synchronized (mSpManager) {
success &= mSpManager.migrateKeyNamespace();
}
success &= migrateProfileLockKeys();
if (success) {
- setString("migrated_keystore_namespace", "true", 0);
+ setString(MIGRATED_KEYSTORE_NS, "true", 0);
Slog.i(TAG, "Migrated keys to LSS namespace");
} else {
Slog.w(TAG, "Failed to migrate keys to LSS namespace");
@@ -930,9 +937,9 @@ public class LockSettingsService extends ILockSettings.Stub {
// "migrated_frp" to "migrated_frp2" to cause migrateFrpCredential() to run again on devices
// where it had run before.
if (LockPatternUtils.frpCredentialEnabled(mContext)
- && !getBoolean("migrated_frp2", false, 0)) {
+ && !getBoolean(MIGRATED_FRP2, false, 0)) {
migrateFrpCredential();
- setBoolean("migrated_frp2", true, 0);
+ setBoolean(MIGRATED_FRP2, true, 0);
}
}
@@ -979,6 +986,16 @@ public class LockSettingsService extends ILockSettings.Stub {
return success;
}
+ @VisibleForTesting
+ void deleteRepairModePersistentDataIfNeeded() {
+ if (!LockPatternUtils.isRepairModeSupported(mContext)
+ || LockPatternUtils.isRepairModeActive(mContext)
+ || mInjector.isGsiRunning()) {
+ return;
+ }
+ mStorage.deleteRepairModePersistentData();
+ }
+
// This is called when Weaver is guaranteed to be available (if the device supports Weaver).
// It does any synthetic password related work that was delayed from earlier in the boot.
private void onThirdPartyAppsStarted() {
@@ -1010,16 +1027,16 @@ public class LockSettingsService extends ILockSettings.Stub {
// they did have an SP then their CE key wasn't encrypted by it.
//
// 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 setUserKeyProtection() is
- // okay with the key being already protected by the given secret.
- if (getString("migrated_all_users_to_sp_and_bound_ce", null, 0) == null) {
+ // 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);
}
}
- setString("migrated_all_users_to_sp_and_bound_ce", "true", 0);
+ setString(MIGRATED_SP_CE_ONLY, "true", 0);
}
mThirdPartyAppsStarted = true;
@@ -1046,7 +1063,7 @@ public class LockSettingsService extends ILockSettings.Stub {
Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
return;
}
- setUserKeyProtection(userId, result.syntheticPassword.deriveFileBasedEncryptionKey());
+ setCeStorageProtection(userId, result.syntheticPassword);
}
}
@@ -1305,8 +1322,8 @@ public class LockSettingsService extends ILockSettings.Stub {
* {@link #CREDENTIAL_TYPE_PASSWORD}
*/
private int getCredentialTypeInternal(int userId) {
- if (userId == USER_FRP) {
- return getFrpCredentialType();
+ if (isSpecialUserId(userId)) {
+ return mSpManager.getSpecialUserCredentialType(userId);
}
synchronized (mSpManager) {
final long protectorId = getCurrentLskfBasedProtectorId(userId);
@@ -1322,29 +1339,6 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private int getFrpCredentialType() {
- PersistentData data = mStorage.readPersistentDataBlock();
- if (data.type != PersistentData.TYPE_SP_GATEKEEPER &&
- data.type != PersistentData.TYPE_SP_WEAVER) {
- return CREDENTIAL_TYPE_NONE;
- }
- int credentialType = SyntheticPasswordManager.getFrpCredentialType(data.payload);
- if (credentialType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) {
- return credentialType;
- }
- return pinOrPasswordQualityToCredentialType(data.qualityForUi);
- }
-
- private static int pinOrPasswordQualityToCredentialType(int quality) {
- if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
- return CREDENTIAL_TYPE_PASSWORD;
- }
- if (LockPatternUtils.isQualityNumericPin(quality)) {
- return CREDENTIAL_TYPE_PIN;
- }
- throw new IllegalArgumentException("Quality is neither Pin nor password: " + quality);
- }
-
private boolean isUserSecure(int userId) {
return getCredentialTypeInternal(userId) != CREDENTIAL_TYPE_NONE;
}
@@ -1354,8 +1348,8 @@ public class LockSettingsService extends ILockSettings.Stub {
AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
}
- private void unlockKeystore(byte[] password, int userHandle) {
- Authorization.onLockScreenEvent(false, userHandle, password, null);
+ private void unlockKeystore(int userId, SyntheticPassword sp) {
+ Authorization.onLockScreenEvent(false, userId, sp.deriveKeyStorePassword(), null);
}
@VisibleForTesting /** Note: this method is overridden in unit tests */
@@ -1586,8 +1580,8 @@ public class LockSettingsService extends ILockSettings.Stub {
* unlock operation.
*/
private void sendCredentialsOnUnlockIfRequired(LockscreenCredential credential, int userId) {
- // Don't send credentials during the factory reset protection flow.
- if (userId == USER_FRP) {
+ // Don't send credentials during the special user flow.
+ if (isSpecialUserId(userId)) {
return;
}
@@ -2008,10 +2002,11 @@ public class LockSettingsService extends ILockSettings.Stub {
mStorage.writeChildProfileLock(profileUserId, ArrayUtils.concat(iv, ciphertext));
}
- private void setUserKeyProtection(@UserIdInt int userId, byte[] secret) {
+ private void setCeStorageProtection(@UserIdInt int userId, SyntheticPassword sp) {
+ final byte[] secret = sp.deriveFileBasedEncryptionKey();
final long callingId = Binder.clearCallingIdentity();
try {
- mStorageManager.setUserKeyProtection(userId, secret);
+ mStorageManager.setCeStorageProtection(userId, secret);
} catch (RemoteException e) {
throw new IllegalStateException("Failed to protect CE key for user " + userId, e);
} finally {
@@ -2019,11 +2014,11 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private boolean isUserKeyUnlocked(int userId) {
+ private boolean isCeStorageUnlocked(int userId) {
try {
- return mStorageManager.isUserKeyUnlocked(userId);
+ return mStorageManager.isCeStorageUnlocked(userId);
} catch (RemoteException e) {
- Slog.e(TAG, "failed to check user key locked state", e);
+ Slog.e(TAG, "Error checking whether CE storage is unlocked", e);
return false;
}
}
@@ -2034,8 +2029,8 @@ public class LockSettingsService extends ILockSettings.Stub {
* This method doesn't throw exceptions because it is called opportunistically whenever a user
* is started. Whether it worked or not can be detected by whether the key got unlocked or not.
*/
- private void unlockUserKey(@UserIdInt int userId, SyntheticPassword sp) {
- if (isUserKeyUnlocked(userId)) {
+ private void unlockCeStorage(@UserIdInt int userId, SyntheticPassword sp) {
+ if (isCeStorageUnlocked(userId)) {
Slogf.d(TAG, "CE storage for user %d is already unlocked", userId);
return;
}
@@ -2043,7 +2038,7 @@ public class LockSettingsService extends ILockSettings.Stub {
final String userType = isUserSecure(userId) ? "secured" : "unsecured";
final byte[] secret = sp.deriveFileBasedEncryptionKey();
try {
- mStorageManager.unlockUserKey(userId, userInfo.serialNumber, secret);
+ mStorageManager.unlockCeStorage(userId, userInfo.serialNumber, secret);
Slogf.i(TAG, "Unlocked CE storage for %s user %d", userType, userId);
} catch (RemoteException e) {
Slogf.wtf(TAG, e, "Failed to unlock CE storage for %s user %d", userType, userId);
@@ -2052,10 +2047,14 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- private void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
+ @Override
+ public void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
+ checkPasswordReadPermission();
synchronized (mSpManager) {
- if (isUserKeyUnlocked(userId)) {
+ if (isCeStorageUnlocked(userId)) {
Slogf.d(TAG, "CE storage for user %d is already unlocked", userId);
+ // This method actually does more than unlock CE storage. However, if CE storage is
+ // already unlocked, then the other parts must have already been done too.
return;
}
if (isUserSecure(userId)) {
@@ -2072,7 +2071,7 @@ public class LockSettingsService extends ILockSettings.Stub {
return;
}
onSyntheticPasswordUnlocked(userId, result.syntheticPassword);
- unlockUserKey(userId, result.syntheticPassword);
+ unlockCeStorage(userId, result.syntheticPassword);
}
}
@@ -2208,15 +2207,19 @@ public class LockSettingsService extends ILockSettings.Stub {
Slog.e(TAG, "FRP credential can only be verified prior to provisioning.");
return VerifyCredentialResponse.ERROR;
}
+ if (userId == USER_REPAIR_MODE && !LockPatternUtils.isRepairModeActive(mContext)) {
+ Slog.e(TAG, "Repair mode is not active on the device.");
+ return VerifyCredentialResponse.ERROR;
+ }
Slogf.i(TAG, "Verifying lockscreen credential for user %d", userId);
final AuthenticationResult authResult;
VerifyCredentialResponse response;
synchronized (mSpManager) {
- if (userId == USER_FRP) {
- return mSpManager.verifyFrpCredential(getGateKeeperService(), credential,
- progressCallback);
+ if (isSpecialUserId(userId)) {
+ return mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(),
+ credential, progressCallback);
}
long protectorId = getCurrentLskfBasedProtectorId(userId);
@@ -2225,6 +2228,12 @@ public class LockSettingsService extends ILockSettings.Stub {
response = authResult.gkResponse;
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+ if ((flags & VERIFY_FLAG_WRITE_REPAIR_MODE_PW) != 0) {
+ if (!mSpManager.writeRepairModeCredentialLocked(protectorId, userId)) {
+ Slog.e(TAG, "Failed to write repair mode credential");
+ return VerifyCredentialResponse.ERROR;
+ }
+ }
// credential has matched
mBiometricDeferredQueue.addPendingLockoutResetForUser(userId,
authResult.syntheticPassword.deriveGkPassword());
@@ -2765,7 +2774,7 @@ public class LockSettingsService extends ILockSettings.Stub {
final long protectorId = mSpManager.createLskfBasedProtector(getGateKeeperService(),
LockscreenCredential.createNone(), sp, userId);
setCurrentLskfBasedProtectorId(protectorId, userId);
- setUserKeyProtection(userId, sp.deriveFileBasedEncryptionKey());
+ setCeStorageProtection(userId, sp);
onSyntheticPasswordCreated(userId, sp);
Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
return sp;
@@ -2824,9 +2833,9 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
- unlockKeystore(sp.deriveKeyStorePassword(), userId);
+ unlockKeystore(userId, sp);
- unlockUserKey(userId, sp);
+ unlockCeStorage(userId, sp);
unlockUser(userId);
@@ -2890,8 +2899,8 @@ public class LockSettingsService extends ILockSettings.Stub {
mSpManager.clearSidForUser(userId);
gateKeeperClearSecureUserId(userId);
- unlockUserKey(userId, sp);
- unlockKeystore(sp.deriveKeyStorePassword(), userId);
+ unlockCeStorage(userId, sp);
+ unlockKeystore(userId, sp);
setKeystorePassword(null, userId);
removeBiometricsForUser(userId);
}
@@ -3451,11 +3460,6 @@ public class LockSettingsService extends ILockSettings.Stub {
}
@Override
- public void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
- LockSettingsService.this.unlockUserKeyIfUnsecured(userId);
- }
-
- @Override
public void createNewUser(@UserIdInt int userId, int userSerialNumber) {
LockSettingsService.this.createNewUser(userId, userSerialNumber);
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index df95c69e7271..4bac872dbaa9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -174,7 +174,7 @@ class LockSettingsShellCommand extends ShellCommand {
pw.println(" Sets the lock screen as PIN, using the given PIN to unlock.");
pw.println("");
pw.println(" set-password [--old <CREDENTIAL>] [--user USER_ID] <PASSWORD>");
- pw.println(" Sets the lock screen as password, using the given PASSOWRD to unlock.");
+ pw.println(" Sets the lock screen as password, using the given PASSWORD to unlock.");
pw.println("");
pw.println(" clear [--old <CREDENTIAL>] [--user USER_ID]");
pw.println(" Clears the lock credentials.");
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 18f84c03d04f..1e8b387fc189 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -19,7 +19,7 @@ package com.android.server.locksettings;
import static android.content.Context.USER_SERVICE;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-import static com.android.internal.widget.LockPatternUtils.USER_FRP;
+import static com.android.internal.widget.LockPatternUtils.isSpecialUserId;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
@@ -90,6 +90,9 @@ class LockSettingsStorage {
private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
+ private static final String REPAIR_MODE_DIRECTORY = "repair-mode/";
+ private static final String REPAIR_MODE_PERSISTENT_FILE = "pst";
+
private static final Object DEFAULT = new Object();
private static final String[] SETTINGS_TO_BACKUP = new String[] {
@@ -390,6 +393,29 @@ class LockSettingsStorage {
}
}
+ @VisibleForTesting
+ File getRepairModePersistentDataFile() {
+ final File directory = new File(Environment.getMetadataDirectory(), REPAIR_MODE_DIRECTORY);
+ return new File(directory, REPAIR_MODE_PERSISTENT_FILE);
+ }
+
+ public PersistentData readRepairModePersistentData() {
+ final byte[] data = readFile(getRepairModePersistentDataFile());
+ if (data == null) {
+ return PersistentData.NONE;
+ }
+ return PersistentData.fromBytes(data);
+ }
+
+ public void writeRepairModePersistentData(int persistentType, int userId, byte[] payload) {
+ writeFile(getRepairModePersistentDataFile(),
+ PersistentData.toBytes(persistentType, userId, /* qualityForUi= */0, payload));
+ }
+
+ public void deleteRepairModePersistentData() {
+ deleteFile(getRepairModePersistentDataFile());
+ }
+
/**
* Writes the synthetic password state file for the given user ID, protector ID, and state name.
* If the file already exists, then it is atomically replaced.
@@ -510,7 +536,8 @@ class LockSettingsStorage {
}
public void setString(String key, String value, int userId) {
- Preconditions.checkArgument(userId != USER_FRP, "cannot store lock settings for FRP user");
+ Preconditions.checkArgument(!isSpecialUserId(userId),
+ "cannot store lock settings for special user: %d", userId);
writeKeyValue(key, value, userId);
if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) {
@@ -535,7 +562,7 @@ class LockSettingsStorage {
}
public String getString(String key, String defaultValue, int userId) {
- if (userId == USER_FRP) {
+ if (isSpecialUserId(userId)) {
return null;
}
return readKeyValue(key, defaultValue, userId);
@@ -583,6 +610,17 @@ class LockSettingsStorage {
}
}
+ /**
+ * Provides a concrete data structure to represent the minimal information from
+ * a user's LSKF-based SP protector that is needed to verify the user's LSKF,
+ * in combination with the corresponding Gatekeeper enrollment or Weaver slot.
+ * It can be stored in {@link com.android.server.PersistentDataBlockService} for
+ * FRP to live across factory resets not initiated via the Settings UI.
+ * Written to {@link #REPAIR_MODE_PERSISTENT_FILE} to support verification for
+ * exiting repair mode, since the device runs with an empty data partition in
+ * repair mode and the same credential be provided to exit repair mode is
+ * required.
+ */
public static class PersistentData {
static final byte VERSION_1 = 1;
static final int VERSION_1_HEADER_SIZE = 1 + 1 + 4 + 4;
@@ -685,6 +723,19 @@ class LockSettingsStorage {
}
pw.decreaseIndent();
}
+ // Dump repair mode file states
+ final File repairModeFile = getRepairModePersistentDataFile();
+ if (repairModeFile.exists()) {
+ pw.println(TextUtils.formatSimple("Repair Mode [%s]:", repairModeFile.getParent()));
+ pw.increaseIndent();
+ pw.println(TextUtils.formatSimple("%6d %s %s", repairModeFile.length(),
+ LockSettingsService.timestampToString(repairModeFile.lastModified()),
+ repairModeFile.getName()));
+ final PersistentData data = readRepairModePersistentData();
+ pw.println(TextUtils.formatSimple("type: %d, user id: %d, payload size: %d",
+ data.type, data.userId, data.payload != null ? data.payload.length : 0));
+ pw.decreaseIndent();
+ }
}
static class DatabaseHelper extends SQLiteOpenHelper {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 3d3703aa6b01..8e9c21f5f35f 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -16,9 +16,14 @@
package com.android.server.locksettings;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABLE;
+import static com.android.internal.widget.LockPatternUtils.USER_FRP;
+import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE;
+import static com.android.internal.widget.LockPatternUtils.pinOrPasswordQualityToCredentialType;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -768,11 +773,30 @@ class SyntheticPasswordManager {
return PasswordData.fromBytes(passwordData).credentialType;
}
- static int getFrpCredentialType(byte[] payload) {
- if (payload == null) {
+ int getSpecialUserCredentialType(int userId) {
+ final PersistentData data = getSpecialUserPersistentData(userId);
+ if (data.type != PersistentData.TYPE_SP_GATEKEEPER
+ && data.type != PersistentData.TYPE_SP_WEAVER) {
+ return CREDENTIAL_TYPE_NONE;
+ }
+ if (data.payload == null) {
return LockPatternUtils.CREDENTIAL_TYPE_NONE;
}
- return PasswordData.fromBytes(payload).credentialType;
+ final int credentialType = PasswordData.fromBytes(data.payload).credentialType;
+ if (credentialType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) {
+ return credentialType;
+ }
+ return pinOrPasswordQualityToCredentialType(data.qualityForUi);
+ }
+
+ private PersistentData getSpecialUserPersistentData(int userId) {
+ if (userId == USER_FRP) {
+ return mStorage.readPersistentDataBlock();
+ }
+ if (userId == USER_REPAIR_MODE) {
+ return mStorage.readRepairModePersistentData();
+ }
+ throw new IllegalArgumentException("Unknown special user id " + userId);
}
/**
@@ -1057,10 +1081,10 @@ class SyntheticPasswordManager {
return sizeOfCredential;
}
- public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
- LockscreenCredential userCredential,
+ public VerifyCredentialResponse verifySpecialUserCredential(int sourceUserId,
+ IGateKeeperService gatekeeper, LockscreenCredential userCredential,
ICheckCredentialProgressCallback progressCallback) {
- PersistentData persistentData = mStorage.readPersistentDataBlock();
+ final PersistentData persistentData = getSpecialUserPersistentData(sourceUserId);
if (persistentData.type == PersistentData.TYPE_SP_GATEKEEPER) {
PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
byte[] stretchedLskf = stretchLskf(userCredential, pwd);
@@ -1071,14 +1095,14 @@ class SyntheticPasswordManager {
0 /* challenge */, pwd.passwordHandle,
stretchedLskfToGkPassword(stretchedLskf));
} catch (RemoteException e) {
- Slog.e(TAG, "FRP verifyChallenge failed", e);
+ Slog.e(TAG, "Persistent data credential verifyChallenge failed", e);
return VerifyCredentialResponse.ERROR;
}
return VerifyCredentialResponse.fromGateKeeperResponse(response);
} else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
final IWeaver weaver = getWeaverService();
if (weaver == null) {
- Slog.e(TAG, "No weaver service to verify SP-based FRP credential");
+ Slog.e(TAG, "No weaver service to verify SP-based persistent data credential");
return VerifyCredentialResponse.ERROR;
}
PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
@@ -1168,6 +1192,57 @@ class SyntheticPasswordManager {
}
}
+ /**
+ * Writes the user's synthetic password data to the repair mode file.
+ *
+ * @param protectorId current LSKF based protectorId
+ * @param userId user id of the user
+ */
+ public boolean writeRepairModeCredentialLocked(long protectorId, int userId) {
+ if (!shouldWriteRepairModeCredential(userId)) {
+ return false;
+ }
+ final byte[] data = loadState(PASSWORD_DATA_NAME, protectorId, userId);
+ if (data == null) {
+ Slogf.w(TAG, "Password data not found for user %d", userId);
+ return false;
+ }
+ final PasswordData pwd = PasswordData.fromBytes(data);
+ if (isNoneCredential(pwd)) {
+ Slogf.w(TAG, "User %d has NONE credential", userId);
+ return false;
+ }
+ Slogf.d(TAG, "Writing repair mode credential tied to user %d", userId);
+ final int weaverSlot = loadWeaverSlot(protectorId, userId);
+ if (weaverSlot != INVALID_WEAVER_SLOT) {
+ // write weaver password
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_WEAVER, weaverSlot, pwd.toBytes());
+ } else {
+ // write gatekeeper password
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_GATEKEEPER, userId, pwd.toBytes());
+ }
+ return true;
+ }
+
+ private boolean shouldWriteRepairModeCredential(int userId) {
+ final UserInfo userInfo = mUserManager.getUserInfo(userId);
+ if (!LockPatternUtils.canUserEnterRepairMode(mContext, userInfo)) {
+ Slogf.w(TAG, "User %d can't enter repair mode", userId);
+ return false;
+ }
+ if (LockPatternUtils.isRepairModeActive(mContext)) {
+ Slog.w(TAG, "Can't write repair mode credential while repair mode is already active");
+ return false;
+ }
+ if (LockPatternUtils.isGsiRunning()) {
+ Slog.w(TAG, "Can't write repair mode credential while GSI is running");
+ return false;
+ }
+ return true;
+ }
+
private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>();
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index ec0d9856486d..77a60289d7a9 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -123,7 +123,7 @@ public class KeySyncTask implements Runnable {
* @param userId The uid of the user whose profile has been unlocked.
* @param credentialType The type of credential as defined in {@code LockPatternUtils}
* @param credential The credential, encoded as a byte array
- * @param credentialUpdated signals weather credentials were updated.
+ * @param credentialUpdated indicates credentials change.
* @param platformKeyManager platform key manager
* @param testOnlyInsecureCertificateHelper utility class used for end-to-end tests
*/
@@ -143,7 +143,7 @@ public class KeySyncTask implements Runnable {
mRecoverableKeyStoreDb = recoverableKeyStoreDb;
mUserId = userId;
mCredentialType = credentialType;
- mCredential = credential;
+ mCredential = credential != null ? Arrays.copyOf(credential, credential.length) : null;
mCredentialUpdated = credentialUpdated;
mPlatformKeyManager = platformKeyManager;
mRecoverySnapshotStorage = snapshotStorage;
@@ -160,6 +160,10 @@ public class KeySyncTask implements Runnable {
}
} catch (Exception e) {
Log.e(TAG, "Unexpected exception thrown during KeySyncTask", e);
+ } finally {
+ if (mCredential != null) {
+ Arrays.fill(mCredential, (byte) 0); // no longer needed.
+ }
}
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
index 7009a41726e2..ce3fb850b517 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java
@@ -166,6 +166,7 @@ public class PlatformKeyManager {
* @param userId The ID of the user to whose lock screen the platform key must be bound.
* @throws NoSuchAlgorithmException if AES is unavailable - should never happen.
* @throws KeyStoreException if there is an error in AndroidKeyStore.
+ * @throws InsecureUserException if the user does not have a lock screen set.
* @throws IOException if there was an issue with local database update.
* @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*
@@ -174,7 +175,7 @@ public class PlatformKeyManager {
@VisibleForTesting
void regenerate(int userId)
throws NoSuchAlgorithmException, KeyStoreException, IOException,
- RemoteException {
+ RemoteException, InsecureUserException {
int generationId = getGenerationId(userId);
int nextId;
if (generationId == -1) {
@@ -195,13 +196,14 @@ public class PlatformKeyManager {
* @throws UnrecoverableKeyException if the key could not be recovered.
* @throws NoSuchAlgorithmException if AES is unavailable - should never occur.
* @throws IOException if there was an issue with local database update.
+ * @throws InsecureUserException if the user does not have a lock screen set.
* @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*
* @hide
*/
public PlatformEncryptionKey getEncryptKey(int userId)
throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
- IOException, RemoteException {
+ IOException, RemoteException, InsecureUserException {
init(userId);
try {
// Try to see if the decryption key is still accessible before using the encryption key.
@@ -254,7 +256,7 @@ public class PlatformKeyManager {
*/
public PlatformDecryptionKey getDecryptKey(int userId)
throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException,
- IOException, RemoteException {
+ IOException, InsecureUserException, RemoteException {
init(userId);
try {
PlatformDecryptionKey decryptionKey = getDecryptKeyInternal(userId);
@@ -328,7 +330,7 @@ public class PlatformKeyManager {
*/
void init(int userId)
throws KeyStoreException, NoSuchAlgorithmException, IOException,
- RemoteException {
+ RemoteException, InsecureUserException {
int generationId = getGenerationId(userId);
if (isKeyLoaded(userId, generationId)) {
Log.i(TAG, String.format(
@@ -414,7 +416,8 @@ public class PlatformKeyManager {
* @throws RemoteException if there was an issue communicating with {@link IGateKeeperService}.
*/
private void generateAndLoadKey(int userId, int generationId)
- throws NoSuchAlgorithmException, KeyStoreException, IOException, RemoteException {
+ throws NoSuchAlgorithmException, KeyStoreException, IOException, RemoteException,
+ InsecureUserException {
String encryptAlias = getEncryptAlias(userId, generationId);
String decryptAlias = getDecryptAlias(userId, generationId);
// SecretKey implementation doesn't provide reliable way to destroy the secret
@@ -427,23 +430,31 @@ public class PlatformKeyManager {
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE);
// Skip UserAuthenticationRequired for main user
if (userId == UserHandle.USER_SYSTEM) {
+ // attempt to store key will fail if screenlock is not set.
decryptionKeyProtection.setUnlockedDeviceRequired(true);
} else {
// Don't set protection params to prevent losing key.
}
// Store decryption key first since it is more likely to fail.
- mKeyStore.setEntry(
- decryptAlias,
- new KeyStore.SecretKeyEntry(secretKey),
- decryptionKeyProtection.build());
- mKeyStore.setEntry(
- encryptAlias,
- new KeyStore.SecretKeyEntry(secretKey),
- new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
- .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
- .build());
-
+ try {
+ mKeyStore.setEntry(
+ decryptAlias,
+ new KeyStore.SecretKeyEntry(secretKey),
+ decryptionKeyProtection.build());
+ mKeyStore.setEntry(
+ encryptAlias,
+ new KeyStore.SecretKeyEntry(secretKey),
+ new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+ } catch (KeyStoreException e) {
+ if (!isDeviceSecure(userId)) {
+ throw new InsecureUserException("Screenlock is not set");
+ } else {
+ throw e;
+ }
+ }
setGenerationId(userId, generationId);
}
@@ -477,4 +488,8 @@ public class PlatformKeyManager {
return keyStore;
}
+ private boolean isDeviceSecure(int userId) {
+ return mContext.getSystemService(KeyguardManager.class).isDeviceSecure(userId);
+ }
+
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 24dbce49eace..10b6052bec69 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -19,6 +19,7 @@ package com.android.server.locksettings.recoverablekeystore;
import static android.security.keystore.recovery.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT;
import static android.security.keystore.recovery.RecoveryController.ERROR_DECRYPTION_FAILED;
import static android.security.keystore.recovery.RecoveryController.ERROR_DOWNGRADE_CERTIFICATE;
+import static android.security.keystore.recovery.RecoveryController.ERROR_INSECURE_USER;
import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE;
import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_KEY_FORMAT;
import static android.security.keystore.recovery.RecoveryController.ERROR_NO_SNAPSHOT_PENDING;
@@ -194,8 +195,12 @@ public class RecoverableKeyStoreManager {
mApplicationKeyStorage = applicationKeyStorage;
mTestCertHelper = testOnlyInsecureCertificateHelper;
mCleanupManager = cleanupManager;
- // Clears data for removed users.
- mCleanupManager.verifyKnownUsers();
+ try {
+ // Clears data for removed users.
+ mCleanupManager.verifyKnownUsers();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to verify known users", e);
+ }
try {
mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
} catch (NoSuchAlgorithmException e) {
@@ -750,6 +755,8 @@ public class RecoverableKeyStoreManager {
throw new RuntimeException(e);
} catch (KeyStoreException | UnrecoverableKeyException | IOException e) {
throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ } catch (InsecureUserException e) {
+ throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
}
try {
@@ -817,6 +824,8 @@ public class RecoverableKeyStoreManager {
throw new RuntimeException(e);
} catch (KeyStoreException | UnrecoverableKeyException | IOException e) {
throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
+ } catch (InsecureUserException e) {
+ throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
}
try {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
index 06db6b8a38f8..ef56a1e26854 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
@@ -138,7 +138,7 @@ public class ApplicationKeyStorage {
} catch (android.security.KeyStoreException e) {
if (e.getNumericErrorCode()
== android.security.KeyStoreException.ERROR_KEY_DOES_NOT_EXIST) {
- Log.e(TAG, "Failed to get grant for KeyStore key - key not found", e);
+ Log.w(TAG, "Failed to get grant for KeyStore key - key not found");
throw new ServiceSpecificException(ERROR_KEY_NOT_FOUND, e.getMessage());
}
Log.e(TAG, "Failed to get grant for KeyStore key.", e);
diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java
index 3e7024735b20..681d1a0ee10a 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -75,7 +75,6 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.HexDump;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.NetdUtils;
-import com.android.net.module.util.NetdUtils.ModifyOperation;
import com.android.net.module.util.PermissionUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -758,18 +757,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
}
}
- @Override
- public void addRoute(int netId, RouteInfo route) {
- PermissionUtils.enforceNetworkStackPermission(mContext);
- NetdUtils.modifyRoute(mNetdService, ModifyOperation.ADD, netId, route);
- }
-
- @Override
- public void removeRoute(int netId, RouteInfo route) {
- PermissionUtils.enforceNetworkStackPermission(mContext);
- NetdUtils.modifyRoute(mNetdService, ModifyOperation.REMOVE, netId, route);
- }
-
@android.annotation.EnforcePermission(android.Manifest.permission.SHUTDOWN)
@Override
public void shutdown() {
@@ -1072,17 +1059,25 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
Log.w(TAG, "setDataSaverMode(): already " + mDataSaverMode);
return true;
}
- Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "bandwidthEnableDataSaver");
+ Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setDataSaverModeEnabled");
try {
- final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
- if (changed) {
+ if (SdkLevel.isAtLeastV()) {
+ // setDataSaverEnabled throws if it fails to set data saver.
+ mContext.getSystemService(ConnectivityManager.class)
+ .setDataSaverEnabled(enable);
mDataSaverMode = enable;
+ return true;
} else {
- Log.w(TAG, "setDataSaverMode(" + enable + "): netd command silently failed");
+ final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
+ if (changed) {
+ mDataSaverMode = enable;
+ } else {
+ Log.e(TAG, "setDataSaverMode(" + enable + "): failed to set iptables");
+ }
+ return changed;
}
- return changed;
- } catch (RemoteException e) {
- Log.w(TAG, "setDataSaverMode(" + enable + "): netd command failed", e);
+ } catch (RemoteException | IllegalStateException e) {
+ Log.e(TAG, "setDataSaverMode(" + enable + "): failed with exception", e);
return false;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
diff --git a/services/core/java/com/android/server/notification/OWNERS b/services/core/java/com/android/server/notification/OWNERS
index 6c4dd6d13d92..9f16662fd749 100644
--- a/services/core/java/com/android/server/notification/OWNERS
+++ b/services/core/java/com/android/server/notification/OWNERS
@@ -1,6 +1,9 @@
-# Bug component: 34005
+# Bug component: 78010
juliacr@google.com
yurilin@google.com
+aroederer@google.com
+matiashe@google.com
+valiiftime@google.com
jeffdq@google.com
dsandler@android.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index f95f7bc0d165..0803bb662c70 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -122,7 +122,7 @@ public class AppDataHelper {
StorageManagerInternal.class);
for (UserInfo user : umInternal.getUsers(false /*excludeDying*/)) {
final int flags;
- if (StorageManager.isUserKeyUnlocked(user.id)
+ if (StorageManager.isCeStorageUnlocked(user.id)
&& smInternal.isCeStoragePrepared(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
@@ -404,7 +404,7 @@ public class AppDataHelper {
// First look for stale data that doesn't belong, and check if things
// have changed since we did our last restorecon
if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
- if (StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId)) {
+ if (StorageManager.isFileEncrypted() && !StorageManager.isCeStorageUnlocked(userId)) {
throw new RuntimeException(
"Yikes, someone asked us to reconcile CE storage while " + userId
+ " was still locked; this would have caused massive data loss!");
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 78f1fa60b69f..723a3ae659c9 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -3538,7 +3538,7 @@ public class ComputerEngine implements Computer {
@Override
public int getPackageStartability(boolean safeMode, @NonNull String packageName, int callingUid,
@UserIdInt int userId) {
- final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId);
+ final boolean ceStorageUnlocked = StorageManager.isCeStorageUnlocked(userId);
final PackageStateInternal ps = getPackageStateInternal(packageName);
if (ps == null || shouldFilterApplication(ps, callingUid, userId)
|| !ps.getUserStateOrDefault(userId).isInstalled()) {
@@ -3553,7 +3553,7 @@ public class ComputerEngine implements Computer {
return PackageManagerService.PACKAGE_STARTABILITY_FROZEN;
}
- if (!userKeyUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.getPkg())) {
+ if (!ceStorageUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.getPkg())) {
return PackageManagerService.PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED;
}
return PackageManagerService.PACKAGE_STARTABILITY_OK;
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 622cb6609630..988a32f3ffc7 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -114,6 +114,7 @@ import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.DataLoaderType;
+import android.content.pm.Flags;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
@@ -167,6 +168,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemConfig;
+import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.DexoptParams;
import com.android.server.art.model.DexoptResult;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
@@ -484,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;
}
/**
@@ -2534,8 +2546,15 @@ final class InstallPackageHelper {
LocalManagerRegistry.getManager(PackageManagerLocal.class);
try (PackageManagerLocal.FilteredSnapshot snapshot =
packageManagerLocal.withFilteredSnapshot()) {
- DexoptParams params =
- dexoptOptions.convertToDexoptParams(0 /* extraFlags */);
+ boolean ignoreDexoptProfile =
+ (installRequest.getInstallFlags()
+ & PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE)
+ != 0;
+ /*@DexoptFlags*/ int extraFlags =
+ ignoreDexoptProfile && Flags.useArtServiceV2()
+ ? ArtFlags.FLAG_IGNORE_PROFILE
+ : 0;
+ DexoptParams params = dexoptOptions.convertToDexoptParams(extraFlags);
DexoptResult dexOptResult = DexOptHelper.getArtManagerLocal().dexoptPackage(
snapshot, packageName, params);
installRequest.onDexoptFinished(dexOptResult);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 9ac983dfebeb..c401dab3a482 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageStats;
+import android.os.Binder;
import android.os.Build;
import android.os.CreateAppDataArgs;
import android.os.CreateAppDataResult;
@@ -34,9 +35,11 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.storage.CrateMetadata;
import android.text.format.DateUtils;
+import android.util.EventLog;
import android.util.Slog;
import com.android.internal.os.BackgroundThread;
+import com.android.server.EventLogTags;
import com.android.server.SystemService;
import dalvik.system.BlockGuard;
@@ -438,6 +441,26 @@ public class Installer extends SystemService {
if (!checkBeforeRemote()) return;
try {
mInstalld.clearAppData(uuid, packageName, userId, flags, ceDataInode);
+
+ final StackTraceElement[] elements = Thread.currentThread().getStackTrace();
+ String className;
+ String methodName;
+ String fileName;
+ int lineNumber;
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ EventLog.writeEvent(EventLogTags.INSTALLER_CLEAR_APP_DATA_CALLER, pid, uid, packageName,
+ flags);
+ // Skip the first two elements since they are always the same, ie
+ // Thread#getStackTrace() and VMStack#getThreadStackTrace()
+ for (int i = 2; i < elements.length; i++) {
+ className = elements[i].getClassName();
+ methodName = elements[i].getMethodName();
+ fileName = elements[i].getFileName();
+ lineNumber = elements[i].getLineNumber();
+ EventLog.writeEvent(EventLogTags.INSTALLER_CLEAR_APP_DATA_CALL_STACK, methodName,
+ className, fileName, lineNumber);
+ }
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 481bd975fef5..4780ed1950b8 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -89,6 +89,21 @@ public final class MovePackageHelper {
if (packageState == null || packageState.getPkg() == null) {
throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
}
+ final int[] installedUserIds = PackageStateUtils.queryInstalledUsers(packageState,
+ mPm.mUserManager.getUserIds(), true);
+ final UserHandle userForMove;
+ if (installedUserIds.length > 0) {
+ userForMove = UserHandle.of(installedUserIds[0]);
+ } else {
+ throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST,
+ "Package is not installed for any user");
+ }
+ for (int userId : installedUserIds) {
+ if (snapshot.shouldFilterApplicationIncludingUninstalled(packageState, callingUid,
+ userId)) {
+ throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
+ }
+ }
final AndroidPackage pkg = packageState.getPkg();
if (packageState.isSystem()) {
throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
@@ -137,8 +152,6 @@ public final class MovePackageHelper {
final String label = String.valueOf(pm.getApplicationLabel(
AndroidPackageUtils.generateAppInfoWithoutState(pkg)));
final int targetSdkVersion = pkg.getTargetSdkVersion();
- final int[] installedUserIds = PackageStateUtils.queryInstalledUsers(packageState,
- mPm.mUserManager.getUserIds(), true);
final String fromCodePath;
if (codeFile.getParentFile().getName().startsWith(
PackageManagerService.RANDOM_DIR_PREFIX)) {
@@ -185,7 +198,8 @@ public final class MovePackageHelper {
// If we're moving app data around, we need all the users unlocked
if (moveCompleteApp) {
for (int userId : installedUserIds) {
- if (StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId)) {
+ if (StorageManager.isFileEncrypted()
+ && !StorageManager.isCeStorageUnlocked(userId)) {
freezer.close();
throw new PackageManagerException(MOVE_FAILED_LOCKED_USER,
"User " + userId + " must be unlocked");
@@ -305,8 +319,8 @@ public final class MovePackageHelper {
final PackageLite lite = ret.isSuccess() ? ret.getResult() : null;
final InstallingSession installingSession = new InstallingSession(origin, move,
installObserver, installFlags, /* developmentInstallFlags= */ 0, installSource,
- volumeUuid, user, packageAbiOverride, PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED,
- lite, mPm);
+ volumeUuid, userForMove, packageAbiOverride,
+ PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED, lite, mPm);
installingSession.movePackage();
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 6a2ddc8f94b0..ea082cf77987 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -159,6 +159,9 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
if (pkgSetting.getPkg().isCoreApp()) {
throw new IllegalStateException("Found a core app that's not important");
}
+ // Use REASON_FIRST_BOOT to query "pm.dexopt.first-boot" for the compiler filter, but
+ // the reason itself won't make it into the actual compiler reason because it will be
+ // overridden in otapreopt.cpp.
mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.getPkg(), pkgSetting,
PackageManagerService.REASON_FIRST_BOOT));
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 20bd56d56945..a1c435af0452 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3460,7 +3460,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
UserManagerInternal umInternal = mInjector.getUserManagerInternal();
StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
final int flags;
- if (StorageManager.isUserKeyUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
+ if (StorageManager.isCeStorageUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(userId)) {
flags = StorageManager.FLAG_STORAGE_DE;
@@ -4729,6 +4729,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
throw new SecurityException("Cannot clear data for a protected package: "
+ packageName);
}
+ final int callingPid = Binder.getCallingPid();
+ EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid,
+ packageName);
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
@@ -4861,6 +4864,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
/* checkShell= */ false, "delete application cache files");
final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.ACCESS_INSTANT_APPS);
+ final int callingPid = Binder.getCallingPid();
+ EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid,
+ packageName);
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2327b85ed0bb..3a84e1505475 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3368,6 +3368,9 @@ class PackageManagerShellCommand extends ShellCommand {
sessionParams.installFlags |=
PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK;
break;
+ case "--ignore-dexopt-profile":
+ sessionParams.installFlags |= PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE;
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
@@ -4259,7 +4262,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" [--enable-rollback]");
pw.println(" [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
pw.println(" [--apex] [--non-staged] [--force-non-staged]");
- pw.println(" [--staged-ready-timeout TIMEOUT]");
+ pw.println(" [--staged-ready-timeout TIMEOUT] [--ignore-dexopt-profile]");
pw.println(" [PATH [SPLIT...]|-]");
pw.println(" Install an application. Must provide the apk data to install, either as");
pw.println(" file path(s) or '-' to read from stdin. Options are:");
@@ -4299,6 +4302,13 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" milliseconds for pre-reboot verification to complete when");
pw.println(" performing staged install. This flag is used to alter the waiting");
pw.println(" time. You can skip the waiting time by specifying a TIMEOUT of '0'");
+ pw.println(" --ignore-dexopt-profile: If set, all profiles are ignored by dexopt");
+ pw.println(" during the installation, including the profile in the DM file and");
+ pw.println(" the profile embedded in the APK file. If an invalid profile is");
+ pw.println(" provided during installation, no warning will be reported by `adb");
+ pw.println(" install`.");
+ pw.println(" This option does not affect later dexopt operations (e.g.,");
+ pw.println(" background dexopt and manual `pm compile` invocations).");
pw.println("");
pw.println(" install-existing [--user USER_ID|all|current]");
pw.println(" [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 8f8f4376d2cc..89619232eb04 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -183,7 +183,7 @@ public final class StorageEventHelper extends StorageEventListener {
StorageManagerInternal.class);
for (UserInfo user : mPm.mUserManager.getUsers(false /* includeDying */)) {
final int flags;
- if (StorageManager.isUserKeyUnlocked(user.id)
+ if (StorageManager.isCeStorageUnlocked(user.id)
&& smInternal.isCeStoragePrepared(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
} else if (umInternal.isUserRunning(user.id)) {
diff --git a/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java b/services/core/java/com/android/server/pm/UpdateOwnershipHelper.java
index 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/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7e88e13e1788..e11e226054e3 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1348,7 +1348,7 @@ public class UserManagerService extends IUserManager.Stub {
}
final boolean needToShowConfirmCredential = !dontAskCredential
&& mLockPatternUtils.isSecure(userId)
- && (!hasUnifiedChallenge || !StorageManager.isUserKeyUnlocked(userId));
+ && (!hasUnifiedChallenge || !StorageManager.isCeStorageUnlocked(userId));
if (needToShowConfirmCredential) {
if (onlyIfCredentialNotRequired) {
return false;
@@ -4905,9 +4905,9 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- t.traceBegin("createUserKey");
+ t.traceBegin("createUserStorageKeys");
final StorageManager storage = mContext.getSystemService(StorageManager.class);
- storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
+ storage.createUserStorageKeys(userId, userInfo.serialNumber, userInfo.isEphemeral());
t.traceEnd();
// Only prepare DE storage here. CE storage will be prepared later, when the user is
@@ -5731,17 +5731,18 @@ public class UserManagerService extends IUserManager.Stub {
private void removeUserState(final @UserIdInt int userId) {
Slog.i(LOG_TAG, "Removing user state of user " + userId);
- // Cleanup lock settings. This must happen before destroyUserKey(), since the user's DE
- // storage must still be accessible for the lock settings state to be properly cleaned up.
+ // Cleanup lock settings. This requires that the user's DE storage still be accessible, so
+ // this must happen before destroyUserStorageKeys().
mLockPatternUtils.removeUser(userId);
// Evict and destroy the user's CE and DE encryption keys. At this point, the user's CE and
// DE storage is made inaccessible, except to delete its contents.
try {
- mContext.getSystemService(StorageManager.class).destroyUserKey(userId);
+ mContext.getSystemService(StorageManager.class).destroyUserStorageKeys(userId);
} catch (IllegalStateException e) {
// This may be simply because the user was partially created.
- Slog.i(LOG_TAG, "Destroying key for user " + userId + " failed, continuing anyway", e);
+ Slog.i(LOG_TAG, "Destroying storage keys for user " + userId
+ + " failed, continuing anyway", e);
}
// Cleanup package manager settings
@@ -6991,9 +6992,9 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mUserStates) {
state = mUserStates.get(userId, -1);
}
- // Special case, in the stopping/shutdown state user key can still be unlocked
+ // Special case: in the stopping/shutdown state, CE storage can still be unlocked.
if (state == UserState.STATE_STOPPING || state == UserState.STATE_SHUTDOWN) {
- return StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isCeStorageUnlocked(userId);
}
return (state == UserState.STATE_RUNNING_UNLOCKING)
|| (state == UserState.STATE_RUNNING_UNLOCKED);
@@ -7010,9 +7011,9 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mUserStates) {
state = mUserStates.get(userId, -1);
}
- // Special case, in the stopping/shutdown state user key can still be unlocked
+ // Special case: in the stopping/shutdown state, CE storage can still be unlocked.
if (state == UserState.STATE_STOPPING || state == UserState.STATE_SHUTDOWN) {
- return StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isCeStorageUnlocked(userId);
}
return state == UserState.STATE_RUNNING_UNLOCKED;
}
diff --git a/services/core/java/com/android/server/pm/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..1970ee4b4463 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -3,3 +3,5 @@ santoscordon@google.com
philipjunker@google.com
per-file ThermalManagerService.java=wvw@google.com
+per-file LowPowerStandbyController.java=qingxun@google.com
+per-file LowPowerStandbyControllerInternal.java=qingxun@google.com \ No newline at end of file
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 7b0fe9a9abc7..a01bac688fbf 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -266,10 +266,10 @@ public class AppDataRollbackHelper {
}
/**
- * @return {@code true} iff. {@code userId} is locked on an FBE device.
+ * @return {@code true} iff the credential-encrypted storage for {@code userId} is locked.
*/
@VisibleForTesting
public boolean isUserCredentialLocked(int userId) {
- return StorageManager.isFileEncrypted() && !StorageManager.isUserKeyUnlocked(userId);
+ return StorageManager.isFileEncrypted() && !StorageManager.isCeStorageUnlocked(userId);
}
}
diff --git a/services/core/java/com/android/server/stats/OWNERS b/services/core/java/com/android/server/stats/OWNERS
index 174ad3ad2e25..c33f3d9fa264 100644
--- a/services/core/java/com/android/server/stats/OWNERS
+++ b/services/core/java/com/android/server/stats/OWNERS
@@ -1,11 +1,10 @@
jeffreyhuang@google.com
joeo@google.com
-jtnguyen@google.com
+monicamwang@google.com
muhammadq@google.com
+rayhdez@google.com
rslawik@google.com
-ruchirr@google.com
sharaienko@google.com
singhtejinder@google.com
tsaichristine@google.com
yaochen@google.com
-yro@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 9128974fa9d3..76ee84571b04 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1281,12 +1281,19 @@ public class StatsPullAtomService extends SystemService {
private void addBytesTransferByTagAndMeteredAtoms(@NonNull NetworkStatsExt statsExt,
@NonNull List<StatsEvent> pulledData) {
+ // Workaround for 5G NSA mode, see {@link NetworkStatsManager#NETWORK_TYPE_5G_NSA}.
+ // 5G NSA mode means the primary cell is LTE with a secondary connection to an
+ // NR cell. To mitigate risk, NetworkStats is currently storing this state as
+ // a fake RAT type rather than storing the boolean separately.
+ final boolean is5GNsa = statsExt.ratType == NetworkStatsManager.NETWORK_TYPE_5G_NSA;
+
for (NetworkStats.Entry entry : statsExt.stats) {
pulledData.add(FrameworkStatsLog.buildStatsEvent(
FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.getUid(),
entry.getMetered() == NetworkStats.METERED_YES, entry.getTag(),
entry.getRxBytes(), entry.getRxPackets(), entry.getTxBytes(),
- entry.getTxPackets()));
+ entry.getTxPackets(),
+ is5GNsa ? TelephonyManager.NETWORK_TYPE_LTE : statsExt.ratType));
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java
index d958222ea407..9213d96ad4ca 100644
--- a/services/core/java/com/android/server/vcn/VcnContext.java
+++ b/services/core/java/com/android/server/vcn/VcnContext.java
@@ -18,6 +18,8 @@ package com.android.server.vcn;
import android.annotation.NonNull;
import android.content.Context;
+import android.net.vcn.FeatureFlags;
+import android.net.vcn.FeatureFlagsImpl;
import android.os.Looper;
import java.util.Objects;
@@ -31,6 +33,7 @@ public class VcnContext {
@NonNull private final Context mContext;
@NonNull private final Looper mLooper;
@NonNull private final VcnNetworkProvider mVcnNetworkProvider;
+ @NonNull private final FeatureFlags mFeatureFlags;
private final boolean mIsInTestMode;
public VcnContext(
@@ -42,6 +45,9 @@ public class VcnContext {
mLooper = Objects.requireNonNull(looper, "Missing looper");
mVcnNetworkProvider = Objects.requireNonNull(vcnNetworkProvider, "Missing networkProvider");
mIsInTestMode = isInTestMode;
+
+ // Auto-generated class
+ mFeatureFlags = new FeatureFlagsImpl();
}
@NonNull
@@ -63,6 +69,11 @@ public class VcnContext {
return mIsInTestMode;
}
+ @NonNull
+ public FeatureFlags getFeatureFlags() {
+ return mFeatureFlags;
+ }
+
/**
* Verifies that the caller is running on the VcnContext Thread.
*
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index d480ddb092eb..54c97dd37941 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1222,6 +1222,14 @@ public class VcnGatewayConnection extends StateMachine {
@VisibleForTesting(visibility = Visibility.PRIVATE)
void setSafeModeAlarm() {
+ final boolean isFlagSafeModeConfigEnabled = mVcnContext.getFeatureFlags().safeModeConfig();
+ logVdbg("isFlagSafeModeConfigEnabled " + isFlagSafeModeConfigEnabled);
+
+ if (isFlagSafeModeConfigEnabled && !mConnectionConfig.isSafeModeEnabled()) {
+ logVdbg("setSafeModeAlarm: safe mode disabled");
+ return;
+ }
+
logVdbg("Setting safe mode alarm; mCurrentToken: " + mCurrentToken);
// Only schedule a NEW alarm if none is already set.
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 9cf08340f613..749cd8e55bb7 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -731,18 +731,24 @@ final class VibrationSettings {
private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
public boolean isUidForeground(int uid) {
- return mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
- <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ synchronized (this) {
+ return mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
+ <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ }
}
@Override
public void onUidGone(int uid, boolean disabled) {
- mProcStatesCache.delete(uid);
+ synchronized (this) {
+ mProcStatesCache.delete(uid);
+ }
}
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
- mProcStatesCache.put(uid, procState);
+ synchronized (this) {
+ mProcStatesCache.put(uid, procState);
+ }
}
}
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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7addaa265ab2..1c8770b123bb 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4668,6 +4668,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
scheduleAnimation();
mWmService.mH.post(() -> InputMethodManagerInternal.get().onImeParentChanged());
+ } else if (mImeControlTarget != null && mImeControlTarget == mImeLayeringTarget) {
+ // Even if the IME surface parent is not changed, the layer target belonging to the
+ // parent may have changes. Then attempt to reassign if the IME control target is
+ // possible to be the relative layer.
+ final SurfaceControl lastRelativeLayer = mImeWindowsContainer.getLastRelativeLayer();
+ if (lastRelativeLayer != mImeLayeringTarget.mSurfaceControl) {
+ assignRelativeLayerForIme(getSyncTransaction(), false /* forceUpdate */);
+ if (lastRelativeLayer != mImeWindowsContainer.getLastRelativeLayer()) {
+ scheduleAnimation();
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index f6c364008c62..cd704478aa83 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -16,6 +16,10 @@ lihongyu@google.com
mariiasand@google.com
rgl@google.com
yunfanc@google.com
+wilsonshih@google.com
+
+# Files related to background activity launches
+per-file Background*Start* = set noparent
+per-file Background*Start* = file:/BAL_OWNERS
+per-file Background*Start* = ogunwale@google.com, louischang@google.com
-per-file BackgroundActivityStartController.java = set noparent
-per-file BackgroundActivityStartController.java = brufino@google.com, topjohnwu@google.com, achim@google.com, ogunwale@google.com, louischang@google.com, lus@google.com
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 9a0e47de7873..eba9bf669d47 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1637,8 +1637,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return false;
}
- if (!StorageManager.isUserKeyUnlocked(mCurrentUser)) {
- // Can't launch home on secondary display areas if device is still locked.
+ if (!StorageManager.isCeStorageUnlocked(mCurrentUser)) {
+ // Can't launch home on secondary display areas if CE storage is still locked.
return false;
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3063d46208c2..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");
}
@@ -6510,11 +6510,11 @@ class Task extends TaskFragment {
mActivityType = ACTIVITY_TYPE_STANDARD;
}
- if (mActivityType != ACTIVITY_TYPE_STANDARD
+ if (!DisplayContent.alwaysCreateRootTask(tda.getWindowingMode(), mActivityType)
&& mActivityType != ACTIVITY_TYPE_UNDEFINED) {
- // For now there can be only one root task of a particular non-standard activity
- // type on a display. So, get that ignoring whatever windowing mode it is
- // currently in.
+ // Only Recents or Standard activity types are allowed to have more than one
+ // root task on a display, this is independent of whatever windowing mode it
+ // is currently in.
Task rootTask = tda.getRootTask(WINDOWING_MODE_UNDEFINED, mActivityType);
if (rootTask != null) {
throw new IllegalArgumentException("Root task=" + rootTask + " of activityType="
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 0152666a830d..4cab3a29f136 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2635,6 +2635,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return mLastLayer;
}
+ SurfaceControl getLastRelativeLayer() {
+ return mLastRelativeToLayer;
+ }
+
protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
if (mSurfaceFreezer.hasLeash()) {
// When the freezer has created animation leash parent for the window, set the layer
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 10687001f9a8..d4a88c7dd6d7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5206,7 +5206,11 @@ public class WindowManagerService extends IWindowManager.Stub
public void displayReady() {
synchronized (mGlobalLock) {
if (mMaxUiWidth > 0) {
- mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth));
+ mRoot.forAllDisplays(dc -> {
+ if (dc.mDisplay.getType() == Display.TYPE_INTERNAL) {
+ dc.setMaxUiWidth(mMaxUiWidth);
+ }
+ });
}
applyForcedPropertiesForDefaultDisplay();
mAnimator.ready();
diff --git a/services/midi/OWNERS b/services/midi/OWNERS
index f4d51f91b51b..683cae1b0f3a 100644
--- a/services/midi/OWNERS
+++ b/services/midi/OWNERS
@@ -1 +1,3 @@
philburk@google.com
+robertwu@google.com
+elaurent@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/proguard.flags b/services/proguard.flags
index 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/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index c6a914b37206..ccbaf78bfc95 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -2530,7 +2530,7 @@ public class MockingOomAdjusterTests {
PROCESS_STATE_NONEXISTENT, PROCESS_STATE_NONEXISTENT,
0, 0, false, false, false, ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE,
false, false, false, hasShownUi, false, false, false, false, false, false, null,
- 0, 0, 0, true, 0, null, false);
+ 0, Long.MIN_VALUE, Long.MIN_VALUE, true, 0, null, false);
}
private ProcessRecord makeProcessRecord(ActivityManagerService service, int pid, int uid,
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/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index f770f8c4571e..e656cf30e8e8 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -65,6 +65,7 @@ android_test {
"ActivityContext",
"coretests-aidl",
"securebox",
+ "flag-junit",
],
libs: [
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index dccacb4d301a..a3403bb3ec39 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -345,7 +345,7 @@ public class UserControllerTest {
assertWithMessage("should not have received intents")
.that(getActions(mInjector.mSentIntents)).isEmpty();
// TODO(b/140868593): should have received a USER_UNLOCK_MSG message as well, but it doesn't
- // because StorageManager.isUserKeyUnlocked(TEST_PRE_CREATED_USER_ID) returns false - to
+ // because StorageManager.isCeStorageUnlocked(TEST_PRE_CREATED_USER_ID) returns false - to
// properly fix it, we'd need to move this class to FrameworksMockingServicesTests so we can
// mock static methods (but moving this class would involve changing the presubmit tests,
// and the cascade effect goes on...). In fact, a better approach would to not assert the
@@ -648,7 +648,7 @@ public class UserControllerTest {
// checking.
waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
verify(mInjector.mStorageManagerMock, times(0))
- .lockUserKey(anyInt());
+ .lockCeStorage(anyInt());
addForegroundUserAndContinueUserSwitch(TEST_USER_ID2, TEST_USER_ID1,
numerOfUserSwitches, true);
@@ -663,7 +663,7 @@ public class UserControllerTest {
mUserController.finishUserStopped(ussUser1, /* allowDelayedLocking= */ true);
waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
verify(mInjector.mStorageManagerMock, times(1))
- .lockUserKey(TEST_USER_ID);
+ .lockCeStorage(TEST_USER_ID);
}
/**
@@ -757,7 +757,7 @@ public class UserControllerTest {
mUserController.startUser(TEST_USER_ID, USER_START_MODE_BACKGROUND);
verify(mInjector.mStorageManagerMock, never())
- .unlockUserKey(eq(TEST_USER_ID), anyInt(), any());
+ .unlockCeStorage(eq(TEST_USER_ID), anyInt(), any());
}
@Test
@@ -1035,7 +1035,7 @@ public class UserControllerTest {
mUserController.finishUserStopped(ussUser, delayedLocking);
waitForHandlerToComplete(FgThread.getHandler(), HANDLER_WAIT_TIME_MS);
verify(mInjector.mStorageManagerMock, times(expectLocking ? 1 : 0))
- .lockUserKey(userId);
+ .lockCeStorage(userId);
}
private void addForegroundUserAndContinueUserSwitch(int newUserId, int expectedOldUserId,
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 31599eed539d..aba24fbd55b7 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -29,13 +29,14 @@ import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.BluetoothProfileConnectionInfo;
import android.util.Log;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
@@ -54,7 +55,6 @@ public class AudioDeviceBrokerTest {
private static final String TAG = "AudioDeviceBrokerTest";
private static final int MAX_MESSAGE_HANDLING_DELAY_MS = 100;
- private Context mContext;
// the actual class under test
private AudioDeviceBroker mAudioDeviceBroker;
@@ -67,13 +67,13 @@ public class AudioDeviceBrokerTest {
@Before
public void setUp() throws Exception {
- mContext = InstrumentationRegistry.getTargetContext();
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
mMockAudioService = mock(AudioService.class);
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
mSpyDevInventory = spy(new AudioDeviceInventory(mSpyAudioSystem));
mSpySystemServer = spy(new NoOpSystemServerAdapter());
- mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory,
+ mAudioDeviceBroker = new AudioDeviceBroker(context, mMockAudioService, mSpyDevInventory,
mSpySystemServer, mSpyAudioSystem);
mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker);
@@ -197,6 +197,37 @@ public class AudioDeviceBrokerTest {
any(Intent.class));
}
+ /**
+ * Test that constructing an AdiDeviceState instance requires a non-null address for a
+ * wireless type, but can take null for a non-wireless type;
+ * @throws Exception
+ */
+ @Test
+ public void testAdiDeviceStateNullAddressCtor() throws Exception {
+ try {
+ new AdiDeviceState(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+ AudioManager.DEVICE_OUT_SPEAKER, null);
+ new AdiDeviceState(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP, null);
+ Assert.fail();
+ } catch (NullPointerException e) { }
+ }
+
+ @Test
+ public void testAdiDeviceStateStringSerialization() throws Exception {
+ Log.i(TAG, "starting testAdiDeviceStateStringSerialization");
+ final AdiDeviceState devState = new AdiDeviceState(
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, AudioManager.DEVICE_OUT_SPEAKER, "bla");
+ devState.setHasHeadTracker(false);
+ devState.setHeadTrackerEnabled(false);
+ devState.setSAEnabled(true);
+ final String persistString = devState.toPersistableString();
+ final AdiDeviceState result = AdiDeviceState.fromPersistedString(persistString);
+ Log.i(TAG, "original:" + devState);
+ Log.i(TAG, "result :" + result);
+ Assert.assertEquals(devState, result);
+ }
+
private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection,
boolean mockMediaPlayback, boolean guaranteeSingleConnection) throws Exception {
when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC))
diff --git a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
index 3ad24de4cdca..ad09ef0ccdc1 100644
--- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
@@ -15,8 +15,6 @@
*/
package com.android.server.audio;
-import com.android.server.audio.SpatializerHelper.SADeviceState;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doNothing;
@@ -26,12 +24,12 @@ import static org.mockito.Mockito.when;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
-import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioSystem;
import android.util.Log;
import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Assert;
@@ -55,72 +53,25 @@ public class SpatializerHelperTest {
@Mock private AudioService mMockAudioService;
@Spy private AudioSystemAdapter mSpyAudioSystem;
- @Mock private AudioSystemAdapter mMockAudioSystem;
+ @Spy private AudioDeviceBroker mSpyDeviceBroker;
@Before
public void setUp() throws Exception {
mMockAudioService = mock(AudioService.class);
- }
-
- /**
- * Initializes mSpatHelper, the SpatizerHelper instance under test, to use the mock or spy
- * AudioSystemAdapter
- * @param useSpyAudioSystem true to use the spy adapter, mSpyAudioSystem, or false to use
- * the mock adapter, mMockAudioSystem.
- */
- private void setUpSpatHelper(boolean useSpyAudioSystem) {
- final AudioSystemAdapter asAdapter;
- if (useSpyAudioSystem) {
- mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
- asAdapter = mSpyAudioSystem;
- mMockAudioSystem = null;
- } else {
- mSpyAudioSystem = null;
- mMockAudioSystem = mock(NoOpAudioSystemAdapter.class);
- asAdapter = mMockAudioSystem;
- }
- mSpatHelper = new SpatializerHelper(mMockAudioService, asAdapter,
- true /*binauralEnabledDefault*/,
- true /*transauralEnabledDefault*/,
- false /*headTrackingEnabledDefault*/);
-
- }
- /**
- * Test that constructing an SADeviceState instance requires a non-null address for a
- * wireless type, but can take null for a non-wireless type;
- * @throws Exception
- */
- @Test
- public void testSADeviceStateNullAddressCtor() throws Exception {
- setUpSpatHelper(true /*useSpyAudioSystem*/);
- try {
- SADeviceState devState = new SADeviceState(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null);
- devState = new SADeviceState(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, null);
- Assert.fail();
- } catch (NullPointerException e) { }
- }
-
- @Test
- public void testSADeviceStateStringSerialization() throws Exception {
- Log.i(TAG, "starting testSADeviceStateStringSerialization");
- setUpSpatHelper(true /*useSpyAudioSystem*/);
- final SADeviceState devState = new SADeviceState(
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "bla");
- devState.mHasHeadTracker = false;
- devState.mHeadTrackerEnabled = false;
- devState.mEnabled = true;
- final String persistString = devState.toPersistableString();
- final SADeviceState result = SADeviceState.fromPersistedString(persistString);
- Log.i(TAG, "original:" + devState);
- Log.i(TAG, "result :" + result);
- Assert.assertEquals(devState, result);
+ mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
+ mSpyDeviceBroker = spy(
+ new AudioDeviceBroker(
+ InstrumentationRegistry.getInstrumentation().getTargetContext(),
+ mMockAudioService, mSpyAudioSystem));
+ mSpatHelper = new SpatializerHelper(mMockAudioService, mSpyAudioSystem,
+ mSpyDeviceBroker, /*binauralEnabledDefault=*/true, /*transauralEnabledDefault=*/
+ true, /*headTrackingEnabledDefault*/false);
}
@Test
- public void testSADeviceSettings() throws Exception {
+ public void testAdiDeviceStateSettings() throws Exception {
Log.i(TAG, "starting testSADeviceSettings");
- setUpSpatHelper(true /*useSpyAudioSystem*/);
final AudioDeviceAttributes dev1 =
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, "");
final AudioDeviceAttributes dev2 =
@@ -128,7 +79,7 @@ public class SpatializerHelperTest {
final AudioDeviceAttributes dev3 =
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "R2:D2:bloop");
- doNothing().when(mMockAudioService).persistSpatialAudioDeviceSettings();
+ doNothing().when(mSpyDeviceBroker).persistAudioDeviceSettings();
mSpatHelper.initForTest(true /*binaural*/, true /*transaural*/);
// test with single device
@@ -163,11 +114,11 @@ public class SpatializerHelperTest {
* the original one.
*/
private void checkAddSettings() throws Exception {
- String settings = mSpatHelper.getSADeviceSettings();
+ String settings = mSpyDeviceBroker.getDeviceSettings();
Log.i(TAG, "device settings: " + settings);
- mSpatHelper.clearSADevices();
- mSpatHelper.setSADeviceSettings(settings);
- String settingsRestored = mSpatHelper.getSADeviceSettings();
+ mSpyDeviceBroker.clearDeviceInventory();
+ mSpyDeviceBroker.setDeviceSettings(settings);
+ String settingsRestored = mSpyDeviceBroker.getDeviceSettings();
Log.i(TAG, "device settingsRestored: " + settingsRestored);
Assert.assertEquals(settings, settingsRestored);
}
@@ -179,7 +130,6 @@ public class SpatializerHelperTest {
@Test
public void testNoRoutingCanBeSpatialized() throws Exception {
Log.i(TAG, "Starting testNoRoutingCanBeSpatialized");
- setUpSpatHelper(false /*useSpyAudioSystem*/);
mSpatHelper.forceStateForTest(SpatializerHelper.STATE_ENABLED_AVAILABLE);
final ArrayList<AudioDeviceAttributes> emptyList = new ArrayList<>(0);
@@ -191,12 +141,12 @@ public class SpatializerHelperTest {
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1).build();
- when(mMockAudioSystem.getDevicesForAttributes(any(AudioAttributes.class), anyBoolean()))
+ when(mSpyAudioSystem.getDevicesForAttributes(any(AudioAttributes.class), anyBoolean()))
.thenReturn(emptyList);
Assert.assertFalse("can be spatialized on empty routing",
mSpatHelper.canBeSpatialized(media, spatialFormat));
- when(mMockAudioSystem.getDevicesForAttributes(any(AudioAttributes.class), anyBoolean()))
+ when(mSpyAudioSystem.getDevicesForAttributes(any(AudioAttributes.class), anyBoolean()))
.thenReturn(listWithNull);
Assert.assertFalse("can be spatialized on null routing",
mSpatHelper.canBeSpatialized(media, spatialFormat));
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index f88afe7839c3..a78f2dcf2ab2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -41,6 +41,8 @@ import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -53,6 +55,7 @@ import android.hardware.iris.IIrisService;
import android.os.Binder;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -84,6 +87,8 @@ public class AuthServiceTest {
@Rule
public MockitoRule mockitorule = MockitoJUnit.rule();
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private Context mContext;
@Mock
@@ -418,6 +423,37 @@ public class AuthServiceTest {
eq(callback));
}
+ @Test(expected = UnsupportedOperationException.class)
+ public void testGetLastAuthenticationTime_flaggedOff_throwsUnsupportedOperationException()
+ throws Exception {
+ mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+ setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ mAuthService.mImpl.getLastAuthenticationTime(0,
+ BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void testGetLastAuthenticationTime_flaggedOn_callsBiometricService()
+ throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+ setInternalAndTestBiometricPermissions(mContext, true /* hasPermission */);
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final int userId = 0;
+ final int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG;
+
+ mAuthService.mImpl.getLastAuthenticationTime(userId, authenticators);
+
+ waitForIdle();
+ verify(mBiometricService).getLastAuthenticationTime(eq(userId), eq(authenticators));
+ }
+
private static void setInternalAndTestBiometricPermissions(
Context context, boolean hasPermission) {
for (String p : List.of(TEST_BIOMETRIC, MANAGE_BIOMETRIC, USE_BIOMETRIC_INTERNAL)) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 662477ddbbe9..2aabb1bc24b2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -104,6 +104,7 @@ public class AuthSessionTest {
@Mock private KeyStore mKeyStore;
@Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver;
@Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger;
+ @Mock private BiometricCameraManager mBiometricCameraManager;
private Random mRandom;
private IBinder mToken;
@@ -571,7 +572,8 @@ public class AuthSessionTest {
promptInfo,
TEST_PACKAGE,
checkDevicePolicyManager,
- mContext);
+ mContext,
+ mBiometricCameraManager);
}
private AuthSession createAuthSession(List<BiometricSensor> sensors,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 67be37616d5f..14a567a08165 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -18,7 +18,6 @@ package com.android.server.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricManager.Authenticators;
-import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTHENTICATED_PENDING_SYSUI;
@@ -61,6 +60,7 @@ import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.IBiometricAuthenticator;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.IBiometricSensorReceiver;
@@ -70,12 +70,17 @@ import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.keymaster.HardwareAuthenticatorType;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.security.GateKeeper;
import android.security.KeyStore;
+import android.security.authorization.IKeystoreAuthorization;
+import android.service.gatekeeper.IGateKeeperService;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.WindowManager;
@@ -91,6 +96,7 @@ import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.LockoutTracker;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.AdditionalMatchers;
import org.mockito.ArgumentCaptor;
@@ -104,6 +110,9 @@ import java.util.Random;
@SmallTest
public class BiometricServiceTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private static final String TEST_PACKAGE_NAME = "test_package";
private static final long TEST_REQUEST_ID = 44;
@@ -152,11 +161,19 @@ public class BiometricServiceTest {
private AuthSessionCoordinator mAuthSessionCoordinator;
@Mock
private UserManager mUserManager;
+ @Mock
+ private BiometricCameraManager mBiometricCameraManager;
+
+ @Mock
+ private IKeystoreAuthorization mKeystoreAuthService;
+
+ @Mock
+ private IGateKeeperService mGateKeeperService;
BiometricContextProvider mBiometricContextProvider;
@Before
- public void setUp() {
+ public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
resetReceivers();
@@ -178,6 +195,7 @@ public class BiometricServiceTest {
when(mInjector.getDevicePolicyManager(any())).thenReturn(mDevicePolicyManager);
when(mInjector.getRequestGenerator()).thenReturn(() -> TEST_REQUEST_ID);
when(mInjector.getUserManager(any())).thenReturn(mUserManager);
+ when(mInjector.getBiometricCameraManager(any())).thenReturn(mBiometricCameraManager);
when(mResources.getString(R.string.biometric_error_hw_unavailable))
.thenReturn(ERROR_HW_UNAVAILABLE);
@@ -193,6 +211,9 @@ public class BiometricServiceTest {
mStatusBarService, null /* handler */,
mAuthSessionCoordinator);
when(mInjector.getBiometricContext(any())).thenReturn(mBiometricContextProvider);
+ when(mInjector.getKeystoreAuthorizationService()).thenReturn(mKeystoreAuthService);
+ when(mInjector.getGateKeeperService()).thenReturn(mGateKeeperService);
+ when(mGateKeeperService.getSecureUserId(anyInt())).thenReturn(42L);
final String[] config = {
"0:2:15", // ID0:Fingerprint:Strong
@@ -1609,6 +1630,44 @@ public class BiometricServiceTest {
verifyNoMoreInteractions(callback);
}
+ @Test(expected = UnsupportedOperationException.class)
+ public void testGetLastAuthenticationTime_flagOff_throwsUnsupportedOperationException()
+ throws RemoteException {
+ mSetFlagsRule.disableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+
+ mBiometricService = new BiometricService(mContext, mInjector);
+ mBiometricService.mImpl.getLastAuthenticationTime(0, Authenticators.BIOMETRIC_STRONG);
+ }
+
+ @Test
+ public void testGetLastAuthenticationTime_flagOn_callsKeystoreAuthorization()
+ throws RemoteException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_LAST_AUTHENTICATION_TIME);
+
+ final int[] hardwareAuthenticators = new int[] {
+ HardwareAuthenticatorType.PASSWORD,
+ HardwareAuthenticatorType.FINGERPRINT
+ };
+
+ final int userId = 0;
+ final long secureUserId = mGateKeeperService.getSecureUserId(userId);
+
+ assertNotEquals(GateKeeper.INVALID_SECURE_USER_ID, secureUserId);
+
+ final long expectedResult = 31337L;
+
+ when(mKeystoreAuthService.getLastAuthTime(eq(secureUserId), eq(hardwareAuthenticators)))
+ .thenReturn(expectedResult);
+
+ mBiometricService = new BiometricService(mContext, mInjector);
+
+ final long result = mBiometricService.mImpl.getLastAuthenticationTime(userId,
+ Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL);
+
+ assertEquals(expectedResult, result);
+ verify(mKeystoreAuthService).getLastAuthTime(eq(secureUserId), eq(hardwareAuthenticators));
+ }
+
// Helper methods
private int invokeCanAuthenticate(BiometricService service, int authenticators)
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
new file mode 100644
index 000000000000..c2bdf501198e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+
+import static com.android.server.biometrics.sensors.LockoutTracker.LOCKOUT_NONE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.trust.ITrustManager;
+import android.content.Context;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.IBiometricAuthenticator;
+import android.hardware.biometrics.PromptInfo;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+
+@Presubmit
+@SmallTest
+public class PreAuthInfoTest {
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+ private static final int SENSOR_ID_FACE = 1;
+ private static final String TEST_PACKAGE_NAME = "PreAuthInfoTestPackage";
+
+ @Mock
+ IBiometricAuthenticator mFaceAuthenticator;
+ @Mock
+ Context mContext;
+ @Mock
+ ITrustManager mTrustManager;
+ @Mock
+ DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ BiometricService.SettingObserver mSettingObserver;
+ @Mock
+ BiometricCameraManager mBiometricCameraManager;
+
+ @Before
+ public void setup() throws RemoteException {
+ when(mTrustManager.isDeviceSecure(anyInt(), anyInt())).thenReturn(true);
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt()))
+ .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
+ when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true);
+ when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true);
+ when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
+ when(mFaceAuthenticator.getLockoutModeForUser(anyInt()))
+ .thenReturn(LOCKOUT_NONE);
+ when(mBiometricCameraManager.isCameraPrivacyEnabled()).thenReturn(false);
+ when(mBiometricCameraManager.isAnyCameraUnavailable()).thenReturn(false);
+ }
+
+ @Test
+ public void testFaceAuthentication_whenCameraPrivacyIsEnabled() throws Exception {
+ when(mBiometricCameraManager.isCameraPrivacyEnabled()).thenReturn(true);
+
+ BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FACE, TYPE_FACE,
+ BiometricManager.Authenticators.BIOMETRIC_STRONG, mFaceAuthenticator) {
+ @Override
+ boolean confirmationAlwaysRequired(int userId) {
+ return false;
+ }
+
+ @Override
+ boolean confirmationSupported() {
+ return false;
+ }
+ };
+ PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setConfirmationRequested(false /* requireConfirmation */);
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */);
+ PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(sensor),
+ 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+
+ assertThat(preAuthInfo.eligibleSensors).isEmpty();
+ }
+
+ @Test
+ public void testFaceAuthentication_whenCameraPrivacyIsDisabledAndCameraIsAvailable()
+ throws Exception {
+ BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FACE, TYPE_FACE,
+ BiometricManager.Authenticators.BIOMETRIC_STRONG, mFaceAuthenticator) {
+ @Override
+ boolean confirmationAlwaysRequired(int userId) {
+ return false;
+ }
+
+ @Override
+ boolean confirmationSupported() {
+ return false;
+ }
+ };
+ PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setConfirmationRequested(false /* requireConfirmation */);
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */);
+ PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(sensor),
+ 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+
+ assertThat(preAuthInfo.eligibleSensors).hasSize(1);
+ }
+
+ @Test
+ public void testFaceAuthentication_whenCameraIsUnavailable() throws RemoteException {
+ when(mBiometricCameraManager.isAnyCameraUnavailable()).thenReturn(true);
+ BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FACE, TYPE_FACE,
+ BiometricManager.Authenticators.BIOMETRIC_STRONG, mFaceAuthenticator) {
+ @Override
+ boolean confirmationAlwaysRequired(int userId) {
+ return false;
+ }
+
+ @Override
+ boolean confirmationSupported() {
+ return false;
+ }
+ };
+ PromptInfo promptInfo = new PromptInfo();
+ promptInfo.setConfirmationRequested(false /* requireConfirmation */);
+ promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
+ promptInfo.setDisallowBiometricsIfPolicyExists(false /* checkDevicePolicy */);
+ PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+ mSettingObserver, List.of(sensor),
+ 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+ false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+
+ assertThat(preAuthInfo.eligibleSensors).hasSize(0);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index e960e995f6ce..f5d50d173466 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -254,6 +254,8 @@ public abstract class BaseLockSettingsServiceTests {
.thenReturn(true);
when(res.getBoolean(eq(com.android.internal.R.bool.config_strongAuthRequiredOnBoot)))
.thenReturn(true);
+ when(res.getBoolean(eq(com.android.internal.R.bool.config_repairModeSupported)))
+ .thenReturn(true);
return res;
}
@@ -302,17 +304,17 @@ public abstract class BaseLockSettingsServiceTests {
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
- mStorageManager.unlockUserKey(/* userId= */ (int) args[0],
+ mStorageManager.unlockCeStorage(/* userId= */ (int) args[0],
/* secret= */ (byte[]) args[2]);
return null;
- }).when(sm).unlockUserKey(anyInt(), anyInt(), any());
+ }).when(sm).unlockCeStorage(anyInt(), anyInt(), any());
doAnswer(invocation -> {
Object[] args = invocation.getArguments();
- mStorageManager.setUserKeyProtection(/* userId= */ (int) args[0],
+ mStorageManager.setCeStorageProtection(/* userId= */ (int) args[0],
/* secret= */ (byte[]) args[1]);
return null;
- }).when(sm).setUserKeyProtection(anyInt(), any());
+ }).when(sm).setCeStorageProtection(anyInt(), any());
return sm;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java
index 91f3fed01267..c08ad134d74a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java
@@ -24,7 +24,7 @@ public class FakeStorageManager {
private final ArrayMap<Integer, byte[]> mUserSecrets = new ArrayMap<>();
- public void setUserKeyProtection(int userId, byte[] secret) {
+ public void setCeStorageProtection(int userId, byte[] secret) {
assertThat(mUserSecrets).doesNotContainKey(userId);
mUserSecrets.put(userId, secret);
}
@@ -35,7 +35,7 @@ public class FakeStorageManager {
return secret;
}
- public void unlockUserKey(int userId, byte[] secret) {
+ public void unlockCeStorage(int userId, byte[] secret) {
assertThat(mUserSecrets.get(userId)).isEqualTo(secret);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index cfb3b3497745..fa3c7a4c4769 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -84,6 +84,11 @@ public class LockSettingsStorageTestable extends LockSettingsStorage {
}
@Override
+ File getRepairModePersistentDataFile() {
+ return remapToStorageDir(super.getRepairModePersistentDataFile());
+ }
+
+ @Override
PersistentDataBlockManagerInternal getPersistentDataBlockManager() {
return mPersistentDataBlockManager;
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 39e2792a3a34..02b86db6ab6f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -457,6 +457,31 @@ public class LockSettingsStorageTests {
assertEquals(2, PersistentData.TYPE_SP_WEAVER);
}
+ @Test
+ public void testRepairMode_emptyPersistentData() {
+ assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock());
+ }
+
+ @Test
+ public void testRepairMode_writeGatekeeperPersistentData() {
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_GATEKEEPER, SOME_USER_ID, PAYLOAD);
+
+ final PersistentData data = mStorage.readRepairModePersistentData();
+ assertEquals(PersistentData.TYPE_SP_GATEKEEPER, data.type);
+ assertArrayEquals(PAYLOAD, data.payload);
+ }
+
+ @Test
+ public void testRepairMode_writeWeaverPersistentData() {
+ mStorage.writeRepairModePersistentData(
+ PersistentData.TYPE_SP_WEAVER, SOME_USER_ID, PAYLOAD);
+
+ final PersistentData data = mStorage.readRepairModePersistentData();
+ assertEquals(PersistentData.TYPE_SP_WEAVER, data.type);
+ assertArrayEquals(PAYLOAD, data.payload);
+ }
+
private static void assertArrayEquals(byte[] expected, byte[] actual) {
if (!Arrays.equals(expected, actual)) {
fail("expected:<" + Arrays.toString(expected) +
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java
new file mode 100644
index 000000000000..70150c507460
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE;
+import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.app.PropertyInvalidatedCache;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.locksettings.LockSettingsStorage.PersistentData;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LockscreenRepairModeTest extends BaseLockSettingsServiceTests {
+
+ @Before
+ public void setUp() throws Exception {
+ PropertyInvalidatedCache.disableForTestMode();
+ mService.initializeSyntheticPassword(PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void verifyPin_writeRepairModePW() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN,
+ mService.getCredentialType(USER_REPAIR_MODE));
+ }
+
+ @Test
+ public void verifyPattern_writeRepairModePW() {
+ mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPattern("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ mService.getCredentialType(USER_REPAIR_MODE));
+ }
+
+ @Test
+ public void verifyPassword_writeRepairModePW() {
+ mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPassword("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ mService.getCredentialType(USER_REPAIR_MODE));
+ }
+
+ @Test
+ public void verifyCredential_writeRepairModePW_repairModeActive() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+
+ setRepairModeActive(true);
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ }
+
+ @Test
+ public void deleteRepairModePersistentData() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN,
+ mService.getCredentialType(USER_REPAIR_MODE));
+
+ mService.deleteRepairModePersistentDataIfNeeded();
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ }
+
+ @Test
+ public void verifyPin_userRepairMode() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ setRepairModeActive(true);
+
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN,
+ mService.getCredentialType(USER_REPAIR_MODE));
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(newPin("1234"), USER_REPAIR_MODE, 0 /* flags */)
+ .getResponseCode());
+ }
+
+ @Test
+ public void verifyPattern_userRepairMode() {
+ mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPattern("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ setRepairModeActive(true);
+
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+ mService.getCredentialType(USER_REPAIR_MODE));
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(newPattern("4321"), USER_REPAIR_MODE, 0 /* flags */)
+ .getResponseCode());
+ }
+
+ @Test
+ public void verifyPassword_userRepairMode() {
+ mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPassword("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ setRepairModeActive(true);
+
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+ mService.getCredentialType(USER_REPAIR_MODE));
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(newPassword("4321"), USER_REPAIR_MODE, 0 /* flags */)
+ .getResponseCode());
+ }
+
+ @Test
+ public void verifyCredential_userRepairMode_repairModeIsNotActive() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN,
+ mService.getCredentialType(USER_REPAIR_MODE));
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
+ mService.verifyCredential(newPin("1234"), USER_REPAIR_MODE, 0 /* flags */)
+ .getResponseCode());
+ }
+
+ @Test
+ public void verifyCredential_userRepairMode_wrongPin() {
+ mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+ assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData());
+ assertEquals(VerifyCredentialResponse.RESPONSE_OK,
+ mService.verifyCredential(
+ newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW)
+ .getResponseCode());
+ setRepairModeActive(true);
+
+ assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN,
+ mService.getCredentialType(USER_REPAIR_MODE));
+ assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
+ mService.verifyCredential(newPin("5678"), USER_REPAIR_MODE, 0 /* flags */)
+ .getResponseCode());
+ }
+
+ private void setRepairModeActive(boolean active) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.REPAIR_MODE_ACTIVE, active ? 1 : 0);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 37a6d22f038b..eca19c8e8c4d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -255,7 +255,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
public void testUnlockUserKeyIfUnsecuredPassesPrimaryUserAuthSecret() throws RemoteException {
initSpAndSetCredential(PRIMARY_USER_ID, newPassword(null));
reset(mAuthSecretService);
- mLocalService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(any(byte[].class));
}
@@ -267,7 +267,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID);
reset(mAuthSecretService);
- mLocalService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(any(byte[].class));
}
@@ -285,39 +285,39 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
@Test
public void testHeadlessSystemUserDoesNotPassAuthSecret() throws RemoteException {
setupHeadlessTest();
- mLocalService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(PRIMARY_USER_ID);
verify(mAuthSecretService, never()).setPrimaryUserCredential(any(byte[].class));
}
@Test
public void testHeadlessSecondaryUserPassesAuthSecret() throws RemoteException {
setupHeadlessTest();
- mLocalService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(any(byte[].class));
}
@Test
public void testHeadlessTertiaryUserPassesSameAuthSecret() throws RemoteException {
setupHeadlessTest();
- mLocalService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
var captor = ArgumentCaptor.forClass(byte[].class);
verify(mAuthSecretService).setPrimaryUserCredential(captor.capture());
var value = captor.getValue();
reset(mAuthSecretService);
- mLocalService.unlockUserKeyIfUnsecured(TERTIARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(TERTIARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(eq(value));
}
@Test
public void testHeadlessTertiaryUserPassesSameAuthSecretAfterReset() throws RemoteException {
setupHeadlessTest();
- mLocalService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(SECONDARY_USER_ID);
var captor = ArgumentCaptor.forClass(byte[].class);
verify(mAuthSecretService).setPrimaryUserCredential(captor.capture());
var value = captor.getValue();
mService.clearAuthSecret();
reset(mAuthSecretService);
- mLocalService.unlockUserKeyIfUnsecured(TERTIARY_USER_ID);
+ mService.unlockUserKeyIfUnsecured(TERTIARY_USER_ID);
verify(mAuthSecretService).setPrimaryUserCredential(eq(value));
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 5654c89196f2..80fb5e3f950d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -36,6 +36,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -297,13 +298,15 @@ public class KeySyncTaskTest {
TestData.getInsecureCertPathForEndpoint1());
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias);
+ setExpectedScryptArgument(password.getBytes());
+
mKeySyncTask.run();
KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(UI_FORMAT_PASSWORD);
- verify(mMockScrypt).scrypt(eq(password.getBytes()), any(),
+ verify(mMockScrypt).scrypt(any(), any(),
eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
KeyDerivationParams keyDerivationParams =
@@ -314,6 +317,44 @@ public class KeySyncTaskTest {
}
@Test
+ public void run_zeroizedCredential() throws Exception {
+ String password = TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "123";
+ String appKeyAlias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "alias";
+ byte[] zeroizedCredential = password.getBytes();
+ mKeySyncTask = new KeySyncTask(
+ mRecoverableKeyStoreDb,
+ mRecoverySnapshotStorage,
+ mSnapshotListenersStorage,
+ TEST_USER_ID,
+ CREDENTIAL_TYPE_PASSWORD,
+ /*credential=*/ zeroizedCredential,
+ /*credentialUpdated=*/ false,
+ mPlatformKeyManager,
+ mTestOnlyInsecureCertificateHelper,
+ mMockScrypt);
+ mRecoverableKeyStoreDb.setServerParams(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+ mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+ mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+ TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+ TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS,
+ TestData.getInsecureCertPathForEndpoint1());
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, appKeyAlias);
+
+ // Need to check array value during method call since it is modified later.
+ setExpectedScryptArgument(password.getBytes());
+
+ Arrays.fill(zeroizedCredential, (byte) 0);
+ mKeySyncTask.run();
+
+ verify(mMockScrypt).scrypt(any(), any(),
+ eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
+ eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
+ }
+
+ @Test
public void run_useSha256ToHashPatternInProdMode() throws Exception {
String pattern = "123456";
mKeySyncTask = new KeySyncTask(
@@ -368,13 +409,15 @@ public class KeySyncTaskTest {
mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+ setExpectedScryptArgument(shortPassword.getBytes());
+
mKeySyncTask.run();
KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(UI_FORMAT_PASSWORD);
- verify(mMockScrypt).scrypt(eq(shortPassword.getBytes()), any(),
+ verify(mMockScrypt).scrypt(any(), any(),
eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
KeyDerivationParams keyDerivationParams =
@@ -650,13 +693,15 @@ public class KeySyncTaskTest {
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ setExpectedScryptArgument(password.getBytes());
+
mKeySyncTask.run();
KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(UI_FORMAT_PASSWORD);
- verify(mMockScrypt).scrypt(eq(password.getBytes()), any(),
+ verify(mMockScrypt).scrypt(any(), any(),
eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
}
@@ -681,6 +726,8 @@ public class KeySyncTaskTest {
when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+ setExpectedScryptArgument(pin.getBytes());
+
mKeySyncTask.run();
KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
@@ -688,7 +735,7 @@ public class KeySyncTaskTest {
// Password with only digits is changed to pin.
assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
isEqualTo(UI_FORMAT_PIN);
- verify(mMockScrypt).scrypt(eq(pin.getBytes()), any(),
+ verify(mMockScrypt).scrypt(any(), any(),
eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
}
@@ -868,4 +915,14 @@ public class KeySyncTaskTest {
new Random().nextBytes(bytes);
return bytes;
}
+
+ private void setExpectedScryptArgument(byte[] credentials) {
+ doAnswer(invocation -> {
+ assertThat((byte[]) invocation.getArguments()[0]).isEqualTo(credentials);
+ return invocation.callRealMethod();
+ }).when(mMockScrypt).scrypt(any(), any(),
+ eq(KeySyncTask.SCRYPT_PARAM_N), eq(KeySyncTask.SCRYPT_PARAM_R),
+ eq(KeySyncTask.SCRYPT_PARAM_P), eq(KeySyncTask.SCRYPT_PARAM_OUTLEN_BYTES));
+
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
index c546a741e76a..c09e09c8404f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/PlatformKeyManagerTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
import android.app.KeyguardManager;
import android.content.Context;
@@ -54,6 +55,7 @@ import org.mockito.MockitoAnnotations;
import java.io.File;
import java.security.KeyStore;
+import java.security.KeyStoreException;
import java.security.UnrecoverableKeyException;
import java.util.List;
@@ -393,6 +395,18 @@ public class PlatformKeyManagerTest {
}
@Test
+ public void getEncryptKey_noScreenlock() throws Exception {
+ when(mKeyguardManager.isDeviceSecure(USER_ID_FIXTURE)).thenReturn(false);
+ doThrow(new KeyStoreException()).when(mKeyStoreProxy).setEntry(
+ anyString(),
+ any(),
+ any());
+
+ assertThrows(InsecureUserException.class,
+ () -> mPlatformKeyManager.getEncryptKey(USER_ID_FIXTURE));
+ }
+
+ @Test
public void getDecryptKey_generatesNewKeyIfOldOneIsInvalid() throws Exception {
doThrow(new UnrecoverableKeyException()).when(mKeyStoreProxy).getKey(
eq(DECRYPTION_KEY_ALIAS_1),
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 af144cf49a46..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,6 +57,7 @@ import android.util.ArrayMap;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
+import com.android.modules.utils.build.SdkLevel;
import org.junit.After;
import org.junit.Before;
@@ -263,7 +264,11 @@ public class NetworkManagementServiceTest {
verify(mCm).addUidToMeteredNetworkDenyList(TEST_UID);
mNMService.setDataSaverModeEnabled(true);
- verify(mNetdService).bandwidthEnableDataSaver(true);
+ if (SdkLevel.isAtLeastV()) {
+ verify(mCm).setDataSaverEnabled(true);
+ } else {
+ verify(mNetdService).bandwidthEnableDataSaver(true);
+ }
mNMService.setUidOnMeteredNetworkDenylist(TEST_UID, false);
assertTrue("Should be true since data saver is on and the uid is not allowlisted",
@@ -279,7 +284,11 @@ public class NetworkManagementServiceTest {
mNMService.setUidOnMeteredNetworkAllowlist(TEST_UID, false);
verify(mCm).removeUidFromMeteredNetworkAllowList(TEST_UID);
mNMService.setDataSaverModeEnabled(false);
- verify(mNetdService).bandwidthEnableDataSaver(false);
+ if (SdkLevel.isAtLeastV()) {
+ verify(mCm).setDataSaverEnabled(false);
+ } else {
+ verify(mNetdService).bandwidthEnableDataSaver(false);
+ }
assertFalse("Network should not be restricted when data saver is off",
mNMService.isNetworkRestricted(TEST_UID));
}
diff --git a/services/tests/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/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 3bc6450ae591..b92cc64194e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -870,7 +870,7 @@ public class RootWindowContainerTests extends WindowTestsBase {
new TestDisplayContent.Builder(mAtm, 1000, 1500)
.setSystemDecorations(true).build();
- // Use invalid user id to let StorageManager.isUserKeyUnlocked() return false.
+ // Use invalid user id to let StorageManager.isCeStorageUnlocked() return false.
final int currentUser = mRootWindowContainer.mCurrentUser;
mRootWindowContainer.mCurrentUser = -1;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 3ec6f425a37a..973ab846fa4a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -448,7 +448,6 @@ public class ZOrderingTests extends WindowTestsBase {
mDisplayContent.updateImeParent();
// Ime should on top of the popup IME layering target window.
- mDisplayContent.assignChildLayers(mTransaction);
assertWindowHigher(mImeWindow, popupImeTargetWin);
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 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/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a72f7806d3ea..89ef523c2c71 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2352,6 +2352,11 @@ public class TelecomManager {
* <p>
* <b>Note</b>: {@link android.app.Notification.CallStyle} notifications should be posted after
* the call is placed in order for the notification to be non-dismissible.
+ * <p><b>Note</b>: Call Forwarding MMI codes can only be dialed by applications that are
+ * configured as the user defined default dialer or system dialer role. If a call containing a
+ * call forwarding MMI code is placed by an application that is not in one of these roles, the
+ * dialer will be launched with a UI showing the MMI code already populated so that the user can
+ * confirm the action before the call is placed.
* @param address The address to make the call to.
* @param extras Bundle of extras to use with the call.
*/
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 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/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
index 29152f19d17d..7b6a201796a6 100644
--- a/telephony/java/android/telephony/BarringInfo.java
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -202,6 +202,24 @@ public final class BarringInfo implements Parcelable {
&& mConditionalBarringTimeSeconds == other.mConditionalBarringTimeSeconds;
}
+ private static String barringTypeToString(@BarringType int barringType) {
+ return switch (barringType) {
+ case BARRING_TYPE_NONE -> "NONE";
+ case BARRING_TYPE_CONDITIONAL -> "CONDITIONAL";
+ case BARRING_TYPE_UNCONDITIONAL -> "UNCONDITIONAL";
+ case BARRING_TYPE_UNKNOWN -> "UNKNOWN";
+ default -> "UNKNOWN(" + barringType + ")";
+ };
+ }
+
+ @Override
+ public String toString() {
+ return "BarringServiceInfo {mBarringType=" + barringTypeToString(mBarringType)
+ + ", mIsConditionallyBarred=" + mIsConditionallyBarred
+ + ", mConditionalBarringFactor=" + mConditionalBarringFactor
+ + ", mConditionalBarringTimeSeconds=" + mConditionalBarringTimeSeconds + "}";
+ }
+
/** @hide */
public BarringServiceInfo(Parcel p) {
mBarringType = p.readInt();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 749ed03b727f..43dab5338e93 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8770,7 +8770,9 @@ public class CarrierConfigManager {
* {@link android.net.ipsec.ike.SaProposal#DH_GROUP_NONE},
* {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1024_BIT_MODP},
* {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1536_BIT_MODP},
- * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_2048_BIT_MODP}
+ * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_2048_BIT_MODP},
+ * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_3072_BIT_MODP},
+ * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_4096_BIT_MODP}
*/
public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY =
KEY_PREFIX + "diffie_hellman_groups_int_array";
@@ -8838,7 +8840,8 @@ public class CarrierConfigManager {
/**
* List of supported encryption algorithms for child session. Possible values are
- * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC}
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
+ * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
*/
public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array";
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index 241e69106718..daaab3314679 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android.test.mock {
@Deprecated public class MockAccountManager {
@@ -158,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);
@@ -181,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);
}
@@ -202,7 +202,7 @@ package android.test.mock {
method @Deprecated public int checkPermission(String, String);
method @Deprecated public int checkSignatures(String, String);
method @Deprecated public int checkSignatures(int, int);
- method public void clearInstantAppCookie();
+ method @Deprecated public void clearInstantAppCookie();
method @Deprecated public void clearPackagePreferredActivities(String);
method @Deprecated public String[] currentToCanonicalPackageNames(String[]);
method @Deprecated public void extendVerificationTimeout(int, int, long);
@@ -224,15 +224,15 @@ package android.test.mock {
method @Deprecated public CharSequence getApplicationLabel(android.content.pm.ApplicationInfo);
method @Deprecated public android.graphics.drawable.Drawable getApplicationLogo(android.content.pm.ApplicationInfo);
method @Deprecated public android.graphics.drawable.Drawable getApplicationLogo(String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.pm.ChangedPackages getChangedPackages(int);
+ method @Deprecated public android.content.pm.ChangedPackages getChangedPackages(int);
method @Deprecated public int getComponentEnabledSetting(android.content.ComponentName);
method @Deprecated public android.graphics.drawable.Drawable getDefaultActivityIcon();
method @Deprecated public android.graphics.drawable.Drawable getDrawable(String, int, android.content.pm.ApplicationInfo);
method @Deprecated public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method @Deprecated public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method @Deprecated public String getInstallerPackageName(String);
- method public byte[] getInstantAppCookie();
- method public int getInstantAppCookieMaxBytes();
+ method @Deprecated public byte[] getInstantAppCookie();
+ method @Deprecated public int getInstantAppCookieMaxBytes();
method @Deprecated public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public android.content.Intent getLaunchIntentForPackage(String);
method @Deprecated public android.content.Intent getLeanbackLaunchIntentForPackage(String);
@@ -241,7 +241,7 @@ package android.test.mock {
method @Deprecated public int[] getPackageGids(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public android.content.pm.PackageInfo getPackageInfo(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public android.content.pm.PackageInfo getPackageInfo(android.content.pm.VersionedPackage, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public android.content.pm.PackageInstaller getPackageInstaller();
+ method @Deprecated public android.content.pm.PackageInstaller getPackageInstaller();
method @Deprecated public int getPackageUid(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @Deprecated public String[] getPackagesForUid(int);
method @Deprecated public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(String[], int);
@@ -265,8 +265,8 @@ package android.test.mock {
method @Deprecated public android.content.res.XmlResourceParser getXml(String, int, android.content.pm.ApplicationInfo);
method @Deprecated public boolean hasSystemFeature(String);
method @Deprecated public boolean hasSystemFeature(String, int);
- method public boolean isInstantApp();
- method public boolean isInstantApp(String);
+ method @Deprecated public boolean isInstantApp();
+ method @Deprecated public boolean isInstantApp(String);
method @Deprecated public boolean isPermissionRevokedByPolicy(String, String);
method @Deprecated public boolean isSafeMode();
method @Deprecated public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -283,11 +283,12 @@ package android.test.mock {
method @Deprecated public android.content.pm.ProviderInfo resolveContentProvider(String, int);
method @Deprecated public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
method @Deprecated public android.content.pm.ResolveInfo resolveServiceAsUser(android.content.Intent, int, int);
- method public void setApplicationCategoryHint(String, int);
+ method @Deprecated public void setApplicationCategoryHint(String, int);
method @Deprecated public void setApplicationEnabledSetting(String, int, int);
method @Deprecated public void setComponentEnabledSetting(android.content.ComponentName, int, int);
method @Deprecated public void setInstallerPackageName(String, String);
- method public void updateInstantAppCookie(@NonNull byte[]);
+ method @Deprecated public boolean setInstantAppCookie(@NonNull byte[]);
+ method @Deprecated public void updateInstantAppCookie(@NonNull byte[]);
method @Deprecated public void verifyPendingInstall(int, int);
}
diff --git a/test-mock/api/removed.txt b/test-mock/api/removed.txt
index fa2fbd276e9a..0c800b6f82b5 100644
--- a/test-mock/api/removed.txt
+++ b/test-mock/api/removed.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android.test.mock {
public class MockContext extends android.content.Context {
@@ -11,7 +9,7 @@ package android.test.mock {
@Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
method @Deprecated public String getDefaultBrowserPackageName(int);
method @Deprecated public boolean setDefaultBrowserPackageName(String, int);
- method public boolean setInstantAppCookie(@NonNull byte[]);
+ method @Deprecated public boolean setInstantAppCookie(@NonNull byte[]);
}
}
diff --git a/test-mock/api/system-current.txt b/test-mock/api/system-current.txt
index 2b5132ecd14f..f35095743738 100644
--- a/test-mock/api/system-current.txt
+++ b/test-mock/api/system-current.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android.test.mock {
public class MockContext extends android.content.Context {
@@ -11,30 +9,30 @@ package android.test.mock {
}
@Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
- method public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
- method public boolean arePermissionsIndividuallyControlled();
+ method @Deprecated public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
+ method @Deprecated public boolean arePermissionsIndividuallyControlled();
method @Deprecated public java.util.List<android.content.IntentFilter> getAllIntentFilters(String);
- method public String getDefaultBrowserPackageNameAsUser(int);
- method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
- method public android.graphics.drawable.Drawable getInstantAppIcon(String);
- method public android.content.ComponentName getInstantAppInstallerComponent();
- method public android.content.ComponentName getInstantAppResolverSettingsComponent();
- method public java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
- method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(String);
- method public int getIntentVerificationStatusAsUser(String, int);
- method public int getPermissionFlags(String, String, android.os.UserHandle);
- method public void grantRuntimePermission(String, String, android.os.UserHandle);
- method public int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException;
- method public int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
- method public void registerDexModule(String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback);
- method public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
- method public void revokeRuntimePermission(String, String, android.os.UserHandle);
- method public boolean setDefaultBrowserPackageNameAsUser(String, int);
+ method @Deprecated public String getDefaultBrowserPackageNameAsUser(int);
+ method @Deprecated public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
+ method @Deprecated public android.graphics.drawable.Drawable getInstantAppIcon(String);
+ method @Deprecated public android.content.ComponentName getInstantAppInstallerComponent();
+ method @Deprecated public android.content.ComponentName getInstantAppResolverSettingsComponent();
+ method @Deprecated public java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
+ method @Deprecated public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(String);
+ method @Deprecated public int getIntentVerificationStatusAsUser(String, int);
+ method @Deprecated public int getPermissionFlags(String, String, android.os.UserHandle);
+ method @Deprecated public void grantRuntimePermission(String, String, android.os.UserHandle);
+ method @Deprecated public int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated public int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @Deprecated public void registerDexModule(String, @Nullable android.content.pm.PackageManager.DexModuleRegisterCallback);
+ method @Deprecated public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
+ method @Deprecated public void revokeRuntimePermission(String, String, android.os.UserHandle);
+ method @Deprecated public boolean setDefaultBrowserPackageNameAsUser(String, int);
method public String[] setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String);
- method public void setUpdateAvailable(String, boolean);
- method public boolean updateIntentVerificationStatusAsUser(String, int, int);
- method public void updatePermissionFlags(String, String, int, int, android.os.UserHandle);
- method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
+ method @Deprecated public void setUpdateAvailable(String, boolean);
+ method @Deprecated public boolean updateIntentVerificationStatusAsUser(String, int, int);
+ method @Deprecated public void updatePermissionFlags(String, String, int, int, android.os.UserHandle);
+ method @Deprecated public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
}
}
diff --git a/test-mock/api/system-removed.txt b/test-mock/api/system-removed.txt
index 14191ebcb080..d802177e249b 100644
--- a/test-mock/api/system-removed.txt
+++ b/test-mock/api/system-removed.txt
@@ -1,3 +1 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index 1752edcd469e..9ed010881067 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -1,6 +1,4 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
package android.test.mock {
public class MockContext extends android.content.Context {
@@ -8,13 +6,13 @@ package android.test.mock {
}
@Deprecated public class MockPackageManager extends android.content.pm.PackageManager {
- method public void addCrossProfileIntentFilter(android.content.IntentFilter, int, int, int);
- method public void clearCrossProfileIntentFilters(int);
- method public int getInstallReason(String, android.os.UserHandle);
- method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
- method public String[] getNamesForUids(int[]);
- method @NonNull public String getServicesSystemSharedLibraryPackageName();
- method @NonNull public String getSharedSystemSharedLibraryPackageName();
+ method @Deprecated public void addCrossProfileIntentFilter(android.content.IntentFilter, int, int, int);
+ method @Deprecated public void clearCrossProfileIntentFilters(int);
+ method @Deprecated public int getInstallReason(String, android.os.UserHandle);
+ method @Deprecated public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
+ method @Deprecated public String[] getNamesForUids(int[]);
+ method @Deprecated @NonNull public String getServicesSystemSharedLibraryPackageName();
+ method @Deprecated @NonNull public String getSharedSystemSharedLibraryPackageName();
}
}
diff --git a/test-mock/api/test-removed.txt b/test-mock/api/test-removed.txt
index 14191ebcb080..d802177e249b 100644
--- a/test-mock/api/test-removed.txt
+++ b/test-mock/api/test-removed.txt
@@ -1,3 +1 @@
// Signature format: 2.0
-// - add-additional-overrides=no
-// - migrating=Migration in progress see b/299366704
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index a1a39ff173b4..59dc68900100 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -117,6 +117,16 @@ public class VcnGatewayConnectionConfigTest {
return buildTestConfig(UNDERLYING_NETWORK_TEMPLATES);
}
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig.Builder newTestBuilderMinimal() {
+ final VcnGatewayConnectionConfig.Builder builder = newBuilder();
+ for (int caps : EXPOSED_CAPS) {
+ builder.addExposedCapability(caps);
+ }
+
+ return builder;
+ }
+
private static VcnGatewayConnectionConfig.Builder newBuilder() {
// Append a unique identifier to the name prefix to guarantee that all created
// VcnGatewayConnectionConfigs have a unique name (required by VcnConfig).
@@ -125,6 +135,17 @@ public class VcnGatewayConnectionConfigTest {
TUNNEL_CONNECTION_PARAMS);
}
+ private static VcnGatewayConnectionConfig.Builder newBuilderMinimal() {
+ final VcnGatewayConnectionConfig.Builder builder =
+ new VcnGatewayConnectionConfig.Builder(
+ "newBuilderMinimal", TUNNEL_CONNECTION_PARAMS);
+ for (int caps : EXPOSED_CAPS) {
+ builder.addExposedCapability(caps);
+ }
+
+ return builder;
+ }
+
private static VcnGatewayConnectionConfig buildTestConfigWithExposedCapsAndOptions(
VcnGatewayConnectionConfig.Builder builder,
Set<Integer> gatewayOptions,
@@ -273,6 +294,7 @@ public class VcnGatewayConnectionConfigTest {
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
assertEquals(MAX_MTU, config.getMaxMtu());
+ assertTrue(config.isSafeModeEnabled());
assertFalse(
config.hasGatewayOption(
@@ -290,6 +312,14 @@ public class VcnGatewayConnectionConfigTest {
}
@Test
+ public void testBuilderAndGettersSafeModeDisabled() {
+ final VcnGatewayConnectionConfig config =
+ newBuilderMinimal().setSafeModeEnabled(false).build();
+
+ assertFalse(config.isSafeModeEnabled());
+ }
+
+ @Test
public void testPersistableBundle() {
final VcnGatewayConnectionConfig config = buildTestConfig();
@@ -305,6 +335,14 @@ public class VcnGatewayConnectionConfigTest {
}
@Test
+ public void testPersistableBundleSafeModeDisabled() {
+ final VcnGatewayConnectionConfig config =
+ newBuilderMinimal().setSafeModeEnabled(false).build();
+
+ assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
+ }
+
+ @Test
public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
@@ -411,4 +449,18 @@ public class VcnGatewayConnectionConfigTest {
assertEquals(config, configEqual);
assertNotEquals(config, configNotEqual);
}
+
+ @Test
+ public void testSafeModeEnableDisableEquality() throws Exception {
+ final VcnGatewayConnectionConfig config = newBuilderMinimal().build();
+ final VcnGatewayConnectionConfig configEqual = newBuilderMinimal().build();
+
+ assertEquals(config.isSafeModeEnabled(), configEqual.isSafeModeEnabled());
+
+ final VcnGatewayConnectionConfig configNotEqual =
+ newBuilderMinimal().setSafeModeEnabled(false).build();
+
+ assertEquals(config, configEqual);
+ assertNotEquals(config, configNotEqual);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 302af523a4bd..f84616426389 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -75,6 +75,9 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
+import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
+import com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import com.android.server.vcn.util.MtuUtils;
@@ -651,6 +654,74 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
verifySafeModeStateAndCallbackFired(2 /* invocationCount */, true /* isInSafeMode */);
}
+ private void verifySetSafeModeAlarm(
+ boolean safeModeEnabledByCaller,
+ boolean safeModeConfigFlagEnabled,
+ boolean expectingSafeModeEnabled)
+ throws Exception {
+ final VcnGatewayConnectionConfig config =
+ VcnGatewayConnectionConfigTest.newTestBuilderMinimal()
+ .setSafeModeEnabled(safeModeEnabledByCaller)
+ .build();
+ final VcnGatewayConnection.Dependencies deps =
+ mock(VcnGatewayConnection.Dependencies.class);
+ setUpWakeupMessage(
+ mSafeModeTimeoutAlarm, VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM, deps);
+ doReturn(safeModeConfigFlagEnabled).when(mFeatureFlags).safeModeConfig();
+
+ final VcnGatewayConnection connection =
+ new VcnGatewayConnection(
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ config,
+ mGatewayStatusCallback,
+ true /* isMobileDataEnabled */,
+ deps);
+
+ connection.setSafeModeAlarm();
+
+ final int expectedCallCnt = expectingSafeModeEnabled ? 1 : 0;
+ verify(deps, times(expectedCallCnt))
+ .newWakeupMessage(
+ eq(mVcnContext),
+ any(),
+ eq(VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM),
+ any());
+ }
+
+ @Test
+ public void testSafeModeEnabled_configFlagEnabled() throws Exception {
+ verifySetSafeModeAlarm(
+ true /* safeModeEnabledByCaller */,
+ true /* safeModeConfigFlagEnabled */,
+ true /* expectingSafeModeEnabled */);
+ }
+
+ @Test
+ public void testSafeModeEnabled_configFlagDisabled() throws Exception {
+ verifySetSafeModeAlarm(
+ true /* safeModeEnabledByCaller */,
+ false /* safeModeConfigFlagEnabled */,
+ true /* expectingSafeModeEnabled */);
+ }
+
+ @Test
+ public void testSafeModeDisabled_configFlagEnabled() throws Exception {
+ verifySetSafeModeAlarm(
+ false /* safeModeEnabledByCaller */,
+ true /* safeModeConfigFlagEnabled */,
+ false /* expectingSafeModeEnabled */);
+ }
+
+ @Test
+ public void testSafeModeDisabled_configFlagDisabled() throws Exception {
+ verifySetSafeModeAlarm(
+ false /* safeModeEnabledByCaller */,
+ false /* safeModeConfigFlagEnabled */,
+ true /* expectingSafeModeEnabled */);
+ }
+
private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
triggerChildOpened();
mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 5efbf598f941..edced87427c8 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -53,6 +53,7 @@ import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
import android.net.ipsec.ike.IkeSessionConnectionInfo;
+import android.net.vcn.FeatureFlags;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
@@ -165,6 +166,7 @@ public class VcnGatewayConnectionTestBase {
@NonNull protected final Context mContext;
@NonNull protected final TestLooper mTestLooper;
@NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
+ @NonNull protected final FeatureFlags mFeatureFlags;
@NonNull protected final VcnContext mVcnContext;
@NonNull protected final VcnGatewayConnectionConfig mConfig;
@NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
@@ -190,6 +192,7 @@ public class VcnGatewayConnectionTestBase {
mContext = mock(Context.class);
mTestLooper = new TestLooper();
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+ mFeatureFlags = mock(FeatureFlags.class);
mVcnContext = mock(VcnContext.class);
mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
@@ -222,6 +225,7 @@ public class VcnGatewayConnectionTestBase {
doReturn(mContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
+ doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags();
doReturn(mUnderlyingNetworkController)
.when(mDeps)
@@ -241,8 +245,15 @@ public class VcnGatewayConnectionTestBase {
doReturn(ELAPSED_REAL_TIME).when(mDeps).getElapsedRealTime();
}
+ protected void setUpWakeupMessage(
+ @NonNull WakeupMessage msg,
+ @NonNull String cmdName,
+ VcnGatewayConnection.Dependencies deps) {
+ doReturn(msg).when(deps).newWakeupMessage(eq(mVcnContext), any(), eq(cmdName), any());
+ }
+
private void setUpWakeupMessage(@NonNull WakeupMessage msg, @NonNull String cmdName) {
- doReturn(msg).when(mDeps).newWakeupMessage(eq(mVcnContext), any(), eq(cmdName), any());
+ setUpWakeupMessage(msg, cmdName, mDeps);
}
@Before
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 0d6dc3522d24..40cba3ed316f 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -119,9 +119,9 @@ cc_library_host_static {
"io/Util.cpp",
"io/ZipArchive.cpp",
"link/AutoVersioner.cpp",
+ "link/FeatureFlagsFilter.cpp",
"link/ManifestFixer.cpp",
"link/NoDefaultResourceRemover.cpp",
- "link/ProductFilter.cpp",
"link/PrivateAttributeMover.cpp",
"link/ReferenceLinker.cpp",
"link/ResourceExcluder.cpp",
@@ -134,6 +134,7 @@ cc_library_host_static {
"optimize/ResourceFilter.cpp",
"optimize/Obfuscator.cpp",
"optimize/VersionCollapser.cpp",
+ "process/ProductFilter.cpp",
"process/SymbolTable.cpp",
"split/TableSplitter.cpp",
"text/Printer.cpp",
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 03f9715fb265..bb7b13a71412 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -45,6 +45,7 @@
#include "io/StringStream.h"
#include "io/Util.h"
#include "io/ZipArchive.h"
+#include "process/ProductFilter.h"
#include "trace/TraceBuffer.h"
#include "util/Files.h"
#include "util/Util.h"
@@ -179,6 +180,15 @@ static bool CompileTable(IAaptContext* context, const CompileOptions& options,
if (!res_parser.Parse(&xml_parser)) {
return false;
}
+
+ if (options.product_.has_value()) {
+ if (!ProductFilter({*options.product_}, /* remove_default_config_values = */ true)
+ .Consume(context, &table)) {
+ context->GetDiagnostics()->Error(android::DiagMessage(path_data.source)
+ << "failed to filter product");
+ return false;
+ }
+ }
}
if (options.pseudolocalize && translatable_file) {
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 14a730a1b1a0..984d8901ac56 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -42,6 +42,7 @@ struct CompileOptions {
// See comments on aapt::ResourceParserOptions.
bool preserve_visibility_of_styleables = false;
bool verbose = false;
+ std::optional<std::string> product_;
};
/** Parses flags and compiles resources to be used in linking. */
@@ -76,6 +77,10 @@ class CompileCommand : public Command {
AddOptionalFlag("--source-path",
"Sets the compiled resource file source file path to the given string.",
&options_.source_path);
+ AddOptionalFlag("--filter-product",
+ "Leave only resources specific to the given product. All "
+ "other resources (including defaults) are removed.",
+ &options_.product_);
}
int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 97404fc69af2..1fb5cc0919a3 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -66,6 +66,7 @@
#include "optimize/ResourceDeduper.h"
#include "optimize/VersionCollapser.h"
#include "process/IResourceTableConsumer.h"
+#include "process/ProductFilter.h"
#include "process/SymbolTable.h"
#include "split/TableSplitter.h"
#include "trace/TraceBuffer.h"
@@ -2127,7 +2128,7 @@ class Linker {
<< "can't select products when building static library");
}
} else {
- ProductFilter product_filter(options_.products);
+ ProductFilter product_filter(options_.products, /* remove_default_config_values = */ false);
if (!product_filter.Consume(context_, &final_table_)) {
context_->GetDiagnostics()->Error(android::DiagMessage() << "failed stripping products");
return 1;
@@ -2512,6 +2513,28 @@ 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 1b1e93bd480a..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;
@@ -323,6 +330,7 @@ class LinkCommand : public Command {
"should only be used together with the --static-lib flag.",
&options_.merge_only);
AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
+ AddOptionalFlagList("--feature-flags", "Placeholder, to be implemented.", &feature_flags_args_);
}
int Action(const std::vector<std::string>& args) override;
@@ -347,6 +355,7 @@ class LinkCommand : public Command {
std::optional<std::string> stable_id_file_path_;
std::vector<std::string> split_args_;
std::optional<std::string> trace_folder_;
+ std::vector<std::string> feature_flags_args_;
};
}// namespace aapt
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index 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/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 44cd276f77a2..18165f7d489f 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -20,12 +20,12 @@
#include <set>
#include <unordered_set>
+#include "Resource.h"
+#include "SdkConstants.h"
#include "android-base/macros.h"
+#include "android-base/result.h"
#include "androidfw/ConfigDescription.h"
#include "androidfw/StringPiece.h"
-
-#include "Resource.h"
-#include "SdkConstants.h"
#include "process/IResourceTableConsumer.h"
#include "xml/XmlDom.h"
@@ -92,28 +92,6 @@ class PrivateAttributeMover : public IResourceTableConsumer {
DISALLOW_COPY_AND_ASSIGN(PrivateAttributeMover);
};
-class ResourceConfigValue;
-
-class ProductFilter : public IResourceTableConsumer {
- public:
- using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
-
- explicit ProductFilter(std::unordered_set<std::string> products) : products_(products) {
- }
-
- ResourceConfigValueIter SelectProductToKeep(const ResourceNameRef& name,
- const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end,
- android::IDiagnostics* diag);
-
- bool Consume(IAaptContext* context, ResourceTable* table) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ProductFilter);
-
- std::unordered_set<std::string> products_;
-};
-
// Removes namespace nodes and URI information from the XmlResource.
//
// Once an XmlResource is processed by this consumer, it is no longer able to have its attributes
diff --git a/tools/aapt2/link/ProductFilter.cpp b/tools/aapt2/process/ProductFilter.cpp
index 9544986fda76..0b1c0a6adb51 100644
--- a/tools/aapt2/link/ProductFilter.cpp
+++ b/tools/aapt2/process/ProductFilter.cpp
@@ -14,16 +14,18 @@
* limitations under the License.
*/
-#include "link/Linkers.h"
+#include "process/ProductFilter.h"
+
+#include <algorithm>
#include "ResourceTable.h"
#include "trace/TraceBuffer.h"
namespace aapt {
-ProductFilter::ResourceConfigValueIter ProductFilter::SelectProductToKeep(
- const ResourceNameRef& name, const ResourceConfigValueIter begin,
- const ResourceConfigValueIter end, android::IDiagnostics* diag) {
+std::optional<ProductFilter::ResourceConfigValueIter> ProductFilter::SelectProductToKeep(
+ const ResourceNameRef& name, ResourceConfigValueIter begin, ResourceConfigValueIter end,
+ android::IDiagnostics* diag) {
ResourceConfigValueIter default_product_iter = end;
ResourceConfigValueIter selected_product_iter = end;
@@ -36,12 +38,11 @@ ProductFilter::ResourceConfigValueIter ProductFilter::SelectProductToKeep(
<< "selection of product '" << config_value->product << "' for resource "
<< name << " is ambiguous");
- ResourceConfigValue* previously_selected_config_value =
- selected_product_iter->get();
+ ResourceConfigValue* previously_selected_config_value = selected_product_iter->get();
diag->Note(android::DiagMessage(previously_selected_config_value->value->GetSource())
<< "product '" << previously_selected_config_value->product
<< "' is also a candidate");
- return end;
+ return std::nullopt;
}
// Select this product.
@@ -54,11 +55,10 @@ ProductFilter::ResourceConfigValueIter ProductFilter::SelectProductToKeep(
diag->Error(android::DiagMessage(config_value->value->GetSource())
<< "multiple default products defined for resource " << name);
- ResourceConfigValue* previously_default_config_value =
- default_product_iter->get();
+ ResourceConfigValue* previously_default_config_value = default_product_iter->get();
diag->Note(android::DiagMessage(previously_default_config_value->value->GetSource())
<< "default product also defined here");
- return end;
+ return std::nullopt;
}
// Mark the default.
@@ -66,9 +66,16 @@ ProductFilter::ResourceConfigValueIter ProductFilter::SelectProductToKeep(
}
}
+ if (remove_default_config_values_) {
+ // If we are leaving only a specific product, return early here instead of selecting the default
+ // value. Returning end here will cause this value set to be skipped, and will be removed with
+ // ClearEmptyValues method.
+ return selected_product_iter;
+ }
+
if (default_product_iter == end) {
diag->Error(android::DiagMessage() << "no default product defined for resource " << name);
- return end;
+ return std::nullopt;
}
if (selected_product_iter == end) {
@@ -89,20 +96,27 @@ bool ProductFilter::Consume(IAaptContext* context, ResourceTable* table) {
ResourceConfigValueIter start_range_iter = iter;
while (iter != entry->values.end()) {
++iter;
- if (iter == entry->values.end() ||
- (*iter)->config != (*start_range_iter)->config) {
+ if (iter == entry->values.end() || (*iter)->config != (*start_range_iter)->config) {
// End of the array, or we saw a different config,
// so this must be the end of a range of products.
// Select the product to keep from the set of products defined.
ResourceNameRef name(pkg->name, type->named_type, entry->name);
- auto value_to_keep = SelectProductToKeep(
- name, start_range_iter, iter, context->GetDiagnostics());
- if (value_to_keep == iter) {
+ auto value_to_keep =
+ SelectProductToKeep(name, start_range_iter, iter, context->GetDiagnostics());
+ if (!value_to_keep.has_value()) {
// An error occurred, we could not pick a product.
error = true;
- } else {
+ } else if (auto val = value_to_keep.value(); val != iter) {
// We selected a product to keep. Move it to the new array.
- new_values.push_back(std::move(*value_to_keep));
+ if (remove_default_config_values_) {
+ // We are filtering values with the given product. The selected value here will be
+ // a new default value, and all other values will be removed.
+ new_values.push_back(
+ std::make_unique<ResourceConfigValue>((*val)->config, android::StringPiece{}));
+ new_values.back()->value = std::move((*val)->value);
+ } else {
+ new_values.push_back(std::move(*val));
+ }
}
// Start the next range of products.
@@ -115,7 +129,27 @@ bool ProductFilter::Consume(IAaptContext* context, ResourceTable* table) {
}
}
}
+
+ if (remove_default_config_values_) {
+ ClearEmptyValues(table);
+ }
+
return !error;
}
+void ProductFilter::ClearEmptyValues(ResourceTable* table) {
+ // Clear any empty packages/types/entries, as remove_default_config_values_ may remove an entire
+ // value set.
+ CHECK(remove_default_config_values_)
+ << __func__ << " should only be called when remove_default_config_values_ is set";
+
+ for (auto& pkg : table->packages) {
+ for (auto& type : pkg->types) {
+ std::erase_if(type->entries, [](auto& entry) { return entry->values.empty(); });
+ }
+ std::erase_if(pkg->types, [](auto& type) { return type->entries.empty(); });
+ }
+ std::erase_if(table->packages, [](auto& package) { return package->types.empty(); });
+}
+
} // namespace aapt
diff --git a/tools/aapt2/process/ProductFilter.h b/tools/aapt2/process/ProductFilter.h
new file mode 100644
index 000000000000..0ec2f00863fc
--- /dev/null
+++ b/tools/aapt2/process/ProductFilter.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "Resource.h"
+#include "android-base/macros.h"
+#include "androidfw/ConfigDescription.h"
+#include "androidfw/IDiagnostics.h"
+#include "process/IResourceTableConsumer.h"
+
+namespace aapt {
+
+class ResourceConfigValue;
+
+class ProductFilter : public IResourceTableConsumer {
+ public:
+ using ResourceConfigValueIter = std::vector<std::unique_ptr<ResourceConfigValue>>::iterator;
+
+ // Setting remove_default_config_values will remove all values other than
+ // specified product, including default. For example, if the following table
+ //
+ // <string name="foo" product="default">foo_default</string>
+ // <string name="foo" product="tablet">foo_tablet</string>
+ // <string name="bar">bar</string>
+ //
+ // is consumed with tablet, it will result in
+ //
+ // <string name="foo">foo_tablet</string>
+ //
+ // removing foo_default and bar. This option is to generate an RRO package
+ // with given product.
+ explicit ProductFilter(std::unordered_set<std::string> products,
+ bool remove_default_config_values)
+ : products_(std::move(products)),
+ remove_default_config_values_(remove_default_config_values) {
+ }
+
+ bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProductFilter);
+
+ // SelectProductToKeep returns an iterator for the selected value.
+ //
+ // Returns std::nullopt in case of failure (e.g. ambiguous values, missing or duplicated default
+ // values).
+ // Returns `end` if keep_as_default_product is set and no value for the specified product was
+ // found.
+ std::optional<ResourceConfigValueIter> SelectProductToKeep(const ResourceNameRef& name,
+ ResourceConfigValueIter begin,
+ ResourceConfigValueIter end,
+ android::IDiagnostics* diag);
+
+ void ClearEmptyValues(ResourceTable* table);
+
+ std::unordered_set<std::string> products_;
+ bool remove_default_config_values_;
+};
+
+} // namespace aapt
diff --git a/tools/aapt2/link/ProductFilter_test.cpp b/tools/aapt2/process/ProductFilter_test.cpp
index 2cb9afa05cad..27a82dcc3453 100644
--- a/tools/aapt2/link/ProductFilter_test.cpp
+++ b/tools/aapt2/process/ProductFilter_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "link/Linkers.h"
+#include "process/ProductFilter.h"
#include "test/Test.h"
@@ -57,17 +57,15 @@ TEST(ProductFilterTest, SelectTwoProducts) {
.Build(),
context->GetDiagnostics()));
- ProductFilter filter({"tablet"});
+ ProductFilter filter({"tablet"}, /* remove_default_config_values = */ false);
ASSERT_TRUE(filter.Consume(context.get(), &table));
- EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", land, ""));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", land, "tablet"));
- EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", port, ""));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one", port, "tablet"));
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", land, ""));
+ EXPECT_NE(nullptr,
+ test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", land, "tablet"));
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", port, ""));
+ EXPECT_NE(nullptr,
+ test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", port, "tablet"));
}
TEST(ProductFilterTest, SelectDefaultProduct) {
@@ -88,15 +86,15 @@ TEST(ProductFilterTest, SelectDefaultProduct) {
context->GetDiagnostics()));
;
- ProductFilter filter(std::unordered_set<std::string>{});
+ ProductFilter filter(std::unordered_set<std::string>{},
+ /* remove_default_config_values = */ false);
ASSERT_TRUE(filter.Consume(context.get(), &table));
- EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one",
- ConfigDescription::DefaultConfig(), ""));
- EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(
- &table, "android:string/one",
- ConfigDescription::DefaultConfig(), "tablet"));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/one",
+ ConfigDescription::DefaultConfig(), ""));
+ EXPECT_EQ(nullptr,
+ test::GetValueForConfigAndProduct<Id>(&table, "android:string/one",
+ ConfigDescription::DefaultConfig(), "tablet"));
}
TEST(ProductFilterTest, FailOnAmbiguousProduct) {
@@ -123,7 +121,7 @@ TEST(ProductFilterTest, FailOnAmbiguousProduct) {
.Build(),
context->GetDiagnostics()));
- ProductFilter filter({"tablet", "no-sdcard"});
+ ProductFilter filter({"tablet", "no-sdcard"}, /* remove_default_config_values = */ false);
ASSERT_FALSE(filter.Consume(context.get(), &table));
}
@@ -144,8 +142,67 @@ TEST(ProductFilterTest, FailOnMultipleDefaults) {
.Build(),
context->GetDiagnostics()));
- ProductFilter filter(std::unordered_set<std::string>{});
+ ProductFilter filter(std::unordered_set<std::string>{},
+ /* remove_default_config_values = */ false);
ASSERT_FALSE(filter.Consume(context.get(), &table));
}
+TEST(ProductFilterTest, RemoveDefaultConfigValues) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ const ConfigDescription land = test::ParseConfigOrDie("land");
+ const ConfigDescription port = test::ParseConfigOrDie("port");
+
+ ResourceTable table;
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/default.xml")).Build(),
+ land)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/tablet.xml")).Build(),
+ land, "tablet")
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/two"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("land/default.xml")).Build(),
+ land)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/default.xml")).Build(),
+ port)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/one"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/tablet.xml")).Build(),
+ port, "tablet")
+ .Build(),
+ context->GetDiagnostics()));
+
+ ASSERT_TRUE(table.AddResource(
+ NewResourceBuilder(test::ParseNameOrDie("android:string/two"))
+ .SetValue(test::ValueBuilder<Id>().SetSource(android::Source("port/default.xml")).Build(),
+ port)
+ .Build(),
+ context->GetDiagnostics()));
+
+ ProductFilter filter({"tablet"}, /* remove_default_config_values = */ true);
+ ASSERT_TRUE(filter.Consume(context.get(), &table));
+
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", land, ""));
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/two", land, ""));
+ EXPECT_NE(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/one", port, ""));
+ EXPECT_EQ(nullptr, test::GetValueForConfigAndProduct<Id>(&table, "android:string/two", port, ""));
+}
+
} // namespace aapt
diff --git a/tools/fonts/update_font_metadata.py b/tools/fonts/update_font_metadata.py
index 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/Constants.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt
index e03d92ab44a0..f1727b78f135 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/aidl/Constants.kt
@@ -30,6 +30,7 @@ const val BINDER_CLASS = "android.os.Binder"
const val IINTERFACE_INTERFACE = "android.os.IInterface"
const val AIDL_PERMISSION_HELPER_SUFFIX = "_enforcePermission"
+const val PERMISSION_PREFIX_LITERAL = "android.permission."
/**
* If a non java (e.g. c++) backend is enabled, the @EnforcePermission
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/aidl/EnforcePermissionUtils.kt
index 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/AndroidGlobalIssueRegistry.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
index a20266a9b140..28eab8f62e74 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
@@ -20,7 +20,6 @@ import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.client.api.Vendor
import com.android.tools.lint.detector.api.CURRENT_API
import com.google.android.lint.aidl.EnforcePermissionDetector
-import com.google.android.lint.aidl.EnforcePermissionHelperDetector
import com.google.android.lint.aidl.SimpleManualPermissionEnforcementDetector
import com.google.auto.service.AutoService
@@ -30,7 +29,8 @@ class AndroidGlobalIssueRegistry : IssueRegistry() {
override val issues = listOf(
EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
- EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION,
SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
)
@@ -45,4 +45,4 @@ class AndroidGlobalIssueRegistry : IssueRegistry() {
feedbackUrl = "http://b/issues/new?component=315013",
contact = "repsonsible-apis@google.com"
)
-} \ No newline at end of file
+}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index 0baac2c7aacf..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
@@ -30,29 +30,34 @@ import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.google.android.lint.findCallExpression
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiArrayInitializerMemberValue
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
-import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UBlockExpression
+import org.jetbrains.uast.UDeclarationsExpression
import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UMethod
-import org.jetbrains.uast.toUElement
+import org.jetbrains.uast.skipParenthesizedExprDown
+
+import java.util.EnumSet
/**
- * Lint Detector that ensures that any method overriding a method annotated
- * with @EnforcePermission is also annotated with the exact same annotation.
- * The intent is to surface the effective permission checks to the service
- * implementations.
+ * Lint Detector that ensures consistency when using the @EnforcePermission
+ * annotation. Multiple verifications are implemented:
*
- * This is done with 2 mechanisms:
* 1. Visit any annotation usage, to ensure that any derived class will have
- * the correct annotation on each methods. This is for the top to bottom
- * propagation.
- * 2. Visit any annotation, to ensure that if a method is annotated, it has
+ * the correct annotation on each methods. Even if the subclass does not
+ * have the annotation, visitAnnotationUsage will be called which allows us
+ * to capture the issue.
+ * 2. Visit any method, to ensure that if a method is annotated, it has
* its ancestor also annotated. This is to avoid having an annotation on a
* Java method without the corresponding annotation on the AIDL interface.
+ * 3. When annotated, ensures that the first instruction is to call the helper
+ * method (or the parent helper).
*/
class EnforcePermissionDetector : Detector(), SourceCodeScanner {
@@ -60,9 +65,8 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
return listOf(ANNOTATION_ENFORCE_PERMISSION)
}
- override fun getApplicableUastTypes(): List<Class<out UElement>> {
- return listOf(UAnnotation::class.java)
- }
+ override fun getApplicableUastTypes(): List<Class<out UElement?>> =
+ listOf(UMethod::class.java)
private fun annotationValueGetChildren(elem: PsiElement): Array<PsiElement> {
if (elem is PsiArrayInitializerMemberValue)
@@ -93,7 +97,7 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
val v1 = ConstantEvaluator.evaluate(context, value1)
val v2 = ConstantEvaluator.evaluate(context, value2)
if (v1 != null && v2 != null) {
- if (v1 != v2) {
+ if (v1 != v2 && !isOneShortPermissionOfOther(v1, v2)) {
return false
}
} else {
@@ -105,7 +109,7 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
for (j in children1.indices) {
val c1 = ConstantEvaluator.evaluate(context, children1[j])
val c2 = ConstantEvaluator.evaluate(context, children2[j])
- if (c1 != c2) {
+ if (c1 != c2 && !isOneShortPermissionOfOther(c1, c2)) {
return false
}
}
@@ -114,6 +118,12 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
return true
}
+ private fun isOneShortPermissionOfOther(
+ permission1: Any?,
+ permission2: Any?
+ ): Boolean = permission1 == (permission2 as? String)?.removePrefix(PERMISSION_PREFIX_LITERAL) ||
+ permission2 == (permission1 as? String)?.removePrefix(PERMISSION_PREFIX_LITERAL)
+
private fun compareMethods(
context: JavaContext,
element: UElement,
@@ -121,11 +131,6 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
overriddenMethod: PsiMethod,
checkEquivalence: Boolean = true
) {
- // If method is not from a Stub subclass, this method shouldn't use @EP at all.
- // This is handled by EnforcePermissionHelperDetector.
- if (!isContainedInSubclassOfStub(context, overridingMethod.toUElement() as? UMethod)) {
- return
- }
val overridingAnnotation = overridingMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION)
val overriddenAnnotation = overriddenMethod.getAnnotation(ANNOTATION_ENFORCE_PERMISSION)
val location = context.getLocation(element)
@@ -161,40 +166,112 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
) {
if (usageInfo.type == AnnotationUsageType.METHOD_OVERRIDE &&
annotationInfo.origin == AnnotationOrigin.METHOD) {
+ /* Ignore implementations that are not a sub-class of Stub (i.e., Proxy). */
+ val uMethod = element as? UMethod ?: return
+ if (getContainingAidlInterface(context, uMethod) == null) {
+ return
+ }
val overridingMethod = element.sourcePsi as PsiMethod
val overriddenMethod = usageInfo.referenced as PsiMethod
compareMethods(context, element, overridingMethod, overriddenMethod)
}
}
- override fun createUastHandler(context: JavaContext): UElementHandler {
- return object : UElementHandler() {
- override fun visitAnnotation(node: UAnnotation) {
- if (node.qualifiedName != ANNOTATION_ENFORCE_PERMISSION) {
- return
- }
- val method = node.uastParent as? UMethod ?: return
- val overridingMethod = method as PsiMethod
- val parents = overridingMethod.findSuperMethods()
- for (overriddenMethod in parents) {
- // The equivalence check can be skipped, if both methods are
- // annotated, it will be verified by visitAnnotationUsage.
- compareMethods(context, method, overridingMethod,
- overriddenMethod, checkEquivalence = false)
- }
+ override fun createUastHandler(context: JavaContext): UElementHandler = AidlStubHandler(context)
+
+ private inner class AidlStubHandler(val context: JavaContext) : UElementHandler() {
+ override fun visitMethod(node: UMethod) {
+ if (context.evaluator.isAbstract(node)) return
+ if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return
+
+ val stubClass = containingStub(context, node)
+ if (stubClass == null) {
+ context.report(
+ ISSUE_MISUSING_ENFORCE_PERMISSION,
+ node,
+ context.getLocation(node),
+ "The class of ${node.name} does not inherit from an AIDL generated Stub class"
+ )
+ return
+ }
+
+ /* Check that we are connected to the super class */
+ val overridingMethod = node as PsiMethod
+ val parents = overridingMethod.findSuperMethods(stubClass)
+ if (parents.isEmpty()) {
+ context.report(
+ ISSUE_MISUSING_ENFORCE_PERMISSION,
+ node,
+ context.getLocation(node),
+ "The method ${node.name} does not override an AIDL generated method"
+ )
+ return
+ }
+ for (overriddenMethod in parents) {
+ // The equivalence check can be skipped, if both methods are
+ // annotated, it will be verified by visitAnnotationUsage.
+ compareMethods(context, node, overridingMethod,
+ overriddenMethod, checkEquivalence = false)
+ }
+
+ /* Check that the helper is called as a first instruction */
+ val targetExpression = getHelperMethodCallSourceString(node)
+ val message =
+ "Method must start with $targetExpression or super.${node.name}(), if applicable"
+
+ val firstExpression = (node.uastBody as? UBlockExpression)
+ ?.expressions?.firstOrNull()
+
+ if (firstExpression == null) {
+ context.report(
+ ISSUE_ENFORCE_PERMISSION_HELPER,
+ context.getLocation(node),
+ message,
+ )
+ return
+ }
+
+ val firstExpressionSource = firstExpression.skipParenthesizedExprDown()
+ .asSourceString()
+ .filterNot(Char::isWhitespace)
+
+ if (firstExpressionSource != targetExpression &&
+ firstExpressionSource != "super.$targetExpression") {
+ // calling super.<methodName>() is also legal
+ val directSuper = context.evaluator.getSuperMethod(node)
+ val firstCall = findCallExpression(firstExpression)?.resolve()
+ if (directSuper != null && firstCall == directSuper) return
+
+ val locationTarget = getLocationTarget(firstExpression)
+ val expressionLocation = context.getLocation(locationTarget)
+
+ context.report(
+ ISSUE_ENFORCE_PERMISSION_HELPER,
+ context.getLocation(node),
+ message,
+ getHelperMethodFix(node, expressionLocation),
+ )
}
}
}
companion object {
+
+ private const val HELPER_SUFFIX = "_enforcePermission"
+
val EXPLANATION = """
- The @EnforcePermission annotation is used to indicate that the underlying binder code
- has already verified the caller's permissions before calling the appropriate method. The
- verification code is usually generated by the AIDL compiler, which also takes care of
- annotating the generated Java code.
+ The @EnforcePermission annotation is used to delegate the verification of the caller's
+ permissions to a generated AIDL method.
In order to surface that information to platform developers, the same annotation must be
used on the implementation class or methods.
+
+ The @EnforcePermission annotation can only be used on methods whose class extends from
+ the Stub class generated by the AIDL compiler. When @EnforcePermission is applied, the
+ AIDL compiler generates a Stub method to do the permission check called yourMethodName$HELPER_SUFFIX.
+
+ yourMethodName$HELPER_SUFFIX must be executed before any other operation. To do that, you can
+ either call it directly, or call it indirectly via super.yourMethodName().
"""
val ISSUE_MISSING_ENFORCE_PERMISSION: Issue = Issue.create(
@@ -206,7 +283,7 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
severity = Severity.ERROR,
implementation = Implementation(
EnforcePermissionDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
)
)
@@ -219,8 +296,47 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
severity = Severity.ERROR,
implementation = Implementation(
EnforcePermissionDetector::class.java,
- Scope.JAVA_FILE_SCOPE
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+
+ val ISSUE_ENFORCE_PERMISSION_HELPER: Issue = Issue.create(
+ id = "MissingEnforcePermissionHelper",
+ briefDescription = """Missing permission-enforcing method call in AIDL method
+ |annotated with @EnforcePermission""".trimMargin(),
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
)
)
+
+ val ISSUE_MISUSING_ENFORCE_PERMISSION: Issue = Issue.create(
+ id = "MisusingEnforcePermissionAnnotation",
+ briefDescription = "@EnforcePermission cannot be used here",
+ explanation = EXPLANATION,
+ category = Category.SECURITY,
+ priority = 6,
+ severity = Severity.ERROR,
+ implementation = Implementation(
+ EnforcePermissionDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+
+ /**
+ * handles an edge case with UDeclarationsExpression, where sourcePsi is null,
+ * resulting in an incorrect Location if used directly
+ */
+ private fun getLocationTarget(firstExpression: UExpression): PsiElement? {
+ if (firstExpression.sourcePsi != null) return firstExpression.sourcePsi
+ if (firstExpression is UDeclarationsExpression) {
+ return firstExpression.declarations.firstOrNull()?.sourcePsi
+ }
+ return null
+ }
}
}
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
deleted file mode 100644
index df13af516514..000000000000
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionHelperDetector.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.lint.aidl
-
-import com.android.tools.lint.client.api.UElementHandler
-import com.android.tools.lint.detector.api.Category
-import com.android.tools.lint.detector.api.Detector
-import com.android.tools.lint.detector.api.Implementation
-import com.android.tools.lint.detector.api.Issue
-import com.android.tools.lint.detector.api.JavaContext
-import com.android.tools.lint.detector.api.Scope
-import com.android.tools.lint.detector.api.Severity
-import com.android.tools.lint.detector.api.SourceCodeScanner
-import com.google.android.lint.findCallExpression
-import com.intellij.psi.PsiElement
-import org.jetbrains.uast.UBlockExpression
-import org.jetbrains.uast.UDeclarationsExpression
-import org.jetbrains.uast.UElement
-import org.jetbrains.uast.UExpression
-import org.jetbrains.uast.UMethod
-import org.jetbrains.uast.skipParenthesizedExprDown
-
-class EnforcePermissionHelperDetector : Detector(), SourceCodeScanner {
- override fun getApplicableUastTypes(): List<Class<out UElement?>> =
- listOf(UMethod::class.java)
-
- override fun createUastHandler(context: JavaContext): UElementHandler = AidlStubHandler(context)
-
- private inner class AidlStubHandler(val context: JavaContext) : UElementHandler() {
- override fun visitMethod(node: UMethod) {
- if (context.evaluator.isAbstract(node)) return
- if (!node.hasAnnotation(ANNOTATION_ENFORCE_PERMISSION)) return
-
- if (!isContainedInSubclassOfStub(context, node)) {
- context.report(
- ISSUE_MISUSING_ENFORCE_PERMISSION,
- node,
- context.getLocation(node),
- "The class of ${node.name} does not inherit from an AIDL generated Stub class"
- )
- return
- }
-
- val targetExpression = getHelperMethodCallSourceString(node)
- val message =
- "Method must start with $targetExpression or super.${node.name}(), if applicable"
-
- val firstExpression = (node.uastBody as? UBlockExpression)
- ?.expressions?.firstOrNull()
-
- if (firstExpression == null) {
- context.report(
- ISSUE_ENFORCE_PERMISSION_HELPER,
- context.getLocation(node),
- message,
- )
- return
- }
-
- val firstExpressionSource = firstExpression.skipParenthesizedExprDown()
- .asSourceString()
- .filterNot(Char::isWhitespace)
-
- if (firstExpressionSource != targetExpression &&
- firstExpressionSource != "super.$targetExpression") {
- // calling super.<methodName>() is also legal
- val directSuper = context.evaluator.getSuperMethod(node)
- val firstCall = findCallExpression(firstExpression)?.resolve()
- if (directSuper != null && firstCall == directSuper) return
-
- val locationTarget = getLocationTarget(firstExpression)
- val expressionLocation = context.getLocation(locationTarget)
-
- context.report(
- ISSUE_ENFORCE_PERMISSION_HELPER,
- context.getLocation(node),
- message,
- getHelperMethodFix(node, expressionLocation),
- )
- }
- }
- }
-
- companion object {
- private const val HELPER_SUFFIX = "_enforcePermission"
-
- private const val EXPLANATION = """
- The @EnforcePermission annotation can only be used on methods whose class extends from
- the Stub class generated by the AIDL compiler. When @EnforcePermission is applied, the
- AIDL compiler generates a Stub method to do the permission check called yourMethodName$HELPER_SUFFIX.
-
- yourMethodName$HELPER_SUFFIX must be executed before any other operation. To do that, you can
- either call it directly, or call it indirectly via super.yourMethodName().
- """
-
- val ISSUE_ENFORCE_PERMISSION_HELPER: Issue = Issue.create(
- id = "MissingEnforcePermissionHelper",
- briefDescription = """Missing permission-enforcing method call in AIDL method
- |annotated with @EnforcePermission""".trimMargin(),
- explanation = EXPLANATION,
- category = Category.SECURITY,
- priority = 6,
- severity = Severity.ERROR,
- implementation = Implementation(
- EnforcePermissionHelperDetector::class.java,
- Scope.JAVA_FILE_SCOPE
- )
- )
-
- val ISSUE_MISUSING_ENFORCE_PERMISSION: Issue = Issue.create(
- id = "MisusingEnforcePermissionAnnotation",
- briefDescription = "@EnforcePermission cannot be used here",
- explanation = EXPLANATION,
- category = Category.SECURITY,
- priority = 6,
- severity = Severity.ERROR,
- implementation = Implementation(
- EnforcePermissionDetector::class.java,
- Scope.JAVA_FILE_SCOPE
- )
- )
-
- /**
- * handles an edge case with UDeclarationsExpression, where sourcePsi is null,
- * resulting in an incorrect Location if used directly
- */
- private fun getLocationTarget(firstExpression: UExpression): PsiElement? {
- if (firstExpression.sourcePsi != null) return firstExpression.sourcePsi
- if (firstExpression is UDeclarationsExpression) {
- return firstExpression.declarations.firstOrNull()?.sourcePsi
- }
- return null
- }
- }
-}
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
index 75b00737a168..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
@@ -28,7 +28,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
override fun getIssues(): List<Issue> = listOf(
EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
- EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION
+ EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION
)
override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
@@ -41,7 +43,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
public class TestClass2 extends IFooMethod.Stub {
@Override
@EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -58,7 +62,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
public class TestClass11 extends IFooMethod.Stub {
@Override
@EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAll() {}
+ public void testMethodAll() {
+ testMethodAll_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -75,7 +81,10 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
public class TestClass111 extends IFooMethod.Stub {
@Override
@EnforcePermission(allOf={"android.permission.INTERNET", android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAllLiteral() {}
+ public void testMethodAllLiteral() {
+ testMethodAllLiteral_enforcePermission();
+
+ }
}
""").indented(),
*stubs
@@ -92,7 +101,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
public class TestClass12 extends IFooMethod.Stub {
@Override
@EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAny() {}
+ public void testMethodAny() {
+ testMethodAny_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -109,7 +120,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
public class TestClass121 extends IFooMethod.Stub {
@Override
@EnforcePermission(anyOf={"android.permission.INTERNET", android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAnyLiteral() {}
+ public void testMethodAnyLiteral() {
+ testMethodAnyLiteral_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -124,7 +137,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
package test.pkg;
public class TestClass4 extends IFooMethod.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -132,21 +147,67 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
.run()
.expect("""
src/test/pkg/TestClass4.java:4: Error: The method TestClass4.testMethod is annotated with @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET) \
- which differs from the overridden method Stub.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). \
+ which differs from the overridden method IFooMethod.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethod() {}
+ public void testMethod() {
~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
}
+ fun testDetectIssuesAnnotationOnNonStubMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass42 extends IFooMethod.Stub {
+ @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+ public void aRegularMethodNotPartOfStub() {
+ }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expect("""
+ src/test/pkg/TestClass42.java:3: Error: The method aRegularMethodNotPartOfStub does not override an AIDL generated method [MisusingEnforcePermissionAnnotation]
+ @android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
+ ^
+ 1 errors, 0 warnings
+ """.addLineContinuation())
+ }
+
+ fun testDetectNoIssuesAnnotationOnNonStubMethod() {
+ lint().files(java(
+ """
+ package test.pkg;
+ public class TestClass43 extends IFooMethod.Stub {
+ public void aRegularMethodNotPartOfStub() {
+ }
+ }
+ """).indented(), java(
+ """
+ package test.pkg;
+ public class TestClass44 extends TestClass43 {
+ @Override
+ public void aRegularMethodNotPartOfStub() {
+ }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
fun testDetectIssuesEmptyAnnotationOnMethod() {
lint().files(java(
"""
package test.pkg;
public class TestClass41 extends IFooMethod.Stub {
@android.annotation.EnforcePermission
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -154,9 +215,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
.run()
.expect("""
src/test/pkg/TestClass41.java:4: Error: The method TestClass41.testMethod is annotated with @android.annotation.EnforcePermission \
- which differs from the overridden method Stub.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). \
+ which differs from the overridden method IFooMethod.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethod() {}
+ public void testMethod() {
~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -168,7 +229,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
package test.pkg;
public class TestClass9 extends IFooMethod.Stub {
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.NFC})
- public void testMethodAny() {}
+ public void testMethodAny() {
+ testMethodAny_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -177,10 +240,10 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
.expect("""
src/test/pkg/TestClass9.java:4: Error: The method TestClass9.testMethodAny is annotated with \
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.NFC}) \
- which differs from the overridden method Stub.testMethodAny: \
+ which differs from the overridden method IFooMethod.testMethodAny: \
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE}). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAny() {}
+ public void testMethodAny() {
~~~~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -192,7 +255,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
package test.pkg;
public class TestClass91 extends IFooMethod.Stub {
@android.annotation.EnforcePermission(anyOf={"android.permission.INTERNET", "android.permissionoopsthisisatypo.READ_PHONE_STATE"})
- public void testMethodAnyLiteral() {}
+ public void testMethodAnyLiteral() {
+ testMethodAnyLiteral_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -201,10 +266,10 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
.expect("""
src/test/pkg/TestClass91.java:4: Error: The method TestClass91.testMethodAnyLiteral is annotated with \
@android.annotation.EnforcePermission(anyOf={"android.permission.INTERNET", "android.permissionoopsthisisatypo.READ_PHONE_STATE"}) \
- which differs from the overridden method Stub.testMethodAnyLiteral: \
+ which differs from the overridden method IFooMethod.testMethodAnyLiteral: \
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAnyLiteral() {}
+ public void testMethodAnyLiteral() {
~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -216,7 +281,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
package test.pkg;
public class TestClass10 extends IFooMethod.Stub {
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.NFC})
- public void testMethodAll() {}
+ public void testMethodAll() {
+ testMethodAll_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -225,10 +292,10 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
.expect("""
src/test/pkg/TestClass10.java:4: Error: The method TestClass10.testMethodAll is annotated with \
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.NFC}) \
- which differs from the overridden method Stub.testMethodAll: \
+ which differs from the overridden method IFooMethod.testMethodAll: \
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE}). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAll() {}
+ public void testMethodAll() {
~~~~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -240,7 +307,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
package test.pkg;
public class TestClass101 extends IFooMethod.Stub {
@android.annotation.EnforcePermission(allOf={"android.permission.INTERNET", "android.permissionoopsthisisatypo.READ_PHONE_STATE"})
- public void testMethodAllLiteral() {}
+ public void testMethodAllLiteral() {
+ testMethodAllLiteral_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -249,10 +318,10 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
.expect("""
src/test/pkg/TestClass101.java:4: Error: The method TestClass101.testMethodAllLiteral is annotated with \
@android.annotation.EnforcePermission(allOf={"android.permission.INTERNET", "android.permissionoopsthisisatypo.READ_PHONE_STATE"}) \
- which differs from the overridden method Stub.testMethodAllLiteral: \
+ which differs from the overridden method IFooMethod.testMethodAllLiteral: \
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). \
The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAllLiteral() {}
+ public void testMethodAllLiteral() {
~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -263,16 +332,18 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
"""
package test.pkg;
public class TestClass6 extends IFooMethod.Stub {
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
)
.run()
.expect("""
- src/test/pkg/TestClass6.java:3: Error: The method TestClass6.testMethod overrides the method Stub.testMethod which is annotated with @EnforcePermission. \
+ src/test/pkg/TestClass6.java:3: Error: The method TestClass6.testMethod overrides the method IFooMethod.testMethod which is annotated with @EnforcePermission. \
The same annotation must be used on TestClass6.testMethod [MissingEnforcePermissionAnnotation]
- public void testMethod() {}
+ public void testMethod() {
~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -284,16 +355,18 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
package test.pkg;
public class TestClass7 extends IBar.Stub {
@android.annotation.EnforcePermission(android.Manifest.permission.INTERNET)
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
)
.run()
.expect("""
- src/test/pkg/TestClass7.java:4: Error: The method TestClass7.testMethod overrides the method Stub.testMethod which is not annotated with @EnforcePermission. \
- The same annotation must be used on Stub.testMethod. Did you forget to annotate the AIDL definition? [MissingEnforcePermissionAnnotation]
- public void testMethod() {}
+ src/test/pkg/TestClass7.java:4: Error: The method TestClass7.testMethod overrides the method IBar.testMethod which is not annotated with @EnforcePermission. \
+ The same annotation must be used on IBar.testMethod. Did you forget to annotate the AIDL definition? [MissingEnforcePermissionAnnotation]
+ public void testMethod() {
~~~~~~~~~~
1 errors, 0 warnings
""".addLineContinuation())
@@ -304,7 +377,9 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
"""
package test.pkg;
public class Default extends IFooMethod.Stub {
- public void testMethod() {}
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -313,23 +388,74 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
.expect(
"""
src/test/pkg/Default.java:3: Error: The method Default.testMethod \
- overrides the method Stub.testMethod which is annotated with @EnforcePermission. The same annotation must be used on Default.testMethod [MissingEnforcePermissionAnnotation]
- public void testMethod() {}
+ overrides the method IFooMethod.testMethod which is annotated with @EnforcePermission. The same annotation must be used on Default.testMethod [MissingEnforcePermissionAnnotation]
+ public void testMethod() {
~~~~~~~~~~
- 1 errors, 0 warnings
+ 1 errors, 0 warnings
""".addLineContinuation()
)
}
- fun testDoesDetectIssuesShortStringsNotAllowed() {
+ fun testDoesNotDetectIssuesShortStringsAllowedInChildAndParent() {
lint().files(java(
"""
package test.pkg;
import android.annotation.EnforcePermission;
public class TestClass121 extends IFooMethod.Stub {
@Override
+ @EnforcePermission("READ_PHONE_STATE")
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void testMethodParentShortPermission() {
+ testMethodParentShortPermission_enforcePermission();
+ }
+ @Override
@EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
- public void testMethodAnyLiteral() {}
+ public void testMethodAnyLiteral() {
+ testMethodAnyLiteral_enforcePermission();
+ }
+ @Override
+ @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
+ public void testMethodAnyLiteralParentsShortPermission() {
+ testMethodAnyLiteralParentsShortPermission_enforcePermission();
+ }
+ }
+ """).indented(),
+ *stubs
+ )
+ .run()
+ .expectClean()
+ }
+
+ fun testDoesDetectIssuesWrongShortStringsInChildAndParent() {
+ lint().files(java(
+ """
+ package test.pkg;
+ import android.annotation.EnforcePermission;
+ public class TestClass121 extends IFooMethod.Stub {
+ @Override
+ @EnforcePermission("READ_WRONG_PHONE_STATE")
+ public void testMethod() {
+ testMethod_enforcePermission();
+ }
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_WRONG_PHONE_STATE)
+ public void testMethodParentShortPermission() {
+ testMethodParentShortPermission_enforcePermission();
+ }
+ @Override
+ @EnforcePermission(anyOf={"WRONG_INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteral() {
+ testMethodAnyLiteral_enforcePermission();
+ }
+ @Override
+ @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_WRONG_PHONE_STATE})
+ public void testMethodAnyLiteralParentsShortPermission() {
+ testMethodAnyLiteralParentsShortPermission_enforcePermission();
+ }
}
""").indented(),
*stubs
@@ -337,14 +463,19 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
.run()
.expect(
"""
- src/test/pkg/TestClass121.java:6: Error: The method \
- TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}) \
- which differs from the overridden method Stub.testMethodAnyLiteral: \
- @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). \
- The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
- public void testMethodAnyLiteral() {}
- ~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
+ src/test/pkg/TestClass121.java:6: Error: The method TestClass121.testMethod is annotated with @EnforcePermission("READ_WRONG_PHONE_STATE") which differs from the overridden method IFooMethod.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethod() {
+ ~~~~~~~~~~
+ src/test/pkg/TestClass121.java:11: Error: The method TestClass121.testMethodParentShortPermission is annotated with @EnforcePermission(android.Manifest.permission.READ_WRONG_PHONE_STATE) which differs from the overridden method IFooMethod.testMethodParentShortPermission: @android.annotation.EnforcePermission("READ_PHONE_STATE"). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodParentShortPermission() {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass121.java:16: Error: The method TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"WRONG_INTERNET", "READ_PHONE_STATE"}) which differs from the overridden method IFooMethod.testMethodAnyLiteral: @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodAnyLiteral() {
+ ~~~~~~~~~~~~~~~~~~~~
+ src/test/pkg/TestClass121.java:21: Error: The method TestClass121.testMethodAnyLiteralParentsShortPermission is annotated with @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_WRONG_PHONE_STATE}) which differs from the overridden method IFooMethod.testMethodAnyLiteralParentsShortPermission: @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+ public void testMethodAnyLiteralParentsShortPermission() {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 4 errors, 0 warnings
""".addLineContinuation()
)
}
@@ -356,28 +487,17 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
"""
public interface IFooMethod extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements IFooMethod {
- @Override
- @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
- public void testMethod() {}
- @Override
- @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAny() {}
- @Override
- @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
- public void testMethodAnyLiteral() {}
- @Override
- @android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
- public void testMethodAll() {}
- @Override
- @android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
- public void testMethodAllLiteral() {}
}
@android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
public void testMethod();
+ @android.annotation.EnforcePermission("READ_PHONE_STATE")
+ public void testMethodParentShortPermission();
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAny() {}
@android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
public void testMethodAnyLiteral() {}
+ @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
+ public void testMethodAnyLiteralParentsShortPermission() {}
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
public void testMethodAll() {}
@android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
@@ -391,8 +511,6 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
"""
public interface IBar extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements IBar {
- @Override
- public void testMethod() {}
}
public void testMethod();
}
@@ -404,6 +522,7 @@ class EnforcePermissionDetectorTest : LintDetectorTest() {
package android.Manifest;
class permission {
public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+ public static final String READ_WRONG_PHONE_STATE = "android.permission.READ_WRONG_PHONE_STATE";
public static final String NFC = "android.permission.NFC";
public static final String INTERNET = "android.permission.INTERNET";
}
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt
index 5a63bb4084d2..a4b0bc3a7c76 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorCodegenTest.kt
@@ -25,10 +25,11 @@ import com.android.tools.lint.detector.api.Issue
@Suppress("UnstableApiUsage")
class EnforcePermissionHelperDetectorCodegenTest : LintDetectorTest() {
- override fun getDetector(): Detector = EnforcePermissionHelperDetector()
+ override fun getDetector(): Detector = EnforcePermissionDetector()
override fun getIssues(): List<Issue> = listOf(
- EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION
)
override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt
index 10a6e1da91dc..64e2bfbad7bb 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionHelperDetectorTest.kt
@@ -20,10 +20,10 @@ import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.checks.infrastructure.TestLintTask
class EnforcePermissionHelperDetectorTest : LintDetectorTest() {
- override fun getDetector() = EnforcePermissionHelperDetector()
+ override fun getDetector() = EnforcePermissionDetector()
override fun getIssues() = listOf(
- EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
- EnforcePermissionHelperDetector.ISSUE_MISUSING_ENFORCE_PERMISSION
+ EnforcePermissionDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
+ EnforcePermissionDetector.ISSUE_MISUSING_ENFORCE_PERMISSION
)
override fun lint(): TestLintTask = super.lint().allowMissingSdk()