summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp20
-rw-r--r--apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java20
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java2
-rw-r--r--apex/permission/Android.bp33
-rw-r--r--apex/permission/OWNERS6
-rw-r--r--apex/permission/apex_manifest.json4
-rw-r--r--apex/permission/com.android.permission.avbpubkeybin0 -> 1032 bytes
-rw-r--r--apex/permission/com.android.permission.pem51
-rw-r--r--apex/permission/com.android.permission.pk8bin0 -> 2376 bytes
-rw-r--r--apex/permission/com.android.permission.x509.pem35
-rw-r--r--apex/statsd/.clang-format17
-rw-r--r--api/current.txt50
-rw-r--r--api/system-current.txt34
-rw-r--r--api/test-current.txt1
-rw-r--r--cmds/incidentd/src/Section.cpp16
-rw-r--r--cmds/statsd/benchmark/log_event_benchmark.cpp4
-rw-r--r--cmds/statsd/src/logd/LogEvent.cpp4
-rw-r--r--core/java/android/annotation/UnsupportedAppUsage.java9
-rw-r--r--core/java/android/app/Activity.java9
-rw-r--r--core/java/android/app/AppOpsManager.java12
-rw-r--r--core/java/android/app/contentsuggestions/ContentSuggestionsManager.java13
-rw-r--r--core/java/android/bluetooth/le/ScanRecord.java2
-rw-r--r--core/java/android/content/ClipData.java5
-rw-r--r--core/java/android/content/ContentProvider.java17
-rw-r--r--core/java/android/content/pm/VerificationParams.java211
-rw-r--r--core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java14
-rw-r--r--core/java/android/net/NetworkCapabilities.java44
-rw-r--r--core/java/android/net/NetworkMisc.java6
-rw-r--r--core/java/android/os/Environment.java15
-rw-r--r--core/java/android/os/UserManager.java42
-rw-r--r--core/java/android/os/image/DynamicSystemManager.java9
-rw-r--r--core/java/android/os/image/IDynamicSystemService.aidl7
-rw-r--r--core/java/android/provider/DeviceConfig.java9
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/service/contentsuggestions/ContentSuggestionsService.java15
-rw-r--r--core/java/android/service/euicc/EuiccService.java2
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java8
-rw-r--r--core/java/android/util/LongSparseArray.java65
-rw-r--r--core/java/android/util/LongSparseLongArray.java10
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java7
-rw-r--r--core/java/android/view/InsetsState.java11
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java21
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java103
-rw-r--r--core/java/com/android/internal/compat/IPlatformCompat.aidl8
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java12
-rw-r--r--core/proto/android/app/settings_enums.proto10
-rw-r--r--core/proto/android/providers/settings/global.proto1
-rw-r--r--core/proto/android/server/notificationhistory.proto18
-rw-r--r--core/proto/android/service/usb.proto68
-rw-r--r--core/res/res/values/strings.xml9
-rw-r--r--core/res/res/values/symbols.xml3
-rw-r--r--core/res/res/xml/sms_short_codes.xml2
-rw-r--r--core/tests/coretests/src/android/content/pm/VerificationParamsTest.java196
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java17
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java31
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--keystore/java/android/security/Credentials.java14
-rw-r--r--libs/hwui/Android.bp2
-rw-r--r--libs/hwui/VectorDrawable.cpp120
-rw-r--r--libs/hwui/VectorDrawable.h33
-rw-r--r--libs/hwui/pipeline/skia/SkiaDisplayList.cpp5
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp23
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.h13
-rw-r--r--libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp283
-rw-r--r--libs/hwui/pipeline/skia/VectorDrawableAtlas.h212
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp20
-rw-r--r--libs/hwui/renderthread/CacheManager.h11
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp1
-rw-r--r--libs/hwui/renderthread/CanvasContext.h3
-rw-r--r--libs/hwui/renderthread/IRenderPipeline.h1
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp22
-rw-r--r--libs/hwui/renderthread/RenderProxy.h4
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp33
-rw-r--r--libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp163
-rw-r--r--location/TEST_MAPPING10
-rw-r--r--location/java/android/location/ILocationManager.aidl19
-rw-r--r--location/java/android/location/LocationManager.java32
-rw-r--r--location/java/android/location/LocationRequest.java6
-rw-r--r--media/java/android/media/AudioSystem.java47
-rw-r--r--media/java/android/media/MediaPlayer.java20
-rw-r--r--media/java/android/media/audiopolicy/AudioPolicy.java4
-rw-r--r--media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl4
-rw-r--r--media/java/android/media/midi/MidiDeviceInfo.aidl2
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java51
-rw-r--r--media/jni/Android.bp24
-rw-r--r--media/jni/android_media_tv_Tuner.cpp135
-rw-r--r--media/jni/android_media_tv_Tuner.h43
-rw-r--r--media/jni/audioeffect/Android.bp5
-rw-r--r--media/jni/audioeffect/Visualizer.cpp446
-rw-r--r--media/jni/audioeffect/Visualizer.h179
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp2
-rw-r--r--media/jni/audioeffect/exports.lds6
-rw-r--r--media/jni/soundpool/Android.bp11
-rw-r--r--media/jni/soundpool/Sound.cpp241
-rw-r--r--media/jni/soundpool/Sound.h93
-rw-r--r--media/jni/soundpool/SoundDecoder.cpp115
-rw-r--r--media/jni/soundpool/SoundDecoder.h51
-rw-r--r--media/jni/soundpool/SoundManager.cpp104
-rw-r--r--media/jni/soundpool/SoundManager.h110
-rw-r--r--media/jni/soundpool/SoundPool.cpp1160
-rw-r--r--media/jni/soundpool/SoundPool.h248
-rw-r--r--media/jni/soundpool/SoundPoolThread.cpp114
-rw-r--r--media/jni/soundpool/SoundPoolThread.h66
-rw-r--r--media/jni/soundpool/Stream.cpp448
-rw-r--r--media/jni/soundpool/Stream.h150
-rw-r--r--media/jni/soundpool/StreamManager.cpp407
-rw-r--r--media/jni/soundpool/StreamManager.h467
-rw-r--r--media/jni/soundpool/tests/Android.bp28
-rwxr-xr-xmedia/jni/soundpool/tests/build_and_run.sh30
-rw-r--r--media/jni/soundpool/tests/soundpool_stress.cpp309
-rw-r--r--media/native/midi/Android.bp7
-rw-r--r--media/native/midi/MidiDeviceInfo.cpp138
-rw-r--r--media/native/midi/MidiDeviceInfo.h81
-rw-r--r--media/native/midi/amidi.cpp2
-rw-r--r--native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl6
-rw-r--r--packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml7
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java3
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java103
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java247
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java12
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java1
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java100
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java6
-rw-r--r--packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java12
-rw-r--r--packages/SettingsLib/res/values/strings.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java9
-rw-r--r--packages/SettingsProvider/OWNERS3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java72
-rw-r--r--packages/SystemUI/res/layout/home_controls.xml7
-rw-r--r--packages/SystemUI/res/layout/people_strip.xml17
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/LatencyTester.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeHost.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt121
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java465
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java361
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java128
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java116
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java224
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java132
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java5
-rw-r--r--packages/Tethering/Android.bp79
-rw-r--r--packages/Tethering/AndroidManifest.xml29
-rw-r--r--packages/Tethering/AndroidManifestBase.xml29
-rw-r--r--packages/Tethering/common/TetheringLib/Android.bp40
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl20
-rw-r--r--packages/Tethering/proguard.flags1
-rw-r--r--packages/Tethering/res/values/config.xml7
-rw-r--r--packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java (renamed from services/net/java/android/net/dhcp/DhcpServerCallbacks.java)0
-rw-r--r--packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java (renamed from services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java)0
-rw-r--r--packages/Tethering/src/android/net/ip/IpServer.java (renamed from services/net/java/android/net/ip/IpServer.java)69
-rw-r--r--packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java (renamed from services/net/java/android/net/ip/RouterAdvertisementDaemon.java)155
-rw-r--r--packages/Tethering/src/android/net/util/InterfaceSet.java (renamed from services/net/java/android/net/util/InterfaceSet.java)2
-rw-r--r--packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java (renamed from services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java)11
-rw-r--r--packages/Tethering/tests/unit/Android.bp50
-rw-r--r--packages/Tethering/tests/unit/AndroidManifest.xml26
-rw-r--r--packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java (renamed from tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java)0
-rw-r--r--packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java (renamed from tests/net/java/android/net/ip/IpServerTest.java)4
-rw-r--r--packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java (renamed from tests/net/java/android/net/util/InterfaceSetTest.java)0
-rw-r--r--packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java (renamed from tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java)36
-rw-r--r--proto/src/system_messages.proto2
-rw-r--r--services/art-profile145
-rw-r--r--services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java19
-rw-r--r--services/core/java/android/app/usage/UsageStatsManagerInternal.java (renamed from core/java/android/app/usage/UsageStatsManagerInternal.java)31
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java8
-rw-r--r--services/core/java/com/android/server/AppStateTracker.java14
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java83
-rw-r--r--services/core/java/com/android/server/DynamicSystemService.java16
-rw-r--r--services/core/java/com/android/server/GnssManagerService.java18
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java83
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java2
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java4
-rw-r--r--services/core/java/com/android/server/am/UserController.java4
-rw-r--r--services/core/java/com/android/server/attention/AttentionManagerService.java7
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java515
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java3
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java104
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java30
-rw-r--r--services/core/java/com/android/server/backup/SystemBackupAgent.java (renamed from core/java/com/android/server/backup/SystemBackupAgent.java)0
-rw-r--r--services/core/java/com/android/server/backup/UsageStatsBackupHelper.java (renamed from core/java/com/android/server/backup/UsageStatsBackupHelper.java)0
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java12
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java18
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompatNative.java8
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkNotificationManager.java21
-rw-r--r--services/core/java/com/android/server/location/CallerIdentity.java5
-rw-r--r--services/core/java/com/android/server/location/GeofenceManager.java9
-rw-r--r--services/core/java/com/android/server/location/GeofenceState.java6
-rw-r--r--services/core/java/com/android/server/location/RemoteListenerHelper.java2
-rw-r--r--services/core/java/com/android/server/location/TEST_MAPPING10
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java87
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java12
-rw-r--r--services/core/java/com/android/server/net/TEST_MAPPING31
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java1
-rw-r--r--services/core/java/com/android/server/os/BugreportManagerServiceImpl.java8
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java87
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelperImpl.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java44
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java8
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java23
-rw-r--r--services/core/java/com/android/server/twilight/TwilightService.java25
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java329
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java13
-rw-r--r--services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java40
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioningController.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskRecord.java20
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java21
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java32
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java2
-rw-r--r--services/net/Android.bp20
-rw-r--r--services/net/java/android/net/netlink/InetDiagMessage.java35
-rw-r--r--services/net/java/android/net/netlink/StructInetDiagReqV2.java63
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java13
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java11
-rw-r--r--services/tests/servicestests/res/raw/apex_test.apexbin0 -> 318688 bytes
-rw-r--r--services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java16
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java93
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java298
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java19
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java84
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java38
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java111
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java81
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java27
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsProto.java17
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java17
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java11
-rw-r--r--services/usb/java/com/android/server/usb/UsbPermissionManager.java20
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java2
-rw-r--r--services/usb/java/com/android/server/usb/UsbUserPermissionManager.java165
-rw-r--r--startop/apps/test/Android.bp5
-rw-r--r--startop/apps/test/AndroidManifest.xml24
-rw-r--r--startop/apps/test/src/CPUIntensiveBenchmarkActivity.java30
-rw-r--r--startop/apps/test/src/CPUIntensiveBenchmarks.java (renamed from startop/apps/test/src/CPUIntensive.java)42
-rw-r--r--startop/apps/test/src/InitCheckOverheadBenchmarkActivity.java30
-rw-r--r--startop/apps/test/src/InitCheckOverheadBenchmarks.java134
-rw-r--r--startop/apps/test/src/SystemServerBenchmarkActivity.java20
-rw-r--r--startop/apps/test/src/SystemServerBenchmarks.java16
-rw-r--r--telecomm/java/android/telecom/Call.java4
-rw-r--r--telephony/java/android/telephony/SmsManager.java646
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java26
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java2
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl15
-rw-r--r--tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java19
-rw-r--r--tests/Compatibility/Android.bp3
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java32
-rw-r--r--tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java3
-rw-r--r--tests/net/Android.bp6
-rw-r--r--tests/net/common/java/android/net/NetworkCapabilitiesTest.java2
-rw-r--r--tests/net/java/android/net/netlink/InetDiagSocketTest.java102
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java166
-rw-r--r--tools/bit/main.cpp10
-rw-r--r--tools/stats_log_api_gen/Android.bp2
-rw-r--r--wifi/java/android/net/wifi/ISoftApCallback.aidl8
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl2
-rw-r--r--wifi/java/android/net/wifi/IWifiScanner.aidl2
-rw-r--r--wifi/java/android/net/wifi/WifiClient.aidl19
-rw-r--r--wifi/java/android/net/wifi/WifiClient.java97
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java12
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java73
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java53
-rw-r--r--wifi/java/android/net/wifi/hotspot2/ConfigParser.java6
-rw-r--r--wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java18
-rw-r--r--wifi/java/com/android/server/wifi/BaseWifiService.java5
-rw-r--r--wifi/tests/Android.mk1
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64142
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf62
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64171
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64171
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64171
-rw-r--r--wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base6488
-rw-r--r--wifi/tests/assets/hsr1/README.txt3
-rw-r--r--wifi/tests/src/android/net/wifi/WifiClientTest.java75
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java55
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java33
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java17
-rw-r--r--wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java13
348 files changed, 11151 insertions, 6084 deletions
diff --git a/Android.bp b/Android.bp
index 3b6eaa7a3afe..8169d6de7813 100644
--- a/Android.bp
+++ b/Android.bp
@@ -548,6 +548,12 @@ filegroup {
],
}
+filegroup {
+ name: "framework-tethering-shared-srcs",
+ srcs: [
+ "core/java/android/util/LocalLog.java",
+ ],
+}
// Build ext.jar
// ============================================================
java_library {
@@ -922,7 +928,8 @@ metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.x
"--hide RequiresPermission " +
"--hide MissingPermission --hide BroadcastBehavior " +
"--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " +
- "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo "
+ "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " +
+ "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*"
// http://b/129765390 Rewrite links to "platform" or "technotes" folders
// which are siblings (and thus outside of) {@docRoot}.
@@ -1599,6 +1606,17 @@ filegroup {
}
filegroup {
+ name: "framework-ims-common-shared-srcs",
+ srcs: [
+ "core/java/android/os/AsyncResult.java",
+ "core/java/android/os/RegistrantList.java",
+ "core/java/android/os/Registrant.java",
+ "core/java/com/android/internal/os/SomeArgs.java",
+ "core/java/com/android/internal/util/Preconditions.java",
+ ],
+}
+
+filegroup {
name: "framework-wifistack-shared-srcs",
srcs: [
":framework-annotations",
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index c036c772d7d0..041825c235d0 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -1,9 +1,9 @@
package com.android.server.usage;
+import android.annotation.UserIdInt;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager.StandbyBuckets;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.content.Context;
import android.os.Looper;
@@ -33,6 +33,24 @@ public interface AppStandbyInternal {
}
}
+ /**
+ * Listener interface for notifications that an app's idle state changed.
+ */
+ abstract static class AppIdleStateChangeListener {
+
+ /** Callback to inform listeners that the idle state has changed to a new bucket. */
+ public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
+ boolean idle, int bucket, int reason);
+
+ /**
+ * Optional callback to inform the listener that the app has transitioned into
+ * an active state due to user interaction.
+ */
+ public void onUserInteractionStarted(String packageName, @UserIdInt int userId) {
+ // No-op by default
+ }
+ }
+
void onBootPhase(int phase);
void postCheckIdleStates(int userId);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index c3ffad66d829..a1734d8d25d5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -37,7 +37,6 @@ import android.app.job.JobSnapshot;
import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -103,6 +102,8 @@ import com.android.server.job.controllers.StorageController;
import com.android.server.job.controllers.TimeController;
import com.android.server.job.restrictions.JobRestriction;
import com.android.server.job.restrictions.ThermalStatusRestriction;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import libcore.util.EmptyArray;
@@ -1295,7 +1296,9 @@ public class JobSchedulerService extends com.android.server.SystemService
// Set up the app standby bucketing tracker
mStandbyTracker = new StandbyTracker();
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
- mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
+
+ AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
+ appStandby.addListener(mStandbyTracker);
// The job store needs to call back
publishLocalService(JobSchedulerInternal.class, new LocalService());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 14dce84e686a..cda524418664 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -35,8 +35,6 @@ import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.IUidObserver;
-import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -70,6 +68,8 @@ import com.android.server.job.ConstantsProto;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobServiceContext;
import com.android.server.job.StateControllerProto;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import java.util.ArrayList;
import java.util.List;
@@ -574,9 +574,8 @@ public final class QuotaController extends StateController {
mContext.registerReceiverAsUser(mPackageAddedReceiver, UserHandle.ALL, filter, null, null);
// Set up the app standby bucketing tracker
- UsageStatsManagerInternal usageStats = LocalServices.getService(
- UsageStatsManagerInternal.class);
- usageStats.addAppIdleStateChangeListener(new StandbyTracker());
+ AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class);
+ appStandby.addListener(new StandbyTracker());
try {
ActivityManager.getService().registerUidObserver(mUidObserver,
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index ecc045995521..bcd8be7b63e0 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -53,7 +53,6 @@ import android.app.AppGlobals;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager.StandbyBuckets;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -102,6 +101,7 @@ import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.usage.AppIdleHistory.AppUsageHistory;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import java.io.File;
import java.io.PrintWriter;
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
new file mode 100644
index 000000000000..027481401557
--- /dev/null
+++ b/apex/permission/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+apex {
+ name: "com.android.permission",
+
+ manifest: "apex_manifest.json",
+
+ key: "com.android.permission.key",
+ certificate: ":com.android.permission.certificate",
+}
+
+apex_key {
+ name: "com.android.permission.key",
+ public_key: "com.android.permission.avbpubkey",
+ private_key: "com.android.permission.pem",
+}
+
+android_app_certificate {
+ name: "com.android.permission.certificate",
+ certificate: "com.android.permission",
+}
diff --git a/apex/permission/OWNERS b/apex/permission/OWNERS
new file mode 100644
index 000000000000..957e10a582a0
--- /dev/null
+++ b/apex/permission/OWNERS
@@ -0,0 +1,6 @@
+svetoslavganov@google.com
+moltmann@google.com
+eugenesusla@google.com
+zhanghai@google.com
+evanseverson@google.com
+ntmyren@google.com
diff --git a/apex/permission/apex_manifest.json b/apex/permission/apex_manifest.json
new file mode 100644
index 000000000000..2a8c4f737dff
--- /dev/null
+++ b/apex/permission/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.permission",
+ "version": 1
+}
diff --git a/apex/permission/com.android.permission.avbpubkey b/apex/permission/com.android.permission.avbpubkey
new file mode 100644
index 000000000000..9eaf85259637
--- /dev/null
+++ b/apex/permission/com.android.permission.avbpubkey
Binary files differ
diff --git a/apex/permission/com.android.permission.pem b/apex/permission/com.android.permission.pem
new file mode 100644
index 000000000000..3d584be5440d
--- /dev/null
+++ b/apex/permission/com.android.permission.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKgIBAAKCAgEA6snt4eqoz85xiL9Sf6w1S1b9FgSHK05zYTh2JYPvQKQ3yeZp
+E6avJ6FN6XcbmkDzSd658BvUGDBSPhOlzuUO4BsoKBuLMxP6TxIQXFKidzDqY0vQ
+4qkS++bdIhUjwBP3OSZ3Czu0BiihK8GC75Abr//EyCyObGIGGfHEGANiOgrpP4X5
++OmLzQLCjk4iE1kg+U6cRSRI/XLaoWC0TvIIuzxznrQ6r5GmzgTOwyBWyIB+bj73
+bmsweHTU+w9Y7kGOx4hO3XCLIhoBWEw0EbuW9nZmQ4sZls5Jo/CbyJlCclF11yVo
+SCf2LG/T+9pah5NOmDQ1kPbU+0iKZIV4YFHGTIhyGDE/aPOuUT05ziCGDifgHr0u
+SG1x/RLqsVh/POvNxnvP9cQFMQ08BvbEJaTTgB785iwKsvdqCfmng/SAyxSetmzP
+StXVB3fh1OoZ8vunRbQYxnmUxycVqaA96zmBx2wLvbvzKo7pZFDE6nbhnT5+MRAM
+z/VIK89W26uB4gj8sBFslqZjT0jPqsAZuvDm7swOtMwIcEolyGJuFLqlhN7UwMz2
+9y8+IpYixR+HvD1TZI9NtmuCmv3kPrWgoMZg6yvaBayTIr8RdYzi6FO/C1lLiraz
+48dH3sXWRa8cgw6VcSUwYrEBIc3sotdsupO1iOjcFybIwaee0YTZJfjvbqkCAwEA
+AQKCAgEArRnfdpaJi1xLPGTCMDsIt9kUku0XswgN7PmxsYsKFAB+2S40/jYAIRm9
+1YjpItsMA8RgFfSOdJ77o6TctCMQyo17F8bm4+uwuic5RLfv7Cx2QmsdQF8jDfFx
+y7UGPJD7znjbf76uxXOjEB2FqZX3s9TAgkzHXIUQtoQW7RVhkCWHPjxKxgd5+NY2
+FrDoUpd9xhD9CcTsw1+wbRZdGW88nL6/B50dP2AFORM2VYo8MWr6y9FEn3YLsGOC
+uu7fxBk1aUrHyl81VRkTMMROB1zkuiUk1FtzrEm+5U15rXXBFYOVe9+qeLhtuOlh
+wueDoz0pzvF/JLe24uTik6YL0Ae6SD0pFXQ2KDrdH3cUHLok3r76/yGzaDNTFjS2
+2WbQ8dEJV8veNHk8gjGpFTJIsBUlcZpmUCDHlfvVMb3+2ahQ+28piQUt5t3zqJdZ
+NDqsOHzY6BRPc+Wm85Xii/lWiQceZSee/b1Enu+nchsyXhSenBfC6bIGZReyMI0K
+KKKuVhyR6OSOiR5ZdZ/NyXGqsWy05fn/h0X9hnpETsNaNYNKWvpHLfKll+STJpf7
+AZquJPIclQyiq5NONx6kfPztoCLkKV/zOgIj3Sx5oSZq+5gpO91nXWVwkTbqK1d1
+004q2Mah6UQyAk1XGQc2pHx7ouVcWawjU30vZ4C015Hv2lm/gVkCggEBAPltATYS
+OqOSL1YAtIHPiHxMjNAgUdglq8JiJFXVfkocGU9eNub3Ed3sSWu6GB9Myu/sSKje
+bJ5DZqxJnvB2Fqmu9I9OunLGFSD0aXs4prwsQ1Rm5FcbImtrxcciASdkoo8Pj0z4
+vk2r2NZD3VtER5Uh+YjSDkxcS9gBStXUpCL6gj69UpOxMmWqZVjyHatVB4lEvYJl
+N82uT7N7QVNL1DzcZ9z4C4r7ks1Pm7ka12s5m/oaAlAMdVeofiPJe1xA9zRToSr4
+tIbMkOeXFLVRLuji/7XsOgal5Rl59p+OwLshX5cswPVOMrH6zt+hbsJ5q8M5dqnX
+VAOBK7KNQ/EKZwcCggEBAPD6KVvyCim46n5EbcEqCkO7gevwZkw/9vLwmM5YsxTh
+z9FQkPO0iB7mwbX8w04I91Pre4NdfcgMG0pP1b13Sb4KHBchqW1a+TCs3kSGC6gn
+1SxmXHnA9jRxAkrWlGkoAQEz+aP61cXiiy2tXpQwJ8xQCKprfoqWZwhkCtEVU6CE
+S7v9cscOHIqgNxx4WoceMmq4EoihHAZzHxTcNVbByckMjb2XQJ0iNw3lDP4ddvc+
+a4HzHfHkhzeQ5ZNc8SvWU8z80aSCOKRsSD3aUTZzxhZ4O2tZSW7v7p+FpvVee7bC
+g8YCfszTdpVUMlLRLjScimAcovcFLSvtyupinxWg4M8CggEAN9YGEmOsSte7zwXj
+YrfhtumwEBtcFwX/2Ej+F1Tuq4p0xAa0RaoDjumJWhtTsRYQy/raHSuFpzwxbNoi
+QXQ+CIhI6RfXtz/OlQ0B2/rHoJJMFEXgUfuaDfAXW0eqeHYXyezSyIlamKqipPyW
+Pgsf9yue39keKEv1EorfhNTQVaA8rezV4oglXwrxGyNALw2e3UTNI7ai8mFWKDis
+XAg6n9E7UwUYGGnO6DUtCBgRJ0jDOQ6/e8n+LrxiWIKPIgzNCiK6jpMUXqTGv4Fb
+umdNGAdQ9RnHt5tFmRlrczaSwJFtA7uaCpAR2zPpQbiywchZAiAIB2dTwGEXNiZX
+kksg2wKCAQEA6pNad3qhkgPDoK6T+Jkn7M82paoaqtcJWWwEE7oceZNnbWZz9Agl
+CY+vuawXonrv5+0vCq2Tp4zBdBFLC2h3jFrjBVFrUFxifpOIukOSTVqZFON/2bWQ
+9XOcu6UuSz7522Xw+UNPnZXtzcUacD6AP08ZYGvLfrTyDyTzspyED5k48ALEHCkM
+d5WGkFxII4etpF0TDZVnZo/iDbhe49k4yFFEGO6Ho26PESOLBkNAb2V/2bwDxlij
+l9+g21Z6HiZA5SamHPH2mXgeyrcen1cL2QupK9J6vVcqfnboE6qp2zp2c+Yx8MlY
+gfy4EA44YFaSDQVTTgrn8f9Eq+zc130H2QKCAQEAqOKgv68nIPdDSngNyCVyWego
+boFiDaEJoBBg8FrBjTJ6wFLrNAnXmbvfTtgNmNAzF1cUPJZlIIsHgGrMCfpehbXq
+WQQIw+E+yFbTGLxseGRfsLrV0CsgnAoOVeod+yIHmqc3livaUbrWhL1V2f6Ue+sE
+7YLp/iP43NaMfA4kYk2ep7+ZJoEVkCjHJJaHWgAG3RynPJHkTJlSgu7wLYvGc9uE
+ZsEFUM46lX02t7rrtMfasVGrUy1c2xOxFb4v1vG6iEZ7+YWeq5o3AkxUwEGn+mG4
+/3p+k4AaTXJDXgyZ0Sv6CkGuPHenAYG4cswcUUEf/G4Ag77x6LBNMgycJBxUJA==
+-----END RSA PRIVATE KEY-----
diff --git a/apex/permission/com.android.permission.pk8 b/apex/permission/com.android.permission.pk8
new file mode 100644
index 000000000000..d51673dbc2fc
--- /dev/null
+++ b/apex/permission/com.android.permission.pk8
Binary files differ
diff --git a/apex/permission/com.android.permission.x509.pem b/apex/permission/com.android.permission.x509.pem
new file mode 100644
index 000000000000..4b146c9edd4f
--- /dev/null
+++ b/apex/permission/com.android.permission.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGKzCCBBOgAwIBAgIUezo3fQeVZsmLpm/dkpGWJ/G/MN8wDQYJKoZIhvcNAQEL
+BQAwgaMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMR8wHQYDVQQDDBZjb20uYW5kcm9pZC5wZXJtaXNzaW9uMSIwIAYJKoZIhvcN
+AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMCAXDTE5MTAwOTIxMzExOVoYDzQ3NTcw
+OTA0MjEzMTE5WjCBozELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx
+FjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNV
+BAsMB0FuZHJvaWQxHzAdBgNVBAMMFmNvbS5hbmRyb2lkLnBlcm1pc3Npb24xIjAg
+BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCxefguRJ7E6tBCTEOeU2HJEGs6AQQapLz9hMed0aaJ
+Qr7aTQiYJEk+sG4+jPYbjpxa8JDDzJHp+4g7DjfSb+dvT9n84A8lWaI/yRXTZTQN
+Hu5m/bgHhi0LbySpiaFyodXBKUAnOhZyGPtYjtBFywFylueub8ryc1Z6UxxU7udH
+1mkIr7sE48Qkq5SyjFROE96iFmYA+vS/JXOfS0NBHiMB4GBxx4V7kXpvrTI7hhZG
+HiyhKvNh7wyHIhO9nDEw1rwtAH6CsL3YkQEVBeAU98m+0Au+qStLYkKHh2l8zT4W
+7sVK1VSqfB+VqOUmeIGdzlBfqMsoXD+FJz6KnIdUHIwjFDjL7Xr+hd+7xve+Q3S+
+U3Blk/U6atY8PM09wNfilG+SvwcKk5IgriDcu3rWKgIFxbUUaxLrDW7pLlu6wt/d
+GGtKK+Bc0jF+9Z901Tl33i5xhc5yOktT0btkKs7lSeE6VzP/Nk5g0SuzixmuRoh9
+f5Ge41N2ZCEHNXx3wZeVZwHIIPfYrL7Yql1Xoxbfs4ETFk6ChzVQcvjfDQQuK58J
+uNc+TOCoI/qflXwGCwpuHl0ier8V5Z4tpMUl5rWyVR/QGRtLPvs2lLuxczDw1OXq
+wEVtCMn9aNnd4y7R9PZ52hi53HAvDjpWefrLYi+Q04J6iGFQ1qAFBClK9DquBvmR
+swIDAQABo1MwUTAdBgNVHQ4EFgQULpfus5s5SrqLkoUKyPXA0D1iHPMwHwYDVR0j
+BBgwFoAULpfus5s5SrqLkoUKyPXA0D1iHPMwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQsFAAOCAgEAjxQG5EFv8V/9yV2glI53VOmlWMjfEgvUjd39s/XLyPlr
+OzPOKSB0NFo8To3l4l+MsManxPK8y0OyfEVKbWVz9onv0ovo5MVokBmV/2G0jmsV
+B4e9yjOq+DmqIvY/Qh63Ywb97sTgcFI8620MhQDbh2IpEGv4ZNV0H6rgXmgdSCBw
+1EjBoYfFpN5aMgZjeyzZcq+d1IapdWqdhuEJQkMvoYS4WIumNIJlEXPQRoq/F5Ih
+nszdbKI/jVyiGFa2oeZ3rja1Y6GCRU8TYEoKx1pjS8uQDOEDTwsG/QnUe9peEj0V
+SsCkIidJWTomAmq9Tub9vpBe1zuTpuRAwxwR0qwgSxozV1Mvow1dJ19oFtHX0yD6
+ZjCpRn5PW9kMvSWSlrcrFs1NJf0j1Cvf7bHpkEDqLqpMnnh9jaFQq3nzDY+MWcIR
+jDcgQpI+AiE2/qtauZnFEVhbce49nCnk9+5bpTTIZJdzqeaExe5KXHwEtZLaEDh4
+atLY9LuEvPsjmDIMOR6hycD9FvwGXhJOQBjESIWFwigtSb1Yud9n6201jw3MLJ4k
++WhkbmZgWy+xc+Mdm5H3XyB1lvHaHGkxu+QB9KyQuVQKwbUVcbwZIfTFPN6Zr/dS
+ZXJqAbBhG/dBgF0LazuLaPVpibi+a3Y+tb9b8eXGkz4F97PWZIEDkELQ+9KOvhc=
+-----END CERTIFICATE-----
diff --git a/apex/statsd/.clang-format b/apex/statsd/.clang-format
new file mode 100644
index 000000000000..cead3a079435
--- /dev/null
+++ b/apex/statsd/.clang-format
@@ -0,0 +1,17 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: false
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+AccessModifierOffset: -4
+IncludeCategories:
+ - Regex: '^"Log\.h"'
+ Priority: -1
diff --git a/api/current.txt b/api/current.txt
index 54d33a2c59b6..fb65243b8e97 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9490,6 +9490,7 @@ package android.content {
method @Nullable public android.database.Cursor query(@NonNull android.net.Uri, @Nullable String[], @Nullable String, @Nullable String[], @Nullable String, @Nullable android.os.CancellationSignal);
method @Nullable public android.database.Cursor query(@NonNull android.net.Uri, @Nullable String[], @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal);
method public boolean refresh(android.net.Uri, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal);
+ method @NonNull public final android.content.Context requireContext();
method public final void restoreCallingIdentity(@NonNull android.content.ContentProvider.CallingIdentity);
method protected final void setPathPermissions(@Nullable android.content.pm.PathPermission[]);
method protected final void setReadPermission(@Nullable String);
@@ -30343,6 +30344,8 @@ package android.net.wifi.hotspot2 {
method public int describeContents();
method public android.net.wifi.hotspot2.pps.Credential getCredential();
method public android.net.wifi.hotspot2.pps.HomeSp getHomeSp();
+ method public long getSubscriptionExpirationTimeInMillis();
+ method public boolean isOsuProvisioned();
method public void setCredential(android.net.wifi.hotspot2.pps.Credential);
method public void setHomeSp(android.net.wifi.hotspot2.pps.HomeSp);
method public void writeToParcel(android.os.Parcel, int);
@@ -44989,13 +44992,60 @@ package android.telephony {
field public static final int MMS_ERROR_RETRY = 6; // 0x6
field public static final int MMS_ERROR_UNABLE_CONNECT_MMS = 3; // 0x3
field public static final int MMS_ERROR_UNSPECIFIED = 1; // 0x1
+ field public static final int RESULT_BLUETOOTH_DISCONNECTED = 27; // 0x1b
+ field public static final int RESULT_CANCELLED = 23; // 0x17
+ field public static final int RESULT_ENCODING_ERROR = 18; // 0x12
+ field public static final int RESULT_ERROR_FDN_CHECK_FAILURE = 6; // 0x6
field public static final int RESULT_ERROR_GENERIC_FAILURE = 1; // 0x1
field public static final int RESULT_ERROR_LIMIT_EXCEEDED = 5; // 0x5
+ field public static final int RESULT_ERROR_NONE = 0; // 0x0
field public static final int RESULT_ERROR_NO_SERVICE = 4; // 0x4
field public static final int RESULT_ERROR_NULL_PDU = 3; // 0x3
field public static final int RESULT_ERROR_RADIO_OFF = 2; // 0x2
field public static final int RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8; // 0x8
field public static final int RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7; // 0x7
+ field public static final int RESULT_INTERNAL_ERROR = 21; // 0x15
+ field public static final int RESULT_INVALID_ARGUMENTS = 11; // 0xb
+ field public static final int RESULT_INVALID_BLUETOOTH_ADDRESS = 26; // 0x1a
+ field public static final int RESULT_INVALID_SMSC_ADDRESS = 19; // 0x13
+ field public static final int RESULT_INVALID_SMS_FORMAT = 14; // 0xe
+ field public static final int RESULT_INVALID_STATE = 12; // 0xc
+ field public static final int RESULT_MODEM_ERROR = 16; // 0x10
+ field public static final int RESULT_NETWORK_ERROR = 17; // 0x11
+ field public static final int RESULT_NETWORK_REJECT = 10; // 0xa
+ field public static final int RESULT_NO_BLUETOOTH_SERVICE = 25; // 0x19
+ field public static final int RESULT_NO_DEFAULT_SMS_APP = 32; // 0x20
+ field public static final int RESULT_NO_MEMORY = 13; // 0xd
+ field public static final int RESULT_NO_RESOURCES = 22; // 0x16
+ field public static final int RESULT_OPERATION_NOT_ALLOWED = 20; // 0x14
+ field public static final int RESULT_RADIO_NOT_AVAILABLE = 9; // 0x9
+ field public static final int RESULT_REMOTE_EXCEPTION = 31; // 0x1f
+ field public static final int RESULT_REQUEST_NOT_SUPPORTED = 24; // 0x18
+ field public static final int RESULT_RIL_CANCELLED = 119; // 0x77
+ field public static final int RESULT_RIL_ENCODING_ERR = 109; // 0x6d
+ field public static final int RESULT_RIL_INTERNAL_ERR = 113; // 0x71
+ field public static final int RESULT_RIL_INVALID_ARGUMENTS = 104; // 0x68
+ field public static final int RESULT_RIL_INVALID_MODEM_STATE = 115; // 0x73
+ field public static final int RESULT_RIL_INVALID_SMSC_ADDRESS = 110; // 0x6e
+ field public static final int RESULT_RIL_INVALID_SMS_FORMAT = 107; // 0x6b
+ field public static final int RESULT_RIL_INVALID_STATE = 103; // 0x67
+ field public static final int RESULT_RIL_MODEM_ERR = 111; // 0x6f
+ field public static final int RESULT_RIL_NETWORK_ERR = 112; // 0x70
+ field public static final int RESULT_RIL_NETWORK_NOT_READY = 116; // 0x74
+ field public static final int RESULT_RIL_NETWORK_REJECT = 102; // 0x66
+ field public static final int RESULT_RIL_NO_MEMORY = 105; // 0x69
+ field public static final int RESULT_RIL_NO_RESOURCES = 118; // 0x76
+ field public static final int RESULT_RIL_OPERATION_NOT_ALLOWED = 117; // 0x75
+ field public static final int RESULT_RIL_RADIO_NOT_AVAILABLE = 100; // 0x64
+ field public static final int RESULT_RIL_REQUEST_NOT_SUPPORTED = 114; // 0x72
+ field public static final int RESULT_RIL_REQUEST_RATE_LIMITED = 106; // 0x6a
+ field public static final int RESULT_RIL_SIM_ABSENT = 120; // 0x78
+ field public static final int RESULT_RIL_SMS_SEND_FAIL_RETRY = 101; // 0x65
+ field public static final int RESULT_RIL_SYSTEM_ERR = 108; // 0x6c
+ field public static final int RESULT_SMS_BLOCKED_DURING_EMERGENCY = 29; // 0x1d
+ field public static final int RESULT_SMS_SEND_RETRY_FAILED = 30; // 0x1e
+ field public static final int RESULT_SYSTEM_ERROR = 15; // 0xf
+ field public static final int RESULT_UNEXPECTED_EVENT_STOP_SENDING = 28; // 0x1c
field public static final int STATUS_ON_ICC_FREE = 0; // 0x0
field public static final int STATUS_ON_ICC_READ = 1; // 0x1
field public static final int STATUS_ON_ICC_SENT = 5; // 0x5
diff --git a/api/system-current.txt b/api/system-current.txt
index ad9a04d16a7e..f744eeb0bb5a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4733,10 +4733,18 @@ package android.net.wifi {
field @Deprecated public byte id;
}
+ public final class WifiClient implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiClient> CREATOR;
+ }
+
@Deprecated public class WifiConfiguration implements android.os.Parcelable {
method @Deprecated public boolean hasNoInternetAccess();
method @Deprecated public boolean isEphemeral();
method @Deprecated public boolean isNoInternetAccessExpected();
+ field @Deprecated public boolean allowAutojoin;
field @Deprecated public String creatorName;
field @Deprecated public int creatorUid;
field @Deprecated public String lastUpdateName;
@@ -4759,6 +4767,7 @@ package android.net.wifi {
public class WifiManager {
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
+ method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void allowAutojoin(int, boolean);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
method @Deprecated @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
@@ -4845,6 +4854,7 @@ package android.net.wifi {
public class WifiScanner {
method @Deprecated public void configureWifiChange(int, int, int, int, int, android.net.wifi.WifiScanner.BssidInfo[]);
method @Deprecated public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
+ method @Nullable @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<java.lang.Integer> getAvailableChannels(int);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults();
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource);
@@ -5645,6 +5655,8 @@ package android.os {
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isPrimaryUser();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile(@NonNull android.os.UserHandle);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isUserUnlockingOrUnlocked(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean removeUser(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(@NonNull android.graphics.Bitmap);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(@Nullable String);
@@ -5939,6 +5951,7 @@ package android.provider {
field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
+ field public static final String NAMESPACE_PERMISSIONS = "permissions";
field public static final String NAMESPACE_PRIVACY = "privacy";
field public static final String NAMESPACE_ROLLBACK = "rollback";
field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
@@ -8217,24 +8230,6 @@ package android.telephony {
method public boolean disableCellBroadcastRange(int, int, int);
method public boolean enableCellBroadcastRange(int, int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
- field public static final int RESULT_CANCELLED = 23; // 0x17
- field public static final int RESULT_ENCODING_ERROR = 18; // 0x12
- field public static final int RESULT_ERROR_FDN_CHECK_FAILURE = 6; // 0x6
- field public static final int RESULT_ERROR_NONE = 0; // 0x0
- field public static final int RESULT_INTERNAL_ERROR = 21; // 0x15
- field public static final int RESULT_INVALID_ARGUMENTS = 11; // 0xb
- field public static final int RESULT_INVALID_SMSC_ADDRESS = 19; // 0x13
- field public static final int RESULT_INVALID_SMS_FORMAT = 14; // 0xe
- field public static final int RESULT_INVALID_STATE = 12; // 0xc
- field public static final int RESULT_MODEM_ERROR = 16; // 0x10
- field public static final int RESULT_NETWORK_ERROR = 17; // 0x11
- field public static final int RESULT_NETWORK_REJECT = 10; // 0xa
- field public static final int RESULT_NO_MEMORY = 13; // 0xd
- field public static final int RESULT_NO_RESOURCES = 22; // 0x16
- field public static final int RESULT_OPERATION_NOT_ALLOWED = 20; // 0x14
- field public static final int RESULT_RADIO_NOT_AVAILABLE = 9; // 0x9
- field public static final int RESULT_REQUEST_NOT_SUPPORTED = 24; // 0x18
- field public static final int RESULT_SYSTEM_ERROR = 15; // 0xf
}
public class SubscriptionInfo implements android.os.Parcelable {
@@ -8294,6 +8289,9 @@ package android.telephony {
public class TelephonyManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionReportDefaultNetworkStatus(int, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionResetAll(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionSetRadioEnabled(int, boolean);
method public int checkCarrierPrivilegesForPackage(String);
method public int checkCarrierPrivilegesForPackageAnyPhone(String);
method public void dial(String);
diff --git a/api/test-current.txt b/api/test-current.txt
index b3834bf9681c..a060dfcb8311 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2373,6 +2373,7 @@ package android.provider {
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
field public static final String NAMESPACE_AUTOFILL = "autofill";
field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
+ field public static final String NAMESPACE_PERMISSIONS = "permissions";
field public static final String NAMESPACE_PRIVACY = "privacy";
field public static final String NAMESPACE_ROLLBACK = "rollback";
field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 1572114c639f..f476fcf91bd5 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -546,16 +546,16 @@ status_t LogSection::BlockingCall(int pipeWriteFd) const {
;
android_log_list_element elem;
- lastTimestamp.tv_sec = msg.entry_v1.sec;
- lastTimestamp.tv_nsec = msg.entry_v1.nsec;
+ lastTimestamp.tv_sec = msg.entry.sec;
+ lastTimestamp.tv_nsec = msg.entry.nsec;
// format a BinaryLogEntry
uint64_t token = proto.start(LogProto::BINARY_LOGS);
- proto.write(BinaryLogEntry::SEC, msg.entry_v1.sec);
- proto.write(BinaryLogEntry::NANOSEC, msg.entry_v1.nsec);
- proto.write(BinaryLogEntry::UID, (int)msg.entry_v4.uid);
- proto.write(BinaryLogEntry::PID, msg.entry_v1.pid);
- proto.write(BinaryLogEntry::TID, msg.entry_v1.tid);
+ proto.write(BinaryLogEntry::SEC, (int32_t)msg.entry.sec);
+ proto.write(BinaryLogEntry::NANOSEC, (int32_t)msg.entry.nsec);
+ proto.write(BinaryLogEntry::UID, (int)msg.entry.uid);
+ proto.write(BinaryLogEntry::PID, msg.entry.pid);
+ proto.write(BinaryLogEntry::TID, (int32_t)msg.entry.tid);
proto.write(BinaryLogEntry::TAG_INDEX,
get4LE(reinterpret_cast<uint8_t const*>(msg.msg())));
do {
@@ -603,7 +603,7 @@ status_t LogSection::BlockingCall(int pipeWriteFd) const {
}
} else {
AndroidLogEntry entry;
- err = android_log_processLogBuffer(&msg.entry_v1, &entry);
+ err = android_log_processLogBuffer(&msg.entry, &entry);
if (err != NO_ERROR) {
ALOGW("[%s] fails to process to an entry.\n", this->name.string());
break;
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
index 43addc28f074..26034695906b 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -54,9 +54,9 @@ static void getSimpleLogMsgData(log_msg* msg) {
write4Bytes(99 /* a value to log*/, &buffer);
buffer.push_back(EVENT_TYPE_LIST_STOP);
- msg->entry_v1.len = buffer.size();
+ msg->entry.len = buffer.size();
msg->entry.hdr_size = kLogMsgHeaderSize;
- msg->entry_v1.sec = time(nullptr);
+ msg->entry.sec = time(nullptr);
std::copy(buffer.begin(), buffer.end(), msg->buf + kLogMsgHeaderSize);
}
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index fd19c9d61f87..262921e5163c 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -38,8 +38,8 @@ using std::vector;
LogEvent::LogEvent(log_msg& msg) {
mContext =
create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
- mLogdTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
- mLogUid = msg.entry_v4.uid;
+ mLogdTimestampNs = msg.entry.sec * NS_PER_SEC + msg.entry.nsec;
+ mLogUid = msg.entry.uid;
init(mContext);
if (mContext) {
// android_log_destroy will set mContext to NULL
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
index a454df5d68f4..204d71d93cd8 100644
--- a/core/java/android/annotation/UnsupportedAppUsage.java
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -83,8 +83,9 @@ public @interface UnsupportedAppUsage {
* <p>Possible values are:
* <ul>
* <li>
- * {@link android.os.Build.VERSION_CODES#O} or {@link android.os.Build.VERSION_CODES#P},
- * to limit access to apps targeting these SDKs (or earlier).
+ * An API level like {@link android.os.Build.VERSION_CODES#O} - in which case the API is
+ * available up to and including the specified release. Or, in other words, the API is
+ * blacklisted (unavailable) from the next API level from the one specified.
* </li>
* <li>
* absent (default value) - All apps can access this API, but doing so may result in
@@ -94,10 +95,6 @@ public @interface UnsupportedAppUsage {
*
* </ul>
*
- * Note, if this is set to {@link android.os.Build.VERSION_CODES#O}, apps targeting O
- * maintenance releases will also be allowed to use the API, and similarly for any future
- * maintenance releases of P.
- *
* @return The maximum value for an apps targetSdkVersion in order to access this API.
*/
int maxTargetSdk() default Integer.MAX_VALUE;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2c57622a4df8..9872e306eb9f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2563,9 +2563,12 @@ public class Activity extends ContextThemeWrapper
}
/**
- * Report to the system that your app is now fully drawn, purely for diagnostic
- * purposes (calling it does not impact the visible behavior of the activity).
- * This is only used to help instrument application launch times, so that the
+ * Report to the system that your app is now fully drawn, for diagnostic and
+ * optimization purposes. The system may adjust optimizations to prioritize
+ * work that happens before reportFullyDrawn is called, to improve app startup.
+ * Misrepresenting the startup window by calling reportFullyDrawn too late or too
+ * early may decrease application and startup performance.<p>
+ * This is also used to help instrument application launch times, so that the
* app can report when it is fully in a usable state; without this, the only thing
* the system itself can determine is the point at which the activity's window
* is <em>first</em> drawn and displayed. To participate in app launch time
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d5e41f0eacbe..8bca87e69ecc 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2822,12 +2822,12 @@ public class AppOpsManager {
LongSparseArray.StringParcelling.class);
return new OpFeatureEntry.Builder(source.readBoolean(),
- (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
- (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
- (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
- (LongSparseLongArray) longSparseLongArrayParcelling.unparcel(source),
- (LongSparseArray<String>) longSparseStringArrayParcelling.unparcel(source),
- (LongSparseArray<String>) longSparseStringArrayParcelling.unparcel(source));
+ longSparseLongArrayParcelling.unparcel(source),
+ longSparseLongArrayParcelling.unparcel(source),
+ longSparseLongArrayParcelling.unparcel(source),
+ longSparseLongArrayParcelling.unparcel(source),
+ longSparseStringArrayParcelling.unparcel(source),
+ longSparseStringArrayParcelling.unparcel(source));
}
}
diff --git a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
index 1bb81b1487af..1e6ab4136187 100644
--- a/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
+++ b/core/java/android/app/contentsuggestions/ContentSuggestionsManager.java
@@ -45,6 +45,17 @@ import java.util.concurrent.Executor;
*/
@SystemApi
public final class ContentSuggestionsManager {
+ /**
+ * Key into the extras Bundle passed to {@link #provideContextImage(int, Bundle)}.
+ * This can be used to provide the bitmap to
+ * {@link android.service.contentsuggestions.ContentSuggestionsService}.
+ * The value must be a {@link android.graphics.Bitmap} with the
+ * config {@link android.graphics.Bitmap.Config.HARDWARE}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_BITMAP = "android.contentsuggestions.extra.BITMAP";
+
private static final String TAG = ContentSuggestionsManager.class.getSimpleName();
/**
@@ -70,7 +81,7 @@ public final class ContentSuggestionsManager {
* system content suggestions service.
*
* @param taskId of the task to snapshot.
- * @param imageContextRequestExtras sent with with request to provide implementation specific
+ * @param imageContextRequestExtras sent with request to provide implementation specific
* extra information.
*/
public void provideContextImage(
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index 30868bfeac66..97e3f5221c72 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -154,7 +154,7 @@ public final class ScanRecord {
}
/**
- * Returns the local name of the BLE device. The is a UTF-8 encoded string.
+ * Returns the local name of the BLE device. This is a UTF-8 encoded string.
*/
@Nullable
public String getDeviceName() {
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index fdef2a143fc4..b6a0a564538f 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -1069,9 +1069,8 @@ public class ClipData implements Parcelable {
if (!first) {
b.append(' ');
}
- mItems.get(0).toShortString(b);
- if (mItems.size() > 1) {
- b.append(" ...");
+ for (int i=0; i<mItems.size(); i++) {
+ b.append("{...}");
}
}
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 02b6b3ef64ef..7de87938c1f6 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -834,6 +834,23 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
}
/**
+ * Retrieves a Non-Nullable Context this provider is running in, this is intended to be called
+ * after {@link #onCreate}. When called before context was created, an IllegalStateException
+ * will be thrown.
+ * <p>
+ * Note A provider must be declared in the manifest and created automatically by the system,
+ * and context is only available after {@link #onCreate} is called.
+ */
+ @NonNull
+ public final Context requireContext() {
+ final Context ctx = getContext();
+ if (ctx == null) {
+ throw new IllegalStateException("Cannot find context from the provider.");
+ }
+ return ctx;
+ }
+
+ /**
* Set the calling package, returning the current value (or {@code null})
* which can be used later to restore the previous state.
*/
diff --git a/core/java/android/content/pm/VerificationParams.java b/core/java/android/content/pm/VerificationParams.java
deleted file mode 100644
index f07216796eb9..000000000000
--- a/core/java/android/content/pm/VerificationParams.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2012 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.pm;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents verification parameters used to verify packages to be installed.
- *
- * @deprecated callers should migrate to {@link PackageInstaller}.
- * @hide
- */
-@Deprecated
-public class VerificationParams implements Parcelable {
- /** A constant used to indicate that a uid value is not present. */
- public static final int NO_UID = -1;
-
- /** What we print out first when toString() is called. */
- private static final String TO_STRING_PREFIX = "VerificationParams{";
-
- /** The location of the supplementary verification file. */
- private final Uri mVerificationURI;
-
- /** URI referencing where the package was downloaded from. */
- private final Uri mOriginatingURI;
-
- /** HTTP referrer URI associated with the originatingURI. */
- private final Uri mReferrer;
-
- /** UID of the application that the install request originated from. */
- private final int mOriginatingUid;
-
- /** UID of application requesting the install */
- private int mInstallerUid;
-
- /**
- * Creates verification specifications for installing with application verification.
- *
- * @param verificationURI The location of the supplementary verification
- * file. This can be a 'file:' or a 'content:' URI. May be {@code null}.
- * @param originatingURI URI referencing where the package was downloaded
- * from. May be {@code null}.
- * @param referrer HTTP referrer URI associated with the originatingURI.
- * May be {@code null}.
- * @param originatingUid UID of the application that the install request originated
- * from, or NO_UID if not present
- */
- public VerificationParams(Uri verificationURI, Uri originatingURI, Uri referrer,
- int originatingUid) {
- mVerificationURI = verificationURI;
- mOriginatingURI = originatingURI;
- mReferrer = referrer;
- mOriginatingUid = originatingUid;
- mInstallerUid = NO_UID;
- }
-
- public Uri getVerificationURI() {
- return mVerificationURI;
- }
-
- public Uri getOriginatingURI() {
- return mOriginatingURI;
- }
-
- public Uri getReferrer() {
- return mReferrer;
- }
-
- /** return NO_UID if not available */
- public int getOriginatingUid() {
- return mOriginatingUid;
- }
-
- /** @return NO_UID when not set */
- public int getInstallerUid() {
- return mInstallerUid;
- }
-
- public void setInstallerUid(int uid) {
- mInstallerUid = uid;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
-
- if (!(o instanceof VerificationParams)) {
- return false;
- }
-
- final VerificationParams other = (VerificationParams) o;
-
- if (mVerificationURI == null) {
- if (other.mVerificationURI != null) {
- return false;
- }
- } else if (!mVerificationURI.equals(other.mVerificationURI)) {
- return false;
- }
-
- if (mOriginatingURI == null) {
- if (other.mOriginatingURI != null) {
- return false;
- }
- } else if (!mOriginatingURI.equals(other.mOriginatingURI)) {
- return false;
- }
-
- if (mReferrer == null) {
- if (other.mReferrer != null) {
- return false;
- }
- } else if (!mReferrer.equals(other.mReferrer)) {
- return false;
- }
-
- if (mOriginatingUid != other.mOriginatingUid) {
- return false;
- }
-
- if (mInstallerUid != other.mInstallerUid) {
- return false;
- }
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int hash = 3;
-
- hash += 5 * (mVerificationURI == null ? 1 : mVerificationURI.hashCode());
- hash += 7 * (mOriginatingURI == null ? 1 : mOriginatingURI.hashCode());
- hash += 11 * (mReferrer == null ? 1 : mReferrer.hashCode());
- hash += 13 * mOriginatingUid;
- hash += 17 * mInstallerUid;
-
- return hash;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder(TO_STRING_PREFIX);
-
- sb.append("mVerificationURI=");
- sb.append(mVerificationURI.toString());
- sb.append(",mOriginatingURI=");
- sb.append(mOriginatingURI.toString());
- sb.append(",mReferrer=");
- sb.append(mReferrer.toString());
- sb.append(",mOriginatingUid=");
- sb.append(mOriginatingUid);
- sb.append(",mInstallerUid=");
- sb.append(mInstallerUid);
- sb.append('}');
-
- return sb.toString();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelable(mVerificationURI, 0);
- dest.writeParcelable(mOriginatingURI, 0);
- dest.writeParcelable(mReferrer, 0);
- dest.writeInt(mOriginatingUid);
- dest.writeInt(mInstallerUid);
- }
-
-
- private VerificationParams(Parcel source) {
- mVerificationURI = source.readParcelable(Uri.class.getClassLoader());
- mOriginatingURI = source.readParcelable(Uri.class.getClassLoader());
- mReferrer = source.readParcelable(Uri.class.getClassLoader());
- mOriginatingUid = source.readInt();
- mInstallerUid = source.readInt();
- }
-
- public static final @android.annotation.NonNull Parcelable.Creator<VerificationParams> CREATOR =
- new Parcelable.Creator<VerificationParams>() {
- public VerificationParams createFromParcel(Parcel source) {
- return new VerificationParams(source);
- }
-
- public VerificationParams[] newArray(int size) {
- return new VerificationParams[size];
- }
- };
-}
diff --git a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
index 160376bf6cba..eb5d0cb539a1 100644
--- a/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
+++ b/core/java/android/hardware/soundtrigger/KeyphraseEnrollmentInfo.java
@@ -58,8 +58,8 @@ public class KeyphraseEnrollmentInfo {
*/
private static final String VOICE_KEYPHRASE_META_DATA = "android.voice_enrollment";
/**
- * Activity Action: Show activity for managing the keyphrases for hotword detection.
- * This needs to be defined by an activity that supports enrolling users for hotword/keyphrase
+ * Intent Action: for managing the keyphrases for hotword detection.
+ * This needs to be defined by a service that supports enrolling users for hotword/keyphrase
* detection.
*/
public static final String ACTION_MANAGE_VOICE_KEYPHRASES =
@@ -101,7 +101,7 @@ public class KeyphraseEnrollmentInfo {
// Find the apps that supports enrollment for hotword keyhphrases,
// Pick a privileged app and obtain the information about the supported keyphrases
// from its metadata.
- List<ResolveInfo> ris = pm.queryIntentActivities(
+ List<ResolveInfo> ris = pm.queryIntentServices(
new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY);
if (ris == null || ris.isEmpty()) {
// No application capable of enrolling for voice keyphrases is present.
@@ -116,11 +116,11 @@ public class KeyphraseEnrollmentInfo {
for (ResolveInfo ri : ris) {
try {
ApplicationInfo ai = pm.getApplicationInfo(
- ri.activityInfo.packageName, PackageManager.GET_META_DATA);
+ ri.serviceInfo.packageName, PackageManager.GET_META_DATA);
if ((ai.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) == 0) {
// The application isn't privileged (/system/priv-app).
// The enrollment application needs to be a privileged system app.
- Slog.w(TAG, ai.packageName + "is not a privileged system app");
+ Slog.w(TAG, ai.packageName + " is not a privileged system app");
continue;
}
if (!Manifest.permission.MANAGE_VOICE_KEYPHRASES.equals(ai.permission)) {
@@ -137,7 +137,7 @@ public class KeyphraseEnrollmentInfo {
}
} catch (PackageManager.NameNotFoundException e) {
String error = "error parsing voice enrollment meta-data for "
- + ri.activityInfo.packageName;
+ + ri.serviceInfo.packageName;
parseErrors.add(error + ": " + e);
Slog.w(TAG, error, e);
}
@@ -290,7 +290,7 @@ public class KeyphraseEnrollmentInfo {
}
/**
- * Returns an intent to launch an activity that manages the given keyphrase
+ * Returns an intent to launch an service that manages the given keyphrase
* for the locale.
*
* @param action The enrollment related action that this intent is supposed to perform.
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 3e325b748f51..e3259ffa9d47 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -57,6 +57,9 @@ public final class NetworkCapabilities implements Parcelable {
private static final String TAG = "NetworkCapabilities";
private static final int INVALID_UID = -1;
+ // Set to true when private DNS is broken.
+ private boolean mPrivateDnsBroken;
+
/**
* @hide
*/
@@ -86,6 +89,7 @@ public final class NetworkCapabilities implements Parcelable {
mUids = null;
mEstablishingVpnAppUid = INVALID_UID;
mSSID = null;
+ mPrivateDnsBroken = false;
}
/**
@@ -104,6 +108,7 @@ public final class NetworkCapabilities implements Parcelable {
mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
mSSID = nc.mSSID;
+ mPrivateDnsBroken = nc.mPrivateDnsBroken;
}
/**
@@ -557,6 +562,9 @@ public final class NetworkCapabilities implements Parcelable {
}
if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
if (hasSignalStrength()) return "signalStrength";
+ if (isPrivateDnsBroken()) {
+ return "privateDnsBroken";
+ }
return null;
}
@@ -1443,7 +1451,8 @@ public final class NetworkCapabilities implements Parcelable {
&& equalsSpecifier(that)
&& equalsTransportInfo(that)
&& equalsUids(that)
- && equalsSSID(that));
+ && equalsSSID(that)
+ && equalsPrivateDnsBroken(that));
}
@Override
@@ -1460,7 +1469,8 @@ public final class NetworkCapabilities implements Parcelable {
+ (mSignalStrength * 29)
+ Objects.hashCode(mUids) * 31
+ Objects.hashCode(mSSID) * 37
- + Objects.hashCode(mTransportInfo) * 41;
+ + Objects.hashCode(mTransportInfo) * 41
+ + Objects.hashCode(mPrivateDnsBroken) * 43;
}
@Override
@@ -1479,6 +1489,7 @@ public final class NetworkCapabilities implements Parcelable {
dest.writeInt(mSignalStrength);
dest.writeArraySet(mUids);
dest.writeString(mSSID);
+ dest.writeBoolean(mPrivateDnsBroken);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1498,6 +1509,7 @@ public final class NetworkCapabilities implements Parcelable {
netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
+ netCap.mPrivateDnsBroken = in.readBoolean();
return netCap;
}
@Override
@@ -1555,6 +1567,10 @@ public final class NetworkCapabilities implements Parcelable {
sb.append(" SSID: ").append(mSSID);
}
+ if (mPrivateDnsBroken) {
+ sb.append(" Private DNS is broken");
+ }
+
sb.append("]");
return sb.toString();
}
@@ -1706,4 +1722,28 @@ public final class NetworkCapabilities implements Parcelable {
public boolean isMetered() {
return !hasCapability(NET_CAPABILITY_NOT_METERED);
}
+
+ /**
+ * Check if private dns is broken.
+ *
+ * @return {@code true} if {@code mPrivateDnsBroken} is set when private DNS is broken.
+ * @hide
+ */
+ public boolean isPrivateDnsBroken() {
+ return mPrivateDnsBroken;
+ }
+
+ /**
+ * Set mPrivateDnsBroken to true when private dns is broken.
+ *
+ * @param broken the status of private DNS to be set.
+ * @hide
+ */
+ public void setPrivateDnsBroken(boolean broken) {
+ mPrivateDnsBroken = broken;
+ }
+
+ private boolean equalsPrivateDnsBroken(NetworkCapabilities nc) {
+ return mPrivateDnsBroken == nc.mPrivateDnsBroken;
+ }
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 9ba3bd940a96..4ad52d5aa1bc 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -77,6 +77,12 @@ public class NetworkMisc implements Parcelable {
*/
public boolean skip464xlat;
+ /**
+ * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
+ * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
+ */
+ public boolean hasShownBroken;
+
public NetworkMisc() {
}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 6d5fe53b3f3d..a92237b9f17f 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -55,6 +55,7 @@ public class Environment {
private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
private static final String ENV_PRODUCT_ROOT = "PRODUCT_ROOT";
private static final String ENV_SYSTEM_EXT_ROOT = "SYSTEM_EXT_ROOT";
+ private static final String ENV_APEX_ROOT = "APEX_ROOT";
/** {@hide} */
public static final String DIR_ANDROID = "Android";
@@ -78,7 +79,9 @@ public class Environment {
private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
private static final File DIR_PRODUCT_ROOT = getDirectory(ENV_PRODUCT_ROOT, "/product");
private static final File DIR_SYSTEM_EXT_ROOT = getDirectory(ENV_SYSTEM_EXT_ROOT,
- "/system_ext");
+ "/system_ext");
+ private static final File DIR_APEX_ROOT = getDirectory(ENV_APEX_ROOT,
+ "/apex");
@UnsupportedAppUsage
private static UserEnvironment sCurrentUser;
@@ -248,6 +251,16 @@ public class Environment {
}
/**
+ * Return root directory of the apex mount point, where all the apex modules are made available
+ * to the rest of the system.
+ *
+ * @hide
+ */
+ public static @NonNull File getApexDirectory() {
+ return DIR_APEX_ROOT;
+ }
+
+ /**
* Return the system directory for a user. This is for use by system
* services to store files relating to the user. This directory will be
* automatically deleted when the user is removed.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c6b63cadd7d1..71b94ed41351 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1748,8 +1748,30 @@ public class UserManager {
}
}
- /** {@hide} */
- public boolean isUserUnlockingOrUnlocked(UserHandle user) {
+ /**
+ * Return whether the provided user is already running in an
+ * "unlocked" state or in the process of unlocking.
+ * <p>
+ * On devices with direct boot, a user is unlocked only after they've
+ * entered their credentials (such as a lock pattern or PIN). On devices
+ * without direct boot, a user is unlocked as soon as it starts.
+ * <p>
+ * When a user is locked, only device-protected data storage is available.
+ * When a user is unlocked, both device-protected and credential-protected
+ * private app data storage is available.
+ *
+ * <p>Requires {@code android.permission.MANAGE_USERS} or
+ * {@code android.permission.INTERACT_ACROSS_USERS}, otherwise specified {@link UserHandle user}
+ * must be the calling user or a profile associated with it.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.INTERACT_ACROSS_USERS
+ })
+ public boolean isUserUnlockingOrUnlocked(@NonNull UserHandle user) {
return isUserUnlockingOrUnlocked(user.getIdentifier());
}
@@ -2568,6 +2590,22 @@ public class UserManager {
}
/**
+ * Checks if the 2 provided user handles belong to the same profile group.
+ *
+ * @param user one of the two user handles to check.
+ * @param otherUser one of the two user handles to check.
+ * @return true if the two users are in the same profile group.
+ *
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public boolean isSameProfileGroup(@NonNull UserHandle user, @NonNull UserHandle otherUser) {
+ return isSameProfileGroup(user.getIdentifier(), otherUser.getIdentifier());
+ }
+
+ /**
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
* @param userId one of the two user ids to check.
* @param otherUserId one of the two user ids to check.
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index 77fd946f7ccb..0e00d5e9b7bf 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -104,16 +104,17 @@ public class DynamicSystemManager {
* Start DynamicSystem installation. This call may take an unbounded amount of time. The caller
* may use another thread to call the getStartProgress() to get the progress.
*
- * @param systemSize system size in bytes
- * @param userdataSize userdata size in bytes
+ * @param name The DSU partition name
+ * @param size Size of the DSU image in bytes
+ * @param readOnly True if the partition is read only, e.g. system.
* @return {@code true} if the call succeeds. {@code false} either the device does not contain
* enough space or a DynamicSystem is currently in use where the {@link #isInUse} would be
* true.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
- public Session startInstallation(long systemSize, long userdataSize) {
+ public Session startInstallation(String name, long size, boolean readOnly) {
try {
- if (mService.startInstallation(systemSize, userdataSize)) {
+ if (mService.startInstallation(name, size, readOnly)) {
return new Session();
} else {
return null;
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index a6de170b5ce5..75f67854743f 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -24,11 +24,12 @@ interface IDynamicSystemService
* Start DynamicSystem installation. This call may take 60~90 seconds. The caller
* may use another thread to call the getStartProgress() to get the progress.
*
- * @param systemSize system size in bytes
- * @param userdataSize userdata size in bytes
+ * @param name The DSU partition name
+ * @param size Size of the DSU image in bytes
+ * @param readOnly True if this partition is readOnly
* @return true if the call succeeds
*/
- boolean startInstallation(long systemSize, long userdataSize);
+ boolean startInstallation(@utf8InCpp String name, long size, boolean readOnly);
/**
* Query the progress of the current installation operation. This can be called while
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index e456c8a42e19..8b8afd549c9f 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -338,6 +338,15 @@ public final class DeviceConfig {
public static final String NAMESPACE_PRIVACY = "privacy";
/**
+ * Permission related properties definitions.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final String NAMESPACE_PERMISSIONS = "permissions";
+
+ /**
* Interface for accessing keys belonging to {@link #NAMESPACE_WINDOW_MANAGER}.
* @hide
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cff99f34126a..aeed20db2850 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8809,6 +8809,13 @@ public final class Settings {
public static final String DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS =
"force_desktop_mode_on_external_displays";
+ /**
+ * Whether to allow non-resizable apps to be freeform.
+ * @hide
+ */
+ public static final String DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM =
+ "enable_sizecompat_freeform";
+
/**
* Whether user has enabled development settings.
*/
diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
index efc8e877f3c3..4bcd39fbd41f 100644
--- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
+++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java
@@ -66,12 +66,17 @@ public abstract class ContentSuggestionsService extends Service {
int colorSpaceId, Bundle imageContextRequestExtras) {
Bitmap wrappedBuffer = null;
- if (contextImage != null) {
- ColorSpace colorSpace = null;
- if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
- colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+ if (imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
+ wrappedBuffer = imageContextRequestExtras.getParcelable(
+ ContentSuggestionsManager.EXTRA_BITMAP);
+ } else {
+ if (contextImage != null) {
+ ColorSpace colorSpace = null;
+ if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
+ colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+ }
+ wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
}
- wrappedBuffer = Bitmap.wrapHardwareBuffer(contextImage, colorSpace);
}
mHandler.sendMessage(
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 8a9f68942e9d..12c25806d666 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -516,7 +516,7 @@ public abstract class EuiccService extends Service {
* @see android.telephony.euicc.EuiccManager#eraseSubscriptions
*
* @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
- * and use @link{onEraseSubscriptionsWithOptions} instead
+ * and use {@link #onEraseSubscriptionsWithOptions(int, int)} instead
*/
@Deprecated
public abstract int onEraseSubscriptions(int slotId);
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 94f6a50c1bd9..cf56eae4a052 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -20,7 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
-import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
@@ -446,7 +446,7 @@ public class AlwaysOnHotwordDetector {
/**
* Creates an intent to start the enrollment for the associated keyphrase.
- * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * This intent must be invoked using {@link Context#startForegroundService(Intent)}.
* Starting re-enrollment is only valid if the keyphrase is un-enrolled,
* i.e. {@link #STATE_KEYPHRASE_UNENROLLED},
* otherwise {@link #createReEnrollIntent()} should be preferred.
@@ -468,7 +468,7 @@ public class AlwaysOnHotwordDetector {
/**
* Creates an intent to start the un-enrollment for the associated keyphrase.
- * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * This intent must be invoked using {@link Context#startForegroundService(Intent)}.
* Starting re-enrollment is only valid if the keyphrase is already enrolled,
* i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
*
@@ -489,7 +489,7 @@ public class AlwaysOnHotwordDetector {
/**
* Creates an intent to start the re-enrollment for the associated keyphrase.
- * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}.
+ * This intent must be invoked using {@link Context#startForegroundService(Intent)}.
* Starting re-enrollment is only valid if the keyphrase is already enrolled,
* i.e. {@link #STATE_KEYPHRASE_ENROLLED}, otherwise invoking this may result in an error.
*
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index e78b79695d3d..73e17a61d17f 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -17,7 +17,6 @@
package android.util;
import android.os.Parcel;
-import android.os.Parcelable;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
@@ -25,6 +24,8 @@ import com.android.internal.util.Preconditions;
import libcore.util.EmptyArray;
+import java.util.Arrays;
+
/**
* SparseArray mapping longs to Objects. Unlike a normal array of Objects,
* there can be gaps in the indices. It is intended to be more memory efficient
@@ -450,22 +451,25 @@ public class LongSparseArray<E> implements Cloneable {
/**
* @hide
*/
- public static class StringParcelling implements com.android.internal.util.Parcelling {
+ public static class StringParcelling implements
+ com.android.internal.util.Parcelling<LongSparseArray<String>> {
@Override
- public void parcel(Object item, Parcel dest, int parcelFlags) {
- if (item == null) {
+ public void parcel(LongSparseArray<String> array, Parcel dest, int parcelFlags) {
+ if (array == null) {
dest.writeInt(-1);
return;
}
- LongSparseArray<String> array = (LongSparseArray<String>) item;
- dest.writeInt(array.mSize);
+ int size = array.mSize;
+
+ dest.writeInt(size);
dest.writeLongArray(array.mKeys);
- dest.writeStringArray((String[]) array.mValues);
+
+ dest.writeStringArray(Arrays.copyOfRange(array.mValues, 0, size, String[].class));
}
@Override
- public Object unparcel(Parcel source) {
+ public LongSparseArray<String> unparcel(Parcel source) {
int size = source.readInt();
if (size == -1) {
return null;
@@ -490,49 +494,4 @@ public class LongSparseArray<E> implements Cloneable {
return array;
}
}
-
- /**
- * @hide
- */
- public static class Parcelling<T extends Parcelable> implements
- com.android.internal.util.Parcelling {
- @Override
- public void parcel(Object item, Parcel dest, int parcelFlags) {
- if (item == null) {
- dest.writeInt(-1);
- return;
- }
-
- LongSparseArray<T> array = (LongSparseArray<T>) item;
- dest.writeInt(array.mSize);
- dest.writeLongArray(array.mKeys);
- dest.writeParcelableArray((T[]) array.mValues, parcelFlags);
- }
-
- @Override
- public Object unparcel(Parcel source) {
- int size = source.readInt();
- if (size == -1) {
- return null;
- }
-
- LongSparseArray<T> array = new LongSparseArray<>(0);
- array.mSize = size;
- array.mKeys = source.createLongArray();
- array.mValues = source.readParcelableArray(null);
-
- // Make sure array is sane
- Preconditions.checkArgument(array.mKeys.length >= size);
- Preconditions.checkArgument(array.mValues.length >= size);
-
- if (size > 0) {
- long last = array.mKeys[0];
- for (int i = 1; i < size; i++) {
- Preconditions.checkArgument(last < array.mKeys[i]);
- }
- }
-
- return array;
- }
- }
}
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index 9ffd4f05bd47..a0edd047a49b 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -289,22 +289,22 @@ public class LongSparseLongArray implements Cloneable {
/**
* @hide
*/
- public static class Parcelling implements com.android.internal.util.Parcelling {
+ public static class Parcelling implements
+ com.android.internal.util.Parcelling<LongSparseLongArray> {
@Override
- public void parcel(Object item, Parcel dest, int parcelFlags) {
- if (item == null) {
+ public void parcel(LongSparseLongArray array, Parcel dest, int parcelFlags) {
+ if (array == null) {
dest.writeInt(-1);
return;
}
- LongSparseLongArray array = (LongSparseLongArray) item;
dest.writeInt(array.mSize);
dest.writeLongArray(array.mKeys);
dest.writeLongArray(array.mValues);
}
@Override
- public Object unparcel(Parcel source) {
+ public LongSparseLongArray unparcel(Parcel source) {
int size = source.readInt();
if (size == -1) {
return null;
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 0fb1c33df2ab..786dbb088e2b 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.InsetsState.INSET_SIDE_BOTTOM;
+import static android.view.InsetsState.INSET_SIDE_FLOATING;
import static android.view.InsetsState.INSET_SIDE_LEFT;
import static android.view.InsetsState.INSET_SIDE_RIGHT;
import static android.view.InsetsState.INSET_SIDE_TOP;
@@ -151,6 +152,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, mPendingInsets.right, params, state);
updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params,
state);
+ updateLeashesForSide(INSET_SIDE_FLOATING, 0 /* offset */, 0 /* inset */, params, state);
+
SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get();
applier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
mCurrentInsets = mPendingInsets;
@@ -238,7 +241,9 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
// If the system is controlling the insets source, the leash can be null.
if (leash != null) {
surfaceParams.add(new SurfaceParams(leash, 1f /* alpha */, mTmpMatrix,
- null /* windowCrop */, 0 /* layer */, 0f /* cornerRadius*/, inset != 0));
+ null /* windowCrop */, 0 /* layer */, 0f /* cornerRadius*/,
+ side == INSET_SIDE_FLOATING
+ ? consumer.isVisible() : inset != 0 /* visible */));
}
}
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 99502a6c1d72..e9de3f0bb3ca 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -109,6 +109,7 @@ public class InsetsState implements Parcelable {
INSET_SIDE_TOP,
INSET_SIDE_RIGHT,
INSET_SIDE_BOTTOM,
+ INSET_SIDE_FLOATING,
INSET_SIDE_UNKNWON
})
public @interface InsetSide {}
@@ -116,7 +117,8 @@ public class InsetsState implements Parcelable {
static final int INSET_SIDE_TOP = 1;
static final int INSET_SIDE_RIGHT = 2;
static final int INSET_SIDE_BOTTOM = 3;
- static final int INSET_SIDE_UNKNWON = 4;
+ static final int INSET_SIDE_FLOATING = 4;
+ static final int INSET_SIDE_UNKNWON = 5;
private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
@@ -224,10 +226,10 @@ public class InsetsState implements Parcelable {
typeVisibilityMap[index] = source.isVisible();
}
- if (typeSideMap != null && !Insets.NONE.equals(insets)) {
+ if (typeSideMap != null) {
@InsetSide int insetSide = getInsetSide(insets);
if (insetSide != INSET_SIDE_UNKNWON) {
- typeSideMap.put(source.getType(), getInsetSide(insets));
+ typeSideMap.put(source.getType(), insetSide);
}
}
}
@@ -237,6 +239,9 @@ public class InsetsState implements Parcelable {
* is set in order that this method returns a meaningful result.
*/
private @InsetSide int getInsetSide(Insets insets) {
+ if (Insets.NONE.equals(insets)) {
+ return INSET_SIDE_FLOATING;
+ }
if (insets.left != 0) {
return INSET_SIDE_LEFT;
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 00c1e2977c03..3b82f186d80e 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2505,9 +2505,7 @@ public class ChooserActivity extends ResolverActivity {
ri.noResourceId = true;
ri.icon = 0;
}
- ResolveInfoPresentationGetter getter = makePresentationGetter(ri);
- mCallerTargets.add(new DisplayResolveInfo(ii, ri,
- getter.getLabel(), getter.getSubLabel(), ii));
+ mCallerTargets.add(new DisplayResolveInfo(ii, ri, ii));
}
}
}
@@ -2606,7 +2604,22 @@ public class ChooserActivity extends ResolverActivity {
@Override
public void onListRebuilt() {
- updateAlphabeticalList();
+ if (getDisplayList() == null || getDisplayList().isEmpty()) {
+ notifyDataSetChanged();
+ } else {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... voids) {
+ updateAlphabeticalList();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ notifyDataSetChanged();
+ }
+ }.execute();
+ }
// don't support direct share on low ram devices
if (ActivityManager.isLowRamDeviceStatic()) {
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 7f18ae35b00c..74996e9fc212 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1406,14 +1406,18 @@ public class ResolverActivity extends Activity {
public final class DisplayResolveInfo implements TargetInfo {
private final ResolveInfo mResolveInfo;
- private final CharSequence mDisplayLabel;
+ private CharSequence mDisplayLabel;
private Drawable mDisplayIcon;
private Drawable mBadge;
- private final CharSequence mExtendedInfo;
+ private CharSequence mExtendedInfo;
private final Intent mResolvedIntent;
private final List<Intent> mSourceIntents = new ArrayList<>();
private boolean mIsSuspended;
+ public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, Intent pOrigIntent) {
+ this(originalIntent, pri, null /*mDisplayLabel*/, null /*mExtendedInfo*/, pOrigIntent);
+ }
+
public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel,
CharSequence pInfo, Intent pOrigIntent) {
mSourceIntents.add(originalIntent);
@@ -1448,9 +1452,26 @@ public class ResolverActivity extends Activity {
}
public CharSequence getDisplayLabel() {
+ if (mDisplayLabel == null) {
+ ResolveInfoPresentationGetter pg = makePresentationGetter(mResolveInfo);
+ mDisplayLabel = pg.getLabel();
+ mExtendedInfo = pg.getSubLabel();
+ }
return mDisplayLabel;
}
+ public boolean hasDisplayLabel() {
+ return mDisplayLabel != null;
+ }
+
+ public void setDisplayLabel(CharSequence displayLabel) {
+ mDisplayLabel = displayLabel;
+ }
+
+ public void setExtendedInfo(CharSequence extendedInfo) {
+ mExtendedInfo = extendedInfo;
+ }
+
public Drawable getDisplayIcon() {
return mDisplayIcon;
}
@@ -1866,9 +1887,7 @@ public class ResolverActivity extends Activity {
final ResolveInfo ri = rci.getResolveInfoAt(0);
if (ri != null) {
mAllTargetsAreBrowsers &= ri.handleAllWebDataURI;
-
- ResolveInfoPresentationGetter pg = makePresentationGetter(ri);
- addResolveInfoWithAlternates(rci, pg.getSubLabel(), pg.getLabel());
+ addResolveInfoWithAlternates(rci);
}
}
}
@@ -1915,14 +1934,12 @@ public class ResolverActivity extends Activity {
return mFilterLastUsed;
}
- private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
- CharSequence extraInfo, CharSequence roLabel) {
+ private void addResolveInfoWithAlternates(ResolvedComponentInfo rci) {
final int count = rci.getCount();
final Intent intent = rci.getIntentAt(0);
final ResolveInfo add = rci.getResolveInfoAt(0);
final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent);
- final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel,
- extraInfo, replaceIntent);
+ final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, replaceIntent);
addResolveInfo(dri);
if (replaceIntent == intent) {
// Only add alternates if we didn't get a specific replacement from
@@ -2054,20 +2071,11 @@ public class ResolverActivity extends Activity {
return;
}
- final CharSequence label = info.getDisplayLabel();
- if (!TextUtils.equals(holder.text.getText(), label)) {
- holder.text.setText(info.getDisplayLabel());
- }
-
- // Always show a subLabel for visual consistency across list items. Show an empty
- // subLabel if the subLabel is the same as the label
- CharSequence subLabel = info.getExtendedInfo();
- if (TextUtils.equals(label, subLabel)) subLabel = null;
-
- if (!TextUtils.equals(holder.text2.getText(), subLabel)
- && !TextUtils.isEmpty(subLabel)) {
- holder.text2.setVisibility(View.VISIBLE);
- holder.text2.setText(subLabel);
+ if (info instanceof DisplayResolveInfo
+ && !((DisplayResolveInfo) info).hasDisplayLabel()) {
+ getLoadLabelTask((DisplayResolveInfo) info, holder).execute();
+ } else {
+ holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo());
}
if (info.isSuspended()) {
@@ -2085,6 +2093,9 @@ public class ResolverActivity extends Activity {
}
}
+ protected LoadLabelTask getLoadLabelTask(DisplayResolveInfo info, ViewHolder holder) {
+ return new LoadLabelTask(info, holder);
+ }
@VisibleForTesting
public static final class ResolvedComponentInfo {
@@ -2148,6 +2159,24 @@ public class ResolverActivity extends Activity {
text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
icon = (ImageView) view.findViewById(R.id.icon);
}
+
+ public void bindLabel(CharSequence label, CharSequence subLabel) {
+ if (!TextUtils.equals(text.getText(), label)) {
+ text.setText(label);
+ }
+
+ // Always show a subLabel for visual consistency across list items. Show an empty
+ // subLabel if the subLabel is the same as the label
+ if (TextUtils.equals(label, subLabel)) {
+ subLabel = null;
+ }
+
+ if (!TextUtils.equals(text2.getText(), subLabel)
+ && !TextUtils.isEmpty(subLabel)) {
+ text2.setVisibility(View.VISIBLE);
+ text2.setText(subLabel);
+ }
+ }
}
class ItemClickListener implements AdapterView.OnItemClickListener,
@@ -2200,6 +2229,34 @@ public class ResolverActivity extends Activity {
}
+ protected class LoadLabelTask extends AsyncTask<Void, Void, CharSequence[]> {
+ private final DisplayResolveInfo mDisplayResolveInfo;
+ private final ViewHolder mHolder;
+
+ protected LoadLabelTask(DisplayResolveInfo dri, ViewHolder holder) {
+ mDisplayResolveInfo = dri;
+ mHolder = holder;
+ }
+
+
+ @Override
+ protected CharSequence[] doInBackground(Void... voids) {
+ ResolveInfoPresentationGetter pg =
+ makePresentationGetter(mDisplayResolveInfo.mResolveInfo);
+ return new CharSequence[] {
+ pg.getLabel(),
+ pg.getSubLabel()
+ };
+ }
+
+ @Override
+ protected void onPostExecute(CharSequence[] result) {
+ mDisplayResolveInfo.setDisplayLabel(result[0]);
+ mDisplayResolveInfo.setExtendedInfo(result[1]);
+ mHolder.bindLabel(result[0], result[1]);
+ }
+ }
+
class LoadIconTask extends AsyncTask<Void, Void, Drawable> {
protected final DisplayResolveInfo mDisplayResolveInfo;
private final ResolveInfo mResolveInfo;
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 4099cfa51b33..8391ad2f12c2 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -49,9 +49,10 @@ interface IPlatformCompat
* you do not need to call this API directly. The change will be reported for you.
*
* @param changeId The ID of the compatibility change taking effect.
+ * @param userId The ID of the user that the operation is done for.
* @param packageName The package name of the app in question.
*/
- void reportChangeByPackageName(long changeId, in String packageName);
+ void reportChangeByPackageName(long changeId, in String packageName, int userId);
/**
* Reports that a compatibility change is affecting an app process now.
@@ -86,7 +87,7 @@ interface IPlatformCompat
* be called when implementing functionality on behalf of the affected app.
*
* <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a package name
- * instead of an {@link ApplicationInfo}
+ * and userId instead of an {@link ApplicationInfo}
* object, and finds an app info object based on the package name. Returns {@code true} if
* there is no installed package by that name.
*
@@ -100,9 +101,10 @@ interface IPlatformCompat
*
* @param changeId The ID of the compatibility change in question.
* @param packageName The package name of the app in question.
+ * @param userId The ID of the user that the operation is done for.
* @return {@code true} if the change is enabled for the current app.
*/
- boolean isChangeEnabledByPackageName(long changeId, in String packageName);
+ boolean isChangeEnabledByPackageName(long changeId, in String packageName, int userId);
/**
* Query if a given compatibility change is enabled for an app process. This method should
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 363e5498f87c..865ec27e00c4 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -254,6 +254,18 @@ public class ZygoteInit {
InputStream is;
try {
+ // If we are profiling the boot image, avoid preloading classes.
+ // Can't use device_config since we are the zygote.
+ String prop = SystemProperties.get(
+ "persist.device_config.runtime_native_boot.profilebootclasspath", "");
+ // Might be empty if the property is unset since the default is "".
+ if (prop.length() == 0) {
+ prop = SystemProperties.get("dalvik.vm.profilebootclasspath", "");
+ }
+ if ("true".equals(prop)) {
+ return;
+ }
+
is = new FileInputStream(PRELOADED_CLASSES);
} catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 94be61f40eae..97dae599f5c2 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2406,6 +2406,11 @@ enum PageId {
// CATEGORY: SETTINGS
// OS: Q
SETTINGS_AWARE_DISPLAY = 1750;
+
+ // OPEN: Settings > System > Input & Gesture > tap gesture
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_GESTURE_TAP = 1751;
// ---- End Q Constants, all Q constants go above this line ----
// OPEN: Settings > Network & Internet > Wi-Fi > Click new network
// CATEGORY: SETTINGS
@@ -2426,4 +2431,9 @@ enum PageId {
// and under gesture navigation mode.
DIALOG_TOGGLE_SCREEN_MAGNIFICATION_GESTURE_NAVIGATION = 1802;
+ // OPEN: Settings > Security & screen lock -> Encryption & credentials > Install a certificate
+ // CATEGORY: SETTINGS
+ // OS: R
+ INSTALL_CERTIFICATE_FROM_STORAGE = 1803;
+
}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index f7d4b3f47edf..31c19ca1be70 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -281,6 +281,7 @@ message GlobalSettingsProto {
optional SettingProto force_rtl = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto emulate_display_cutout = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto force_desktop_mode_on_external_displays = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto enable_sizecompat_freeform = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Development development = 39;
diff --git a/core/proto/android/server/notificationhistory.proto b/core/proto/android/server/notificationhistory.proto
index 148bd7e4b663..1e6ee3f1a3a8 100644
--- a/core/proto/android/server/notificationhistory.proto
+++ b/core/proto/android/server/notificationhistory.proto
@@ -46,7 +46,7 @@ message NotificationHistoryProto {
// The uid of the package that posted the notification
optional int32 uid = 7;
- // The user id of the package that posted the notification
+ // The user id that the notification was posted to
optional int32 user_id = 8;
// The time at which the notification was posted
optional int64 posted_time_ms = 9;
@@ -71,19 +71,19 @@ message NotificationHistoryProto {
optional ImageTypeEnum image_type = 1;
optional string image_bitmap_filename = 2;
optional int32 image_resource_id = 3;
- optional bytes image_data = 4;
- optional string image_uri = 5;
+ optional string image_resource_id_package = 4;
+ optional bytes image_data = 5;
+ optional int32 image_data_length = 6;
+ optional int32 image_data_offset = 7;
+ optional string image_uri = 8;
}
}
- // The time the last entry was written
- optional int64 end_time_ms = 1;
// Pool of strings to save space
- optional StringPool stringpool = 2;
+ optional StringPool string_pool = 1;
// Versioning fields
- optional int32 major_version = 3;
- optional int32 minor_version = 4;
+ optional int32 major_version = 2;
// List of historical notifications
- repeated Notification notification = 5;
+ repeated Notification notification = 3;
}
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index 2e1de79b199f..40c5a85e1f24 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -32,6 +32,7 @@ message UsbServiceDumpProto {
optional UsbPortManagerProto port_manager = 3;
optional UsbAlsaManagerProto alsa_manager = 4;
optional UsbSettingsManagerProto settings_manager = 5;
+ optional UsbPermissionsManagerProto permissions_manager = 6;
}
message UsbDeviceManagerProto {
@@ -309,40 +310,83 @@ message UsbUserSettingsManagerProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 user_id = 1;
- repeated UsbSettingsDevicePermissionProto device_permissions = 2;
- repeated UsbSettingsAccessoryPermissionProto accessory_permissions = 3;
+ reserved 2; // previously device_permissions, now unused
+ reserved 3; // previously accessory_permissions, now unused
repeated UsbDeviceAttachedActivities device_attached_activities = 4;
repeated UsbAccessoryAttachedActivities accessory_attached_activities = 5;
}
-message UsbSettingsDevicePermissionProto {
+message UsbProfileGroupSettingsManagerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // The user id of the personal profile if the device has a work profile.
+ optional int32 parent_user_id = 1;
+ repeated UsbSettingsDevicePreferenceProto device_preferences = 2;
+ repeated UsbSettingsAccessoryPreferenceProto accessory_preferences = 3;
+}
+
+message UsbSettingsDevicePreferenceProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional UsbDeviceFilterProto filter = 1;
+ optional UserPackageProto user_package = 2;
+}
+
+message UsbPermissionsManagerProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ repeated UsbUserPermissionsManagerProto user_permissions = 1;
+}
+
+message UsbUserPermissionsManagerProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ optional int32 user_id = 1;
+
+ repeated UsbDevicePermissionProto device_permissions = 2;
+ repeated UsbAccessoryPermissionProto accessory_permissions = 3;
+
+ repeated UsbDevicePersistentPermissionProto device_persistent_permissions = 4;
+ repeated UsbAccessoryPersistentPermissionProto accessory_persistent_permissions = 5;
+}
+
+message UsbDevicePermissionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Name of device set by manufacturer
+ // All devices of the same model have the same name
optional string device_name = 1;
repeated int32 uids = 2;
}
-message UsbSettingsAccessoryPermissionProto {
+message UsbAccessoryPermissionProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ // Description of accessory set by manufacturer
+ // All accessories of the same model have the same description
optional string accessory_description = 1;
repeated int32 uids = 2;
}
-message UsbProfileGroupSettingsManagerProto {
+message UsbDevicePersistentPermissionProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
- // The user id of the personal profile if the device has a work profile.
- optional int32 parent_user_id = 1;
- repeated UsbSettingsDevicePreferenceProto device_preferences = 2;
- repeated UsbSettingsAccessoryPreferenceProto accessory_preferences = 3;
+ optional UsbDeviceFilterProto device_filter = 1;
+ repeated UsbUidPermissionProto permission_values = 2;
}
-message UsbSettingsDevicePreferenceProto {
+message UsbAccessoryPersistentPermissionProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
- optional UsbDeviceFilterProto filter = 1;
- optional UserPackageProto user_package = 2;
+ optional UsbAccessoryFilterProto accessory_filter = 1;
+ repeated UsbUidPermissionProto permission_values = 2;
+}
+
+message UsbUidPermissionProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 uid = 1;
+ optional bool is_granted = 2;
}
message UsbDeviceFilterProto {
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d88178d481e9..d39d50739e1d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3440,6 +3440,15 @@
<!-- A notification is shown when the user connects to a Wi-Fi network and the system detects that that network has no Internet access. This is the notification's message. -->
<string name="wifi_no_internet_detailed">Tap for options</string>
+ <!-- A notification is shown when the user connects to a mobile network without internet access. This is the notification's title. -->
+ <string name="mobile_no_internet">Mobile network has no internet access</string>
+
+ <!-- A notification is shown when the user connects to a non-mobile and non-wifi network without internet access. This is the notification's title. -->
+ <string name="other_networks_no_internet">Network has no internet access</string>
+
+ <!-- A notification is shown when connected network without internet due to private dns validation failed. This is the notification's message. [CHAR LIMIT=NONE] -->
+ <string name="private_dns_broken_detailed">Private DNS server cannot be accessed</string>
+
<!-- A notification is shown after the user logs in to a captive portal network, to indicate that the network should now have internet connectivity. This is the message of notification. [CHAR LIMIT=50] -->
<string name="captive_portal_logged_in_detailed">Connected</string>
<!-- A notification is shown when the user connects to a network that doesn't have access to some services (e.g. Push notifications may not work). This is the notification's title. [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e2f57fda5bc5..8d96c79ed3d8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -593,9 +593,12 @@
<java-symbol type="string" name="menu_space_shortcut_label" />
<java-symbol type="string" name="menu_shift_shortcut_label" />
<java-symbol type="string" name="menu_sym_shortcut_label" />
+ <java-symbol type="string" name="mobile_no_internet" />
<java-symbol type="string" name="notification_title" />
+ <java-symbol type="string" name="other_networks_no_internet" />
<java-symbol type="string" name="permission_request_notification_with_subtitle" />
<java-symbol type="string" name="prepend_shortcut_label" />
+ <java-symbol type="string" name="private_dns_broken_detailed" />
<java-symbol type="string" name="paste_as_plain_text" />
<java-symbol type="string" name="replace" />
<java-symbol type="string" name="undo" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index f71c8b0558cf..2d1c61cc22fc 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -90,7 +90,7 @@
<shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
<!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
- <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529|70296|83782|3011|73240" />
+ <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529|70296|83782|3011|73240|72438" />
<!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
<shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}|4665" />
diff --git a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java b/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
deleted file mode 100644
index f6527da0ba1a..000000000000
--- a/core/tests/coretests/src/android/content/pm/VerificationParamsTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2012 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.pm;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.test.AndroidTestCase;
-
-import androidx.test.filters.LargeTest;
-
-/**
- * Tests the android.content.pm.VerificationParams class
- *
- * To test run:
- * ./development/testrunner/runtest.py frameworks-core -c android.content.pm.VerificationParamsTest
- */
-@LargeTest
-public class VerificationParamsTest extends AndroidTestCase {
-
- private final static String VERIFICATION_URI_STRING = "http://verification.uri/path";
- private final static String ORIGINATING_URI_STRING = "http://originating.uri/path";
- private final static String REFERRER_STRING = "http://referrer.uri/path";
- private final static int INSTALLER_UID = 42;
-
- private final static Uri VERIFICATION_URI = Uri.parse(VERIFICATION_URI_STRING);
- private final static Uri ORIGINATING_URI = Uri.parse(ORIGINATING_URI_STRING);
- private final static Uri REFERRER = Uri.parse(REFERRER_STRING);
-
- private final static int ORIGINATING_UID = 10042;
-
- public void testParcel() throws Exception {
- VerificationParams expected = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- Parcel parcel = Parcel.obtain();
- expected.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
-
- VerificationParams actual = VerificationParams.CREATOR.createFromParcel(parcel);
-
- assertEquals(VERIFICATION_URI, actual.getVerificationURI());
-
- assertEquals(ORIGINATING_URI, actual.getOriginatingURI());
-
- assertEquals(REFERRER, actual.getReferrer());
-
- assertEquals(ORIGINATING_UID, actual.getOriginatingUid());
- }
-
- public void testEquals_Success() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
- Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
- assertEquals(params1, params2);
- }
-
- public void testEquals_VerificationUri_Failure() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse("http://a.different.uri/"), Uri.parse(ORIGINATING_URI_STRING),
- Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
- assertFalse(params1.equals(params2));
- }
-
- public void testEquals_OriginatingUri_Failure() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse(VERIFICATION_URI_STRING), Uri.parse("http://a.different.uri/"),
- Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
- assertFalse(params1.equals(params2));
- }
-
- public void testEquals_Referrer_Failure() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
- Uri.parse("http://a.different.uri/"), ORIGINATING_UID);
-
- assertFalse(params1.equals(params2));
- }
-
- public void testEquals_Originating_Uid_Failure() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
- Uri.parse(REFERRER_STRING), 12345);
-
- assertFalse(params1.equals(params2));
- }
-
- public void testEquals_InstallerUid_Failure() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
- Uri.parse(REFERRER_STRING), ORIGINATING_UID);
- params2.setInstallerUid(INSTALLER_UID);
-
- assertFalse(params1.equals(params2));
- }
-
- public void testHashCode_Success() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
- Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
- assertEquals(params1.hashCode(), params2.hashCode());
- }
-
- public void testHashCode_VerificationUri_Failure() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(null, Uri.parse(ORIGINATING_URI_STRING),
- Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
- assertFalse(params1.hashCode() == params2.hashCode());
- }
-
- public void testHashCode_OriginatingUri_Failure() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse(VERIFICATION_URI_STRING), Uri.parse("http://a.different.uri/"),
- Uri.parse(REFERRER_STRING), ORIGINATING_UID);
-
- assertFalse(params1.hashCode() == params2.hashCode());
- }
-
- public void testHashCode_Referrer_Failure() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING), null,
- ORIGINATING_UID);
-
- assertFalse(params1.hashCode() == params2.hashCode());
- }
-
- public void testHashCode_Originating_Uid_Failure() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
- Uri.parse(REFERRER_STRING), 12345);
-
- assertFalse(params1.hashCode() == params2.hashCode());
- }
-
- public void testHashCode_InstallerUid_Failure() throws Exception {
- VerificationParams params1 = new VerificationParams(VERIFICATION_URI, ORIGINATING_URI,
- REFERRER, ORIGINATING_UID);
-
- VerificationParams params2 = new VerificationParams(
- Uri.parse(VERIFICATION_URI_STRING), Uri.parse(ORIGINATING_URI_STRING),
- Uri.parse(REFERRER_STRING), ORIGINATING_UID);
- params2.setInstallerUid(INSTALLER_UID);
-
- assertFalse(params1.hashCode() == params2.hashCode());
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 453bddd9e962..0fa29bf4436a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -38,6 +38,7 @@ import android.view.View;
import android.widget.RelativeLayout;
import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.Espresso;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
@@ -82,6 +83,7 @@ public class ResolverActivityTest {
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ Espresso.registerIdlingResources(activity.getLabelIdlingResource());
waitForIdle();
assertThat(activity.getAdapter().getCount(), is(2));
@@ -214,6 +216,7 @@ public class ResolverActivityTest {
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ Espresso.registerIdlingResources(activity.getLabelIdlingResource());
waitForIdle();
// The other entry is filtered to the last used slot
@@ -251,6 +254,7 @@ public class ResolverActivityTest {
Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ Espresso.registerIdlingResources(activity.getLabelIdlingResource());
waitForIdle();
// The other entry is filtered to the other profile slot
@@ -296,6 +300,7 @@ public class ResolverActivityTest {
.thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0));
final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ Espresso.registerIdlingResources(activity.getLabelIdlingResource());
waitForIdle();
// The other entry is filtered to the other profile slot
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 6218fa9fbac8..5ac1489bcfef 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -16,6 +16,8 @@
package com.android.internal.app;
+import static junit.framework.Assert.assertEquals;
+
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Matchers.any;
@@ -126,7 +128,7 @@ public class ResolverListControllerTest {
String annotation = "test_annotation";
Intent sendIntent = createSendImageIntent(annotation);
String refererPackage = "test_referer_package";
- List<ResolvedComponentInfo> resolvedComponents = createResolvedComponentsForTest(15);
+ List<ResolvedComponentInfo> resolvedComponents = createResolvedComponentsForTest(10);
mUsm = new UsageStatsManager(mMockContext, mMockService);
when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent,
@@ -135,7 +137,18 @@ public class ResolverListControllerTest {
mController.topK(topKList, 5);
List<ResolvedComponentInfo> sortList = new ArrayList<>(topKList);
mController.sort(sortList);
- assertThat(sortList.subList(0, 5), is(topKList.subList(0, 5)));
+ assertEquals("Top k elements should be sorted when input size greater than k.",
+ sortList.subList(0, 5), topKList.subList(0, 5));
+ mController.topK(topKList, 10);
+ sortList = new ArrayList<>(topKList);
+ mController.sort(sortList);
+ assertEquals("All elements should be sorted when input size equals k.",
+ sortList, topKList);
+ mController.topK(topKList, 15);
+ sortList = new ArrayList<>(topKList);
+ mController.sort(sortList);
+ assertEquals("All elements should be sorted when input size less than k.",
+ sortList, topKList);
}
private UsageStats initStats(String packageName, String action,
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 83f6bc2e8bf3..9082543e8ebc 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -21,6 +21,8 @@ import static org.mockito.Mockito.mock;
import android.app.usage.UsageStatsManager;
import android.content.pm.PackageManager;
+import androidx.test.espresso.idling.CountingIdlingResource;
+
import java.util.function.Function;
/*
@@ -29,6 +31,12 @@ import java.util.function.Function;
public class ResolverWrapperActivity extends ResolverActivity {
static final OverrideData sOverrides = new OverrideData();
private UsageStatsManager mUsm;
+ private CountingIdlingResource mLabelIdlingResource =
+ new CountingIdlingResource("LoadLabelTask");
+
+ public CountingIdlingResource getLabelIdlingResource() {
+ return mLabelIdlingResource;
+ }
ResolveListAdapter getAdapter() {
return mAdapter;
@@ -64,6 +72,11 @@ public class ResolverWrapperActivity extends ResolverActivity {
return super.getPackageManager();
}
+ @Override
+ protected LoadLabelTask getLoadLabelTask(DisplayResolveInfo info, ViewHolder holder) {
+ return new LoadLabelWrapperTask(info, holder);
+ }
+
/**
* We cannot directly mock the activity created since instrumentation creates it.
* <p>
@@ -83,4 +96,22 @@ public class ResolverWrapperActivity extends ResolverActivity {
resolverListController = mock(ResolverListController.class);
}
}
+
+ class LoadLabelWrapperTask extends LoadLabelTask {
+
+ protected LoadLabelWrapperTask(DisplayResolveInfo dri, ViewHolder holder) {
+ super(dri, holder);
+ }
+
+ @Override
+ protected void onPreExecute() {
+ mLabelIdlingResource.increment();
+ }
+
+ @Override
+ protected void onPostExecute(CharSequence[] result) {
+ super.onPostExecute(result);
+ mLabelIdlingResource.decrement();
+ }
+ }
} \ No newline at end of file
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index ac742e217ceb..a0215e185a4c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -370,6 +370,7 @@ applications that come with the platform
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
+ <permission name="android.permission.READ_DEVICE_CONFIG"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<permission name="android.permission.REQUEST_NETWORK_SCORES"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 54995ac9d050..f25910beb537 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -71,6 +71,15 @@ public class Credentials {
/** Key containing suffix of lockdown VPN profile. */
public static final String LOCKDOWN_VPN = "LOCKDOWN_VPN";
+ /** Name of CA certificate usage. */
+ public static final String CERTIFICATE_USAGE_CA = "ca";
+
+ /** Name of User certificate usage. */
+ public static final String CERTIFICATE_USAGE_USER = "user";
+
+ /** Name of WIFI certificate usage. */
+ public static final String CERTIFICATE_USAGE_WIFI = "wifi";
+
/** Data type for public keys. */
public static final String EXTRA_PUBLIC_KEY = "KEY";
@@ -91,6 +100,11 @@ public class Credentials {
public static final String EXTRA_INSTALL_AS_UID = "install_as_uid";
/**
+ * Intent extra: type of the certificate to install
+ */
+ public static final String EXTRA_CERTIFICATE_USAGE = "certificate_install_usage";
+
+ /**
* Intent extra: name for the user's key pair.
*/
public static final String EXTRA_USER_KEY_ALIAS = "user_key_pair_name";
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 61b72cf9d193..2041e7a46364 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -221,7 +221,6 @@ cc_defaults {
"pipeline/skia/SkiaPipeline.cpp",
"pipeline/skia/SkiaProfileRenderer.cpp",
"pipeline/skia/SkiaVulkanPipeline.cpp",
- "pipeline/skia/VectorDrawableAtlas.cpp",
"pipeline/skia/VkFunctorDrawable.cpp",
"pipeline/skia/VkInteropFunctorDrawable.cpp",
"renderstate/RenderState.cpp",
@@ -347,7 +346,6 @@ cc_test {
"tests/unit/ThreadBaseTests.cpp",
"tests/unit/TypefaceTests.cpp",
"tests/unit/VectorDrawableTests.cpp",
- "tests/unit/VectorDrawableAtlasTests.cpp",
"tests/unit/WebViewFunctorManagerTests.cpp",
],
}
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 61403aa93c97..217b0c4a8b98 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -496,87 +496,6 @@ Bitmap& Tree::getBitmapUpdateIfDirty() {
return *mCache.bitmap;
}
-void Tree::updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context) {
-#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
- SkRect dst;
- sk_sp<SkSurface> surface = mCache.getSurface(&dst);
- bool canReuseSurface = surface && dst.width() >= mProperties.getScaledWidth() &&
- dst.height() >= mProperties.getScaledHeight();
- if (!canReuseSurface) {
- int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
- int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
- auto atlasEntry = atlas->requestNewEntry(scaledWidth, scaledHeight, context);
- if (INVALID_ATLAS_KEY != atlasEntry.key) {
- dst = atlasEntry.rect;
- surface = atlasEntry.surface;
- mCache.setAtlas(atlas, atlasEntry.key);
- } else {
- // don't draw, if we failed to allocate an offscreen buffer
- mCache.clear();
- surface.reset();
- }
- }
- if (!canReuseSurface || mCache.dirty) {
- if (surface) {
- Bitmap& bitmap = getBitmapUpdateIfDirty();
- SkBitmap skiaBitmap;
- bitmap.getSkBitmap(&skiaBitmap);
- surface->writePixels(skiaBitmap, dst.fLeft, dst.fTop);
- }
- mCache.dirty = false;
- }
-#endif
-}
-
-void Tree::Cache::setAtlas(sp<skiapipeline::VectorDrawableAtlas> newAtlas,
- skiapipeline::AtlasKey newAtlasKey) {
- LOG_ALWAYS_FATAL_IF(newAtlasKey == INVALID_ATLAS_KEY);
- clear();
- mAtlas = newAtlas;
- mAtlasKey = newAtlasKey;
-}
-
-sk_sp<SkSurface> Tree::Cache::getSurface(SkRect* bounds) {
- sk_sp<SkSurface> surface;
-#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
- sp<skiapipeline::VectorDrawableAtlas> atlas = mAtlas.promote();
- if (atlas.get() && mAtlasKey != INVALID_ATLAS_KEY) {
- auto atlasEntry = atlas->getEntry(mAtlasKey);
- *bounds = atlasEntry.rect;
- surface = atlasEntry.surface;
- mAtlasKey = atlasEntry.key;
- }
-#endif
-
- return surface;
-}
-
-void Tree::Cache::clear() {
-#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
- if (mAtlasKey != INVALID_ATLAS_KEY) {
- if (renderthread::RenderThread::isCurrent()) {
- sp<skiapipeline::VectorDrawableAtlas> lockAtlas = mAtlas.promote();
- if (lockAtlas.get()) {
- lockAtlas->releaseEntry(mAtlasKey);
- }
- } else {
- // VectorDrawableAtlas can be accessed only on RenderThread.
- // Use by-copy capture of the current Cache variables, because "this" may not be valid
- // by the time the lambda is evaluated on RenderThread.
- renderthread::RenderThread::getInstance().queue().post(
- [atlas = mAtlas, atlasKey = mAtlasKey]() {
- sp<skiapipeline::VectorDrawableAtlas> lockAtlas = atlas.promote();
- if (lockAtlas.get()) {
- lockAtlas->releaseEntry(atlasKey);
- }
- });
- }
- mAtlasKey = INVALID_ATLAS_KEY;
- }
- mAtlas = nullptr;
-#endif
-}
-
void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) {
if (canvas->quickReject(bounds)) {
// The RenderNode is on screen, but the AVD is not.
@@ -587,39 +506,14 @@ void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint)
SkPaint paint = inPaint;
paint.setAlpha(mProperties.getRootAlpha() * 255);
- if (canvas->getGrContext() == nullptr) {
- // Recording to picture, don't use the SkSurface which won't work off of renderthread.
- Bitmap& bitmap = getBitmapUpdateIfDirty();
- SkBitmap skiaBitmap;
- bitmap.getSkBitmap(&skiaBitmap);
-
- int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
- int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
- canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
- &paint, SkCanvas::kFast_SrcRectConstraint);
- return;
- }
+ Bitmap& bitmap = getBitmapUpdateIfDirty();
+ SkBitmap skiaBitmap;
+ bitmap.getSkBitmap(&skiaBitmap);
- SkRect src;
- sk_sp<SkSurface> vdSurface = mCache.getSurface(&src);
- if (vdSurface) {
- canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src, bounds, &paint,
- SkCanvas::kFast_SrcRectConstraint);
- } else {
- // Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure.
- // We render the VD into a temporary standalone buffer and mark the frame as dirty. Next
- // frame will be cached into the atlas.
- Bitmap& bitmap = getBitmapUpdateIfDirty();
- SkBitmap skiaBitmap;
- bitmap.getSkBitmap(&skiaBitmap);
-
- int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
- int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
- canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
- &paint, SkCanvas::kFast_SrcRectConstraint);
- mCache.clear();
- markDirty();
- }
+ int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
+ int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
+ canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
+ &paint, SkCanvas::kFast_SrcRectConstraint);
}
void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 9c0bb161380c..e1b6f2adde74 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -651,46 +651,13 @@ public:
void getPaintFor(SkPaint* outPaint, const TreeProperties &props) const;
BitmapPalette computePalette();
- /**
- * Draws VD into a GPU backed surface.
- * This should always be called from RT and it works with Skia pipeline only.
- */
- void updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context);
-
void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); }
private:
class Cache {
public:
sk_sp<Bitmap> bitmap; // used by HWUI pipeline and software
- // TODO: use surface instead of bitmap when drawing in software canvas
bool dirty = true;
-
- // the rest of the code in Cache is used by Skia pipelines only
-
- ~Cache() { clear(); }
-
- /**
- * Stores a weak pointer to the atlas and a key.
- */
- void setAtlas(sp<skiapipeline::VectorDrawableAtlas> atlas,
- skiapipeline::AtlasKey newAtlasKey);
-
- /**
- * Gets a surface and bounds from the atlas.
- *
- * @return nullptr if the altas has been deleted.
- */
- sk_sp<SkSurface> getSurface(SkRect* bounds);
-
- /**
- * Releases atlas key from the atlas, which makes it available for reuse.
- */
- void clear();
-
- private:
- wp<skiapipeline::VectorDrawableAtlas> mAtlas;
- skiapipeline::AtlasKey mAtlasKey = INVALID_ATLAS_KEY;
};
bool allocateBitmapIfNeeded(Cache& cache, int width, int height);
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index d7076d4cf424..158c3493a90c 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -150,12 +150,7 @@ bool SkiaDisplayList::prepareListAndChildren(
const SkRect& bounds = vectorDrawable->properties().getBounds();
if (intersects(info.screenSize, totalMatrix, bounds)) {
isDirty = true;
-#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
- static_cast<SkiaPipeline*>(info.canvasContext.getRenderPipeline())
- ->getVectorDrawables()
- ->push_back(vectorDrawable);
vectorDrawable->setPropertyChangeWillBeConsumed(true);
-#endif
}
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 3010206cdc5b..87ef7fc9a6e1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -43,7 +43,6 @@ namespace uirenderer {
namespace skiapipeline {
SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
- mVectorDrawables.reserve(30);
}
SkiaPipeline::~SkiaPipeline() {
@@ -73,18 +72,11 @@ void SkiaPipeline::unpinImages() {
mPinnedImages.clear();
}
-void SkiaPipeline::onPrepareTree() {
- // The only time mVectorDrawables is not empty is if prepare tree was called 2 times without
- // a renderFrame in the middle.
- mVectorDrawables.clear();
-}
-
void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry,
LayerUpdateQueue* layerUpdateQueue, bool opaque,
const LightInfo& lightInfo) {
LightingInfo::updateLighting(lightGeometry, lightInfo);
ATRACE_NAME("draw layers");
- renderVectorDrawableCache();
renderLayersImpl(*layerUpdateQueue, opaque);
layerUpdateQueue->clear();
}
@@ -213,19 +205,6 @@ void SkiaPipeline::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
}
}
-void SkiaPipeline::renderVectorDrawableCache() {
- if (!mVectorDrawables.empty()) {
- sp<VectorDrawableAtlas> atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas();
- auto grContext = mRenderThread.getGrContext();
- atlas->prepareForDraw(grContext);
- ATRACE_NAME("Update VectorDrawables");
- for (auto vd : mVectorDrawables) {
- vd->updateCache(atlas, grContext);
- }
- mVectorDrawables.clear();
- }
-}
-
static void savePictureAsync(const sk_sp<SkData>& data, const std::string& filename) {
CommonPool::post([data, filename] {
if (0 == access(filename.c_str(), F_OK)) {
@@ -380,8 +359,6 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli
Properties::skpCaptureEnabled = true;
}
- renderVectorDrawableCache();
-
// draw all layers up front
renderLayersImpl(layers, opaque);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 37b559f92f05..215ff36ebb2b 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -41,7 +41,6 @@ public:
bool pinImages(std::vector<SkImage*>& mutableImages) override;
bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override { return false; }
void unpinImages() override;
- void onPrepareTree() override;
void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
bool opaque, const LightInfo& lightInfo) override;
@@ -57,8 +56,6 @@ public:
const Rect& contentDrawBounds, sk_sp<SkSurface> surface,
const SkMatrix& preTransform);
- std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; }
-
static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap);
void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque);
@@ -93,11 +90,6 @@ private:
const std::vector<sp<RenderNode>>& nodes, const Rect& contentDrawBounds,
sk_sp<SkSurface> surface, const SkMatrix& preTransform);
- /**
- * Render mVectorDrawables into offscreen buffers.
- */
- void renderVectorDrawableCache();
-
// Called every frame. Normally returns early with screen canvas.
// But when capture is enabled, returns an nwaycanvas where commands are also recorded.
SkCanvas* tryCapture(SkSurface* surface);
@@ -113,11 +105,6 @@ private:
std::vector<sk_sp<SkImage>> mPinnedImages;
- /**
- * populated by prepareTree with dirty VDs
- */
- std::vector<VectorDrawableRoot*> mVectorDrawables;
-
// Block of properties used only for debugging to record a SkPicture and save it in a file.
// There are three possible ways of recording drawing commands.
enum class CaptureMode {
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
deleted file mode 100644
index e783f389feb8..000000000000
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "VectorDrawableAtlas.h"
-
-#include <GrRectanizer_pow2.h>
-#include <SkCanvas.h>
-#include <cmath>
-#include "renderthread/RenderProxy.h"
-#include "renderthread/RenderThread.h"
-#include "utils/TraceUtils.h"
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode)
- : mWidth((int)std::sqrt(surfaceArea))
- , mHeight((int)std::sqrt(surfaceArea))
- , mStorageMode(storageMode) {}
-
-void VectorDrawableAtlas::prepareForDraw(GrContext* context) {
- if (StorageMode::allowSharedSurface == mStorageMode) {
- if (!mSurface) {
- mSurface = createSurface(mWidth, mHeight, context);
- mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight);
- mPixelUsedByVDs = 0;
- mPixelAllocated = 0;
- mConsecutiveFailures = 0;
- mFreeRects.clear();
- } else {
- if (isFragmented()) {
- // Invoke repack outside renderFrame to avoid jank.
- renderthread::RenderProxy::repackVectorDrawableAtlas();
- }
- }
- }
-}
-
-#define MAX_CONSECUTIVE_FAILURES 5
-#define MAX_UNUSED_RATIO 2.0f
-
-bool VectorDrawableAtlas::isFragmented() {
- return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES &&
- mPixelUsedByVDs * MAX_UNUSED_RATIO < mPixelAllocated;
-}
-
-void VectorDrawableAtlas::repackIfNeeded(GrContext* context) {
- // We repackage when atlas failed to allocate space MAX_CONSECUTIVE_FAILURES consecutive
- // times and the atlas allocated pixels are at least MAX_UNUSED_RATIO times higher than pixels
- // used by atlas VDs.
- if (isFragmented() && mSurface) {
- repack(context);
- }
-}
-
-// compare to CacheEntry objects based on VD area.
-bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second) {
- return first.VDrect.width() * first.VDrect.height() <
- second.VDrect.width() * second.VDrect.height();
-}
-
-void VectorDrawableAtlas::repack(GrContext* context) {
- ATRACE_CALL();
- sk_sp<SkSurface> newSurface;
- SkCanvas* canvas = nullptr;
- if (StorageMode::allowSharedSurface == mStorageMode) {
- newSurface = createSurface(mWidth, mHeight, context);
- if (!newSurface) {
- return;
- }
- canvas = newSurface->getCanvas();
- canvas->clear(SK_ColorTRANSPARENT);
- mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight);
- } else {
- if (!mSurface) {
- return; // nothing to repack
- }
- mRectanizer.reset();
- }
- mFreeRects.clear();
- SkImage* sourceImageAtlas = nullptr;
- if (mSurface) {
- sourceImageAtlas = mSurface->makeImageSnapshot().get();
- }
-
- // Sort the list by VD size, which allows for the smallest VDs to get first in the atlas.
- // Sorting is safe, because it does not affect iterator validity.
- if (mRects.size() <= 100) {
- mRects.sort(compareCacheEntry);
- }
-
- for (CacheEntry& entry : mRects) {
- SkRect currentVDRect = entry.VDrect;
- SkImage* sourceImage; // copy either from the atlas or from a standalone surface
- if (entry.surface) {
- if (!fitInAtlas(currentVDRect.width(), currentVDRect.height())) {
- continue; // don't even try to repack huge VD
- }
- sourceImage = entry.surface->makeImageSnapshot().get();
- } else {
- sourceImage = sourceImageAtlas;
- }
- size_t VDRectArea = currentVDRect.width() * currentVDRect.height();
- SkIPoint16 pos;
- if (canvas && mRectanizer->addRect(currentVDRect.width(), currentVDRect.height(), &pos)) {
- SkRect newRect =
- SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(), currentVDRect.height());
- canvas->drawImageRect(sourceImage, currentVDRect, newRect, nullptr);
- entry.VDrect = newRect;
- entry.rect = newRect;
- if (entry.surface) {
- // A rectangle moved from a standalone surface to the atlas.
- entry.surface = nullptr;
- mPixelUsedByVDs += VDRectArea;
- }
- } else {
- // Repack failed for this item. If it is not already, store it in a standalone
- // surface.
- if (!entry.surface) {
- // A rectangle moved from an atlas to a standalone surface.
- mPixelUsedByVDs -= VDRectArea;
- SkRect newRect = SkRect::MakeWH(currentVDRect.width(), currentVDRect.height());
- entry.surface = createSurface(newRect.width(), newRect.height(), context);
- auto tempCanvas = entry.surface->getCanvas();
- tempCanvas->clear(SK_ColorTRANSPARENT);
- tempCanvas->drawImageRect(sourceImageAtlas, currentVDRect, newRect, nullptr);
- entry.VDrect = newRect;
- entry.rect = newRect;
- }
- }
- }
- mPixelAllocated = mPixelUsedByVDs;
- context->flush();
- mSurface = newSurface;
- mConsecutiveFailures = 0;
-}
-
-AtlasEntry VectorDrawableAtlas::requestNewEntry(int width, int height, GrContext* context) {
- AtlasEntry result;
- if (width <= 0 || height <= 0) {
- return result;
- }
-
- if (mSurface) {
- const size_t area = width * height;
-
- // Use a rectanizer to allocate unused space from the atlas surface.
- bool notTooBig = fitInAtlas(width, height);
- SkIPoint16 pos;
- if (notTooBig && mRectanizer->addRect(width, height, &pos)) {
- mPixelUsedByVDs += area;
- mPixelAllocated += area;
- result.rect = SkRect::MakeXYWH(pos.fX, pos.fY, width, height);
- result.surface = mSurface;
- auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, nullptr);
- CacheEntry* entry = &(*eraseIt);
- entry->eraseIt = eraseIt;
- result.key = reinterpret_cast<AtlasKey>(entry);
- mConsecutiveFailures = 0;
- return result;
- }
-
- // Try to reuse atlas memory from rectangles freed by "releaseEntry".
- auto freeRectIt = mFreeRects.lower_bound(area);
- while (freeRectIt != mFreeRects.end()) {
- SkRect& freeRect = freeRectIt->second;
- if (freeRect.width() >= width && freeRect.height() >= height) {
- result.rect = SkRect::MakeXYWH(freeRect.fLeft, freeRect.fTop, width, height);
- result.surface = mSurface;
- auto eraseIt = mRects.emplace(mRects.end(), result.rect, freeRect, nullptr);
- CacheEntry* entry = &(*eraseIt);
- entry->eraseIt = eraseIt;
- result.key = reinterpret_cast<AtlasKey>(entry);
- mPixelUsedByVDs += area;
- mFreeRects.erase(freeRectIt);
- mConsecutiveFailures = 0;
- return result;
- }
- freeRectIt++;
- }
-
- if (notTooBig && mConsecutiveFailures <= MAX_CONSECUTIVE_FAILURES) {
- mConsecutiveFailures++;
- }
- }
-
- // Allocate a surface for a rectangle that is too big or if atlas is full.
- if (nullptr != context) {
- result.rect = SkRect::MakeWH(width, height);
- result.surface = createSurface(width, height, context);
- auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, result.surface);
- CacheEntry* entry = &(*eraseIt);
- entry->eraseIt = eraseIt;
- result.key = reinterpret_cast<AtlasKey>(entry);
- }
-
- return result;
-}
-
-AtlasEntry VectorDrawableAtlas::getEntry(AtlasKey atlasKey) {
- AtlasEntry result;
- if (INVALID_ATLAS_KEY != atlasKey) {
- CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
- result.rect = entry->VDrect;
- result.surface = entry->surface;
- if (!result.surface) {
- result.surface = mSurface;
- }
- result.key = atlasKey;
- }
- return result;
-}
-
-void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) {
- if (INVALID_ATLAS_KEY != atlasKey) {
- if (!renderthread::RenderThread::isCurrent()) {
- {
- AutoMutex _lock(mReleaseKeyLock);
- mKeysForRelease.push_back(atlasKey);
- }
- // invoke releaseEntry on the renderthread
- renderthread::RenderProxy::releaseVDAtlasEntries();
- return;
- }
- CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
- if (!entry->surface) {
- // Store freed atlas rectangles in "mFreeRects" and try to reuse them later, when atlas
- // is full.
- SkRect& removedRect = entry->rect;
- size_t rectArea = removedRect.width() * removedRect.height();
- mFreeRects.emplace(rectArea, removedRect);
- SkRect& removedVDRect = entry->VDrect;
- size_t VDRectArea = removedVDRect.width() * removedVDRect.height();
- mPixelUsedByVDs -= VDRectArea;
- mConsecutiveFailures = 0;
- }
- auto eraseIt = entry->eraseIt;
- mRects.erase(eraseIt);
- }
-}
-
-void VectorDrawableAtlas::delayedReleaseEntries() {
- AutoMutex _lock(mReleaseKeyLock);
- for (auto key : mKeysForRelease) {
- releaseEntry(key);
- }
- mKeysForRelease.clear();
-}
-
-sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) {
- SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType);
- // This must have a top-left origin so that calls to surface->canvas->writePixels
- // performs a basic texture upload instead of a more complex drawing operation
- return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 0, kTopLeft_GrSurfaceOrigin,
- nullptr);
-}
-
-void VectorDrawableAtlas::setStorageMode(StorageMode mode) {
- mStorageMode = mode;
- if (StorageMode::disallowSharedSurface == mStorageMode && mSurface) {
- mSurface.reset();
- mRectanizer.reset();
- mFreeRects.clear();
- }
-}
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
deleted file mode 100644
index 5e892aa7e92c..000000000000
--- a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <SkSurface.h>
-#include <utils/FatVector.h>
-#include <utils/RefBase.h>
-#include <utils/Thread.h>
-#include <list>
-#include <map>
-
-class GrRectanizer;
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-typedef uintptr_t AtlasKey;
-
-#define INVALID_ATLAS_KEY 0
-
-struct AtlasEntry {
- sk_sp<SkSurface> surface;
- SkRect rect;
- AtlasKey key = INVALID_ATLAS_KEY;
-};
-
-/**
- * VectorDrawableAtlas provides offscreen buffers used to draw VD and AnimatedVD.
- * VectorDrawableAtlas can allocate a standalone surface or provide a subrect from a shared surface.
- * VectorDrawableAtlas is owned by the CacheManager and weak pointers are kept by each
- * VectorDrawable that is using it. VectorDrawableAtlas and its surface can be deleted at any time,
- * except during a renderFrame call. VectorDrawable does not contain a pointer to atlas SkSurface
- * nor any coordinates into the atlas, but instead holds a rectangle "id", which is resolved only
- * when drawing. This design makes VectorDrawableAtlas free to move the data internally.
- * At draw time a VectorDrawable may find, that its atlas has been deleted, which will make it
- * draw in a standalone cache surface not part of an atlas. In this case VD won't use
- * VectorDrawableAtlas until the next frame.
- * VectorDrawableAtlas tries to fit VDs in the atlas SkSurface. If there is not enough space in
- * the atlas, VectorDrawableAtlas creates a standalone surface for each VD.
- * When a VectorDrawable is deleted, it invokes VectorDrawableAtlas::releaseEntry, which is keeping
- * track of free spaces and allow to reuse the surface for another VD.
- */
-// TODO: Check if not using atlas for AnimatedVD is more efficient.
-// TODO: For low memory situations, when there are no paint effects in VD, we may render without an
-// TODO: offscreen surface.
-class VectorDrawableAtlas : public virtual RefBase {
-public:
- enum class StorageMode { allowSharedSurface, disallowSharedSurface };
-
- explicit VectorDrawableAtlas(size_t surfaceArea,
- StorageMode storageMode = StorageMode::allowSharedSurface);
-
- /**
- * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the
- * atlas at a later time.
- */
- void prepareForDraw(GrContext* context);
-
- /**
- * Repack the atlas if needed, by moving used rectangles into a new atlas surface.
- * The goal of repacking is to fix a fragmented atlas.
- */
- void repackIfNeeded(GrContext* context);
-
- /**
- * Returns true if atlas is fragmented and repack is needed.
- */
- bool isFragmented();
-
- /**
- * "requestNewEntry" is called by VectorDrawable to allocate a new rectangle area from the atlas
- * or create a standalone surface if atlas is full.
- * On success it returns a non-negative unique id, which can be used later with "getEntry" and
- * "releaseEntry".
- */
- AtlasEntry requestNewEntry(int width, int height, GrContext* context);
-
- /**
- * "getEntry" extracts coordinates and surface of a previously created rectangle.
- * "atlasKey" is an unique id created by "requestNewEntry". Passing a non-existing "atlasKey" is
- * causing an undefined behaviour.
- * On success it returns a rectangle Id -> may be same or different from "atlasKey" if
- * implementation decides to move the record internally.
- */
- AtlasEntry getEntry(AtlasKey atlasKey);
-
- /**
- * "releaseEntry" is invoked when a VectorDrawable is deleted. Passing a non-existing "atlasKey"
- * is causing an undefined behaviour. This is the only function in the class that can be
- * invoked from any thread. It will marshal internally to render thread if needed.
- */
- void releaseEntry(AtlasKey atlasKey);
-
- void setStorageMode(StorageMode mode);
-
- /**
- * "delayedReleaseEntries" is indirectly invoked by "releaseEntry", when "releaseEntry" is
- * invoked from a non render thread.
- */
- void delayedReleaseEntries();
-
-private:
- struct CacheEntry {
- CacheEntry(const SkRect& newVDrect, const SkRect& newRect,
- const sk_sp<SkSurface>& newSurface)
- : VDrect(newVDrect), rect(newRect), surface(newSurface) {}
-
- /**
- * size and position of VectorDrawable into the atlas or in "this.surface"
- */
- SkRect VDrect;
-
- /**
- * rect allocated in atlas surface or "this.surface". It may be bigger than "VDrect"
- */
- SkRect rect;
-
- /**
- * this surface is used if atlas is full or VD is too big
- */
- sk_sp<SkSurface> surface;
-
- /**
- * iterator is used to delete self with a constant complexity (without traversing the list)
- */
- std::list<CacheEntry>::iterator eraseIt;
- };
-
- /**
- * atlas surface shared by all VDs
- */
- sk_sp<SkSurface> mSurface;
-
- std::unique_ptr<GrRectanizer> mRectanizer;
- const int mWidth;
- const int mHeight;
-
- /**
- * "mRects" keeps records only for rectangles used by VDs. List has nice properties: constant
- * complexity to insert and erase and references are not invalidated by insert/erase.
- */
- std::list<CacheEntry> mRects;
-
- /**
- * Rectangles freed by "releaseEntry" are removed from "mRects" and added to "mFreeRects".
- * "mFreeRects" is using for an index the rectangle area. There could be more than one free
- * rectangle with the same area, which is the reason to use "multimap" instead of "map".
- */
- std::multimap<size_t, SkRect> mFreeRects;
-
- /**
- * area in atlas used by VectorDrawables (area in standalone surface not counted)
- */
- int mPixelUsedByVDs = 0;
-
- /**
- * area allocated in mRectanizer
- */
- int mPixelAllocated = 0;
-
- /**
- * Consecutive times we had to allocate standalone surfaces, because atlas was full.
- */
- int mConsecutiveFailures = 0;
-
- /**
- * mStorageMode allows using a shared surface to store small vector drawables.
- * Using a shared surface can boost the performance by allowing GL ops to be batched, but may
- * consume more memory.
- */
- StorageMode mStorageMode;
-
- /**
- * mKeysForRelease is used by releaseEntry implementation to pass atlas keys from an arbitrary
- * calling thread to the render thread.
- */
- std::vector<AtlasKey> mKeysForRelease;
-
- /**
- * A lock used to protect access to mKeysForRelease.
- */
- Mutex mReleaseKeyLock;
-
- sk_sp<SkSurface> createSurface(int width, int height, GrContext* context);
-
- inline bool fitInAtlas(int width, int height) {
- return 2 * width < mWidth && 2 * height < mHeight;
- }
-
- void repack(GrContext* context);
-
- static bool compareCacheEntry(const CacheEntry& first, const CacheEntry& second);
-};
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 8eb81533fda8..b366a80f918a 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -56,10 +56,6 @@ CacheManager::CacheManager(const DisplayInfo& display)
, mBackgroundCpuFontCacheBytes(mMaxCpuFontCacheBytes * BACKGROUND_RETENTION_PERCENTAGE) {
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
-
- mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(
- mMaxSurfaceArea / 2,
- skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface);
}
void CacheManager::reset(sk_sp<GrContext> context) {
@@ -76,9 +72,6 @@ void CacheManager::reset(sk_sp<GrContext> context) {
void CacheManager::destroy() {
// cleanup any caches here as the GrContext is about to go away...
mGrContext.reset(nullptr);
- mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(
- mMaxSurfaceArea / 2,
- skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface);
}
class CommonPoolExecutor : public SkExecutor {
@@ -109,7 +102,6 @@ void CacheManager::trimMemory(TrimMemoryMode mode) {
switch (mode) {
case TrimMemoryMode::Complete:
- mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea / 2);
mGrContext->freeGpuResources();
SkGraphics::PurgeAllCaches();
break;
@@ -138,16 +130,6 @@ void CacheManager::trimStaleResources() {
mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
}
-sp<skiapipeline::VectorDrawableAtlas> CacheManager::acquireVectorDrawableAtlas() {
- LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() == nullptr);
- LOG_ALWAYS_FATAL_IF(mGrContext == nullptr);
-
- /**
- * TODO: define memory conditions where we clear the cache (e.g. surface->reset())
- */
- return mVectorDrawableAtlas;
-}
-
void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
if (!mGrContext) {
log.appendFormat("No valid cache instance.\n");
@@ -176,8 +158,6 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState)
log.appendFormat("Other Caches:\n");
log.appendFormat(" Current / Maximum\n");
- log.appendFormat(" VectorDrawableAtlas %6.2f kB / %6.2f KB (entries = %zu)\n", 0.0f, 0.0f,
- (size_t)0);
if (renderState) {
if (renderState->mActiveLayers.size() > 0) {
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index d7977cc4566d..857710babf0c 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -25,8 +25,6 @@
#include <utils/String8.h>
#include <vector>
-#include "pipeline/skia/VectorDrawableAtlas.h"
-
namespace android {
class Surface;
@@ -51,8 +49,6 @@ public:
void trimStaleResources();
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
- sp<skiapipeline::VectorDrawableAtlas> acquireVectorDrawableAtlas();
-
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
@@ -77,13 +73,6 @@ private:
const size_t mMaxGpuFontAtlasBytes;
const size_t mMaxCpuFontCacheBytes;
const size_t mBackgroundCpuFontCacheBytes;
-
- struct PipelineProps {
- const void* pipelineKey = nullptr;
- size_t surfaceArea = 0;
- };
-
- sp<skiapipeline::VectorDrawableAtlas> mVectorDrawableAtlas;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 30cc007d454b..b41bdf6a174f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -306,7 +306,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
info.out.canDrawThisFrame = true;
mAnimationContext->startFrame(info.mode);
- mRenderPipeline->onPrepareTree();
for (const sp<RenderNode>& node : mRenderNodes) {
// Only the primary target node will be drawn full - all other nodes would get drawn in
// real time mode. In case of a window, the primary node is the window content and the other
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 8a76d6b3fc7a..6e3e43af8c6f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -242,7 +242,8 @@ private:
nsecs_t queueDuration;
};
- RingBuffer<SwapHistory, 3> mSwapHistory;
+ // Need at least 4 because we do quad buffer. Add a 5th for good measure.
+ RingBuffer<SwapHistory, 5> mSwapHistory;
int64_t mFrameNumber = -1;
// last vsync for a dropped frame due to stuffed queue
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 3b81014c05e2..ef0aa98d4220 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -80,7 +80,6 @@ public:
virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;
virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0;
virtual void unpinImages() = 0;
- virtual void onPrepareTree() = 0;
virtual SkColorType getSurfaceColorType() const = 0;
virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0;
virtual GrSurfaceOrigin getSurfaceOrigin() = 0;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 40fbdffaf90a..4f7ad7b69f57 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -24,7 +24,6 @@
#include "Readback.h"
#include "Rect.h"
#include "WebViewFunctorManager.h"
-#include "pipeline/skia/VectorDrawableAtlas.h"
#include "renderthread/CanvasContext.h"
#include "renderthread/RenderTask.h"
#include "renderthread/RenderThread.h"
@@ -365,27 +364,6 @@ void RenderProxy::disableVsync() {
Properties::disableVsync = true;
}
-void RenderProxy::repackVectorDrawableAtlas() {
- RenderThread& thread = RenderThread::getInstance();
- thread.queue().post([&thread]() {
- // The context may be null if trimMemory executed, but then the atlas was deleted too.
- if (thread.getGrContext() != nullptr) {
- thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded(
- thread.getGrContext());
- }
- });
-}
-
-void RenderProxy::releaseVDAtlasEntries() {
- RenderThread& thread = RenderThread::getInstance();
- thread.queue().post([&thread]() {
- // The context may be null if trimMemory executed, but then the atlas was deleted too.
- if (thread.getGrContext() != nullptr) {
- thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries();
- }
- });
-}
-
void RenderProxy::preload() {
// Create RenderThread object and start the thread. Then preload Vulkan/EGL driver.
auto& thread = RenderThread::getInstance();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index c3eb6ede9bf2..e6fe1d4864da 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -150,10 +150,6 @@ public:
ANDROID_API static void preload();
- static void repackVectorDrawableAtlas();
-
- static void releaseVDAtlasEntries();
-
private:
RenderThread& mRenderThread;
CanvasContext* mContext;
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 958baa78deab..307d13606cb8 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -61,39 +61,6 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) {
- auto redNode = TestUtils::createSkiaNode(
- 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
- redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
- });
-
- LayerUpdateQueue layerUpdateQueue;
- SkRect dirty = SkRectMakeLargest();
- std::vector<sp<RenderNode>> renderNodes;
- renderNodes.push_back(redNode);
- bool opaque = true;
- android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1);
- auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
- {
- // add a pointer to a deleted vector drawable object in the pipeline
- sp<VectorDrawableRoot> dirtyVD(new VectorDrawableRoot(new VectorDrawable::Group()));
- dirtyVD->mutateProperties()->setScaledSize(5, 5);
- pipeline->getVectorDrawables()->push_back(dirtyVD.get());
- }
-
- // pipeline should clean list of dirty vector drawables before prepare tree
- pipeline->onPrepareTree();
-
- auto surface = SkSurface::MakeRasterN32Premul(1, 1);
- surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
- ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
-
- // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer
- pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
- SkMatrix::I());
- ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
-}
-
RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
auto halfGreenNode = TestUtils::createSkiaNode(
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
diff --git a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp b/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp
deleted file mode 100644
index 0c95fdd42851..000000000000
--- a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-
-#include <GrRectanizer.h>
-#include "pipeline/skia/VectorDrawableAtlas.h"
-#include "tests/common/TestUtils.h"
-
-using namespace android;
-using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-using namespace android::uirenderer::skiapipeline;
-
-RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, addGetRemove) {
- VectorDrawableAtlas atlas(100 * 100);
- atlas.prepareForDraw(renderThread.getGrContext());
- // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects)
- const int MAX_RECTS = 150;
- AtlasEntry VDRects[MAX_RECTS];
-
- sk_sp<SkSurface> atlasSurface;
-
- // check we are able to allocate new rects
- // check that rects in the atlas do not intersect
- for (uint32_t i = 0; i < MAX_RECTS; i++) {
- VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
- if (0 == i) {
- atlasSurface = VDRects[0].surface;
- }
- ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
- ASSERT_TRUE(VDRects[i].surface.get() != nullptr);
- ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10);
-
- // nothing in the atlas should intersect
- if (atlasSurface.get() == VDRects[i].surface.get()) {
- for (uint32_t j = 0; j < i; j++) {
- if (atlasSurface.get() == VDRects[j].surface.get()) {
- ASSERT_FALSE(VDRects[i].rect.intersect(VDRects[j].rect));
- }
- }
- }
- }
-
- // first 1/3 rects should all be in the same surface
- for (uint32_t i = 1; i < MAX_RECTS / 3; i++) {
- ASSERT_NE(VDRects[i].key, VDRects[0].key);
- ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get());
- }
-
- // first rect is using atlas and last is a standalone surface
- ASSERT_NE(VDRects[0].surface.get(), VDRects[MAX_RECTS - 1].surface.get());
-
- // check getEntry returns the same surfaces that we had created
- for (uint32_t i = 0; i < MAX_RECTS; i++) {
- auto VDRect = atlas.getEntry(VDRects[i].key);
- ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY);
- ASSERT_EQ(VDRects[i].key, VDRect.key);
- ASSERT_EQ(VDRects[i].surface.get(), VDRect.surface.get());
- ASSERT_EQ(VDRects[i].rect, VDRect.rect);
- atlas.releaseEntry(VDRect.key);
- }
-
- // check that any new rects will be allocated in the atlas, even that rectanizer is full.
- // rects in the atlas should not intersect.
- for (uint32_t i = 0; i < MAX_RECTS / 3; i++) {
- VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
- ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
- ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get());
- ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10);
- for (uint32_t j = 0; j < i; j++) {
- ASSERT_FALSE(VDRects[i].rect.intersect(VDRects[j].rect));
- }
- }
-}
-
-RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, disallowSharedSurface) {
- VectorDrawableAtlas atlas(100 * 100);
- // don't allow to use a shared surface
- atlas.setStorageMode(VectorDrawableAtlas::StorageMode::disallowSharedSurface);
- atlas.prepareForDraw(renderThread.getGrContext());
- // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects)
- const int MAX_RECTS = 150;
- AtlasEntry VDRects[MAX_RECTS];
-
- // check we are able to allocate new rects
- // check that rects in the atlas use unique surfaces
- for (uint32_t i = 0; i < MAX_RECTS; i++) {
- VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
- ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
- ASSERT_TRUE(VDRects[i].surface.get() != nullptr);
- ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10);
-
- // nothing in the atlas should use the same surface
- for (uint32_t j = 0; j < i; j++) {
- ASSERT_NE(VDRects[i].surface.get(), VDRects[j].surface.get());
- }
- }
-}
-
-RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, repack) {
- VectorDrawableAtlas atlas(100 * 100);
- ASSERT_FALSE(atlas.isFragmented());
- atlas.prepareForDraw(renderThread.getGrContext());
- ASSERT_FALSE(atlas.isFragmented());
- // create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects)
- const int MAX_RECTS = 150;
- AtlasEntry VDRects[MAX_RECTS];
-
- sk_sp<SkSurface> atlasSurface;
-
- // fill the atlas with check we are able to allocate new rects
- for (uint32_t i = 0; i < MAX_RECTS; i++) {
- VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
- if (0 == i) {
- atlasSurface = VDRects[0].surface;
- }
- ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
- }
-
- ASSERT_FALSE(atlas.isFragmented());
-
- // first 1/3 rects should all be in the same surface
- for (uint32_t i = 1; i < MAX_RECTS / 3; i++) {
- ASSERT_NE(VDRects[i].key, VDRects[0].key);
- ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get());
- }
-
- // release all entries
- for (uint32_t i = 0; i < MAX_RECTS; i++) {
- auto VDRect = atlas.getEntry(VDRects[i].key);
- ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY);
- atlas.releaseEntry(VDRect.key);
- }
-
- ASSERT_FALSE(atlas.isFragmented());
-
- // allocate 4x4 rects, which will fragment the atlas badly, because each entry occupies a 10x10
- // area
- for (uint32_t i = 0; i < 4 * MAX_RECTS; i++) {
- AtlasEntry entry = atlas.requestNewEntry(4, 4, renderThread.getGrContext());
- ASSERT_TRUE(entry.key != INVALID_ATLAS_KEY);
- }
-
- ASSERT_TRUE(atlas.isFragmented());
-
- atlas.repackIfNeeded(renderThread.getGrContext());
-
- ASSERT_FALSE(atlas.isFragmented());
-} \ No newline at end of file
diff --git a/location/TEST_MAPPING b/location/TEST_MAPPING
new file mode 100644
index 000000000000..2f386271dcb7
--- /dev/null
+++ b/location/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsLocationCoarseTestCases"
+ },
+ {
+ "name": "CtsLocationNoneTestCases"
+ }
+ ]
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 91f3a20fc11d..79bec920c10e 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -42,20 +42,23 @@ import com.android.internal.location.ProviderProperties;
*/
interface ILocationManager
{
- Location getLastLocation(in LocationRequest request, String packageName);
+ Location getLastLocation(in LocationRequest request, String packageName, String featureId);
boolean getCurrentLocation(in LocationRequest request,
in ICancellationSignal cancellationSignal, in ILocationListener listener,
- String packageName, String listenerIdentifier);
+ String packageName, String featureId, String listenerIdentifier);
void requestLocationUpdates(in LocationRequest request, in ILocationListener listener,
- in PendingIntent intent, String packageName, String listenerIdentifier);
+ in PendingIntent intent, String packageName, String featureId,
+ String listenerIdentifier);
void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName);
void requestGeofence(in LocationRequest request, in Geofence geofence,
- in PendingIntent intent, String packageName, String listenerIdentifier);
+ in PendingIntent intent, String packageName, String featureId,
+ String listenerIdentifier);
void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName);
- boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName);
+ boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName,
+ String featureId);
void unregisterGnssStatusCallback(IGnssStatusListener callback);
boolean geocoderIsPresent();
@@ -69,14 +72,14 @@ interface ILocationManager
boolean sendNiResponse(int notifId, int userResponse);
boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener,
- String packageName, String listenerIdentifier);
+ String packageName, String featureId, String listenerIdentifier);
void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
in String packageName);
long getGnssCapabilities(in String packageName);
void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener,
- String packageName, String listenerIdentifier);
+ String packageName, String featureId, String listenerIdentifier);
void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
int getGnssYearOfHardware();
@@ -84,7 +87,7 @@ interface ILocationManager
int getGnssBatchSize(String packageName);
boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName,
- String listenerIdentifier);
+ String featureId, String listenerIdentifier);
void removeGnssBatchingCallback();
boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName);
void flushGnssBatch(String packageName);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 9e17e9541e5c..d3db9d83b3e7 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -515,7 +515,8 @@ public class LocationManager {
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public Location getLastLocation() {
try {
- return mService.getLastLocation(null, mContext.getPackageName());
+ return mService.getLastLocation(null, mContext.getPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -547,7 +548,8 @@ public class LocationManager {
provider, 0, 0, true);
try {
- return mService.getLastLocation(request, mContext.getPackageName());
+ return mService.getLastLocation(request, mContext.getPackageName(),
+ mContext.getFeatureId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -641,7 +643,7 @@ public class LocationManager {
try {
if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
- listenerTransport, mContext.getPackageName(),
+ listenerTransport, mContext.getPackageName(), mContext.getFeatureId(),
getListenerIdentifier(consumer))) {
listenerTransport.register(mContext.getSystemService(AlarmManager.class),
remoteCancellationSignal);
@@ -1085,7 +1087,8 @@ public class LocationManager {
boolean registered = false;
try {
mService.requestLocationUpdates(locationRequest, transport, null,
- mContext.getPackageName(), getListenerIdentifier(listener));
+ mContext.getPackageName(), mContext.getFeatureId(),
+ getListenerIdentifier(listener));
registered = true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1130,7 +1133,8 @@ public class LocationManager {
try {
mService.requestLocationUpdates(locationRequest, null, pendingIntent,
- mContext.getPackageName(), getListenerIdentifier(pendingIntent));
+ mContext.getPackageName(), mContext.getFeatureId(),
+ getListenerIdentifier(pendingIntent));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1594,7 +1598,7 @@ public class LocationManager {
LocationRequest request = new LocationRequest().setExpireIn(expiration);
try {
mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
- getListenerIdentifier(intent));
+ mContext.getFeatureId(), getListenerIdentifier(intent));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1672,7 +1676,7 @@ public class LocationManager {
try {
mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
- getListenerIdentifier(intent));
+ mContext.getFeatureId(), getListenerIdentifier(intent));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2179,7 +2183,7 @@ public class LocationManager {
public void removeGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {}
/**
- * Registers a GNSS Navigation Message callback.
+ * Registers a GNSS Navigation Message callback which will run on a binder thread.
*
* @param callback a {@link GnssNavigationMessage.Callback} object to register.
* @return {@code true} if the callback was added successfully, {@code false} otherwise.
@@ -2190,7 +2194,7 @@ public class LocationManager {
@Deprecated
public boolean registerGnssNavigationMessageCallback(
@NonNull GnssNavigationMessage.Callback callback) {
- return registerGnssNavigationMessageCallback(callback, null);
+ return registerGnssNavigationMessageCallback(Runnable::run, callback);
}
/**
@@ -2748,7 +2752,7 @@ public class LocationManager {
mListenerTransport = new GnssStatusListener();
return mService.registerGnssStatusCallback(mListenerTransport,
- mContext.getPackageName());
+ mContext.getPackageName(), mContext.getFeatureId());
}
@Override
@@ -2808,7 +2812,8 @@ public class LocationManager {
mListenerTransport = new GnssMeasurementsListener();
return mService.addGnssMeasurementsListener(mListenerTransport,
- mContext.getPackageName(), "gnss measurement callback");
+ mContext.getPackageName(), mContext.getFeatureId(),
+ "gnss measurement callback");
}
@Override
@@ -2844,7 +2849,8 @@ public class LocationManager {
mListenerTransport = new GnssNavigationMessageListener();
return mService.addGnssNavigationMessageListener(mListenerTransport,
- mContext.getPackageName(), "gnss navigation callback");
+ mContext.getPackageName(), mContext.getFeatureId(),
+ "gnss navigation callback");
}
@Override
@@ -2880,7 +2886,7 @@ public class LocationManager {
mListenerTransport = new BatchedLocationCallback();
return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName(),
- "batched location callback");
+ mContext.getFeatureId(), "batched location callback");
}
@Override
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 0902acf176d4..0f38f7f057fa 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -30,6 +30,8 @@ import android.os.SystemClock;
import android.os.WorkSource;
import android.util.TimeUtils;
+import com.android.internal.util.Preconditions;
+
/**
* A data object that contains quality of service parameters for requests
@@ -195,6 +197,8 @@ public final class LocationRequest implements Parcelable {
@NonNull
public static LocationRequest createFromDeprecatedProvider(
@NonNull String provider, long minTime, float minDistance, boolean singleShot) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
if (minTime < 0) minTime = 0;
if (minDistance < 0) minDistance = 0;
@@ -222,6 +226,8 @@ public final class LocationRequest implements Parcelable {
@NonNull
public static LocationRequest createFromDeprecatedCriteria(
@NonNull Criteria criteria, long minTime, float minDistance, boolean singleShot) {
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+
if (minTime < 0) minTime = 0;
if (minDistance < 0) minDistance = 0;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 53bc65d711b0..bb731a8189f9 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -16,6 +16,7 @@
package android.media;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -26,6 +27,8 @@ import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Map;
@@ -431,6 +434,50 @@ public class AudioSystem
public static final int DEAD_OBJECT = -6;
public static final int WOULD_BLOCK = -7;
+ /** @hide */
+ @IntDef({
+ SUCCESS,
+ ERROR,
+ BAD_VALUE,
+ INVALID_OPERATION,
+ PERMISSION_DENIED,
+ NO_INIT,
+ DEAD_OBJECT,
+ WOULD_BLOCK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AudioSystemError {}
+
+ /**
+ * Convert an int error value to its String value for readability.
+ * Accepted error values are the java AudioSystem errors, matching android_media_AudioErrors.h,
+ * which map onto the native status_t type.
+ * @param error one of the java AudioSystem errors
+ * @return a human-readable string
+ */
+ public static String audioSystemErrorToString(@AudioSystemError int error) {
+ switch(error) {
+ case SUCCESS:
+ return "SUCCESS";
+ case ERROR:
+ return "ERROR";
+ case BAD_VALUE:
+ return "BAD_VALUE";
+ case INVALID_OPERATION:
+ return "INVALID_OPERATION";
+ case PERMISSION_DENIED:
+ return "PERMISSION_DENIED";
+ case NO_INIT:
+ return "NO_INIT";
+ case DEAD_OBJECT:
+ return "DEAD_OBJECT";
+ case WOULD_BLOCK:
+ return "WOULD_BLOCK";
+ default:
+ return ("unknown error:" + error);
+ }
+ }
+
/*
* AudioPolicyService methods
*/
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 7906fa34d844..7d107ddbe978 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -89,6 +89,10 @@ import java.util.Vector;
* of audio/video files and streams. An example on how to use the methods in
* this class can be found in {@link android.widget.VideoView}.
*
+ * <p>MediaPlayer is not thread-safe. Creation of and all access to player instances
+ * should be on the same thread. If registering <a href="#Callbacks">callbacks</a>,
+ * the thread must have a Looper.
+ *
* <p>Topics covered here are:
* <ol>
* <li><a href="#StateDiagram">State Diagram</a>
@@ -305,7 +309,7 @@ import java.util.Vector;
* </li>
* <li>When the playback reaches the end of stream, the playback completes.
* <ul>
- * <li>If the looping mode was being set to <var>true</var>with
+ * <li>If the looping mode was being set to <var>true</var> with
* {@link #setLooping(boolean)}, the MediaPlayer object shall remain in
* the <em>Started</em> state.</li>
* <li>If the looping mode was set to <var>false
@@ -554,13 +558,13 @@ import java.util.Vector;
* possible runtime errors during playback or streaming. Registration for
* these events is done by properly setting the appropriate listeners (via calls
* to
- * {@link #setOnPreparedListener(OnPreparedListener)}setOnPreparedListener,
- * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}setOnVideoSizeChangedListener,
- * {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}setOnSeekCompleteListener,
- * {@link #setOnCompletionListener(OnCompletionListener)}setOnCompletionListener,
- * {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}setOnBufferingUpdateListener,
- * {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener,
- * {@link #setOnErrorListener(OnErrorListener)}setOnErrorListener, etc).
+ * {@link #setOnPreparedListener(OnPreparedListener) setOnPreparedListener},
+ * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener) setOnVideoSizeChangedListener},
+ * {@link #setOnSeekCompleteListener(OnSeekCompleteListener) setOnSeekCompleteListener},
+ * {@link #setOnCompletionListener(OnCompletionListener) setOnCompletionListener},
+ * {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener) setOnBufferingUpdateListener},
+ * {@link #setOnInfoListener(OnInfoListener) setOnInfoListener},
+ * {@link #setOnErrorListener(OnErrorListener) setOnErrorListener}, etc).
* In order to receive the respective callback
* associated with these listeners, applications are required to create
* MediaPlayer objects on a thread with its own Looper running (main UI
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 39474e13a2d0..01f12500b77b 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -853,6 +853,10 @@ public class AudioPolicy {
Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
}
}
+
+ public void notifyUnregistration() {
+ setRegistration(null);
+ }
};
//==================================================
diff --git a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
index 107e7cd59ca2..1d581516e7b1 100644
--- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
+++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
@@ -34,4 +34,8 @@ oneway interface IAudioPolicyCallback {
// callback for volume events
void notifyVolumeAdjust(int adjustment);
+
+ // callback for unregistration (e.g. if policy couldn't automatically be re-registered after
+ // an audioserver crash)
+ void notifyUnregistration();
}
diff --git a/media/java/android/media/midi/MidiDeviceInfo.aidl b/media/java/android/media/midi/MidiDeviceInfo.aidl
index 5b2ac9b64663..a2482040526d 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.aidl
+++ b/media/java/android/media/midi/MidiDeviceInfo.aidl
@@ -16,4 +16,4 @@
package android.media.midi;
-parcelable MidiDeviceInfo cpp_header "media/MidiDeviceInfo.h";
+parcelable MidiDeviceInfo cpp_header "MidiDeviceInfo.h";
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
new file mode 100644
index 000000000000..0228dc94fb25
--- /dev/null
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Tuner is used to interact with tuner devices.
+ *
+ * @hide
+ */
+public final class Tuner implements AutoCloseable {
+ private static final String TAG = "MediaTvTuner";
+ private static final boolean DEBUG = false;
+
+ static {
+ System.loadLibrary("media_tv_tuner");
+ nativeInit();
+ }
+
+ public Tuner() {
+ nativeSetup();
+ }
+
+ private long mNativeContext; // used by native jMediaTuner
+
+ @Override
+ public void close() {}
+
+ /**
+ * Native Initialization.
+ */
+ private static native void nativeInit();
+
+ /**
+ * Native setup.
+ */
+ private native void nativeSetup();
+}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index b4edabfb2e8b..a596d89bdcce 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -123,6 +123,30 @@ cc_library_shared {
],
}
+cc_library_shared {
+ name: "libmedia_tv_tuner",
+ srcs: [
+ "android_media_tv_Tuner.cpp",
+ ],
+
+ shared_libs: [
+ "android.hardware.tv.tuner@1.0",
+ "libandroid_runtime",
+ "liblog",
+ "libutils",
+ ],
+
+ export_include_dirs: ["."],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-error=deprecated-declarations",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
+
subdirs = [
"audioeffect",
"soundpool",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
new file mode 100644
index 000000000000..d499eee5a9da
--- /dev/null
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TvTuner-JNI"
+#include <utils/Log.h>
+
+#include "android_media_tv_Tuner.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+using ::android::hardware::tv::tuner::V1_0::ITuner;
+
+struct fields_t {
+ jfieldID context;
+};
+
+static fields_t gFields;
+
+namespace android {
+
+sp<ITuner> JTuner::mTuner;
+
+JTuner::JTuner(JNIEnv *env, jobject thiz)
+ : mClass(NULL) {
+ jclass clazz = env->GetObjectClass(thiz);
+ CHECK(clazz != NULL);
+
+ mClass = (jclass)env->NewGlobalRef(clazz);
+ mObject = env->NewWeakGlobalRef(thiz);
+ if (mTuner == NULL) {
+ mTuner = getTunerService();
+ }
+}
+
+JTuner::~JTuner() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ env->DeleteGlobalRef(mClass);
+ mTuner = NULL;
+ mClass = NULL;
+ mObject = NULL;
+}
+
+sp<ITuner> JTuner::getTunerService() {
+ if (mTuner == nullptr) {
+ mTuner = ITuner::getService();
+
+ if (mTuner == nullptr) {
+ ALOGW("Failed to get tuner service.");
+ }
+ }
+ return mTuner;
+}
+
+} // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static sp<JTuner> setTuner(JNIEnv *env, jobject thiz, const sp<JTuner> &tuner) {
+ sp<JTuner> old = (JTuner *)env->GetLongField(thiz, gFields.context);
+
+ if (tuner != NULL) {
+ tuner->incStrong(thiz);
+ }
+ if (old != NULL) {
+ old->decStrong(thiz);
+ }
+ env->SetLongField(thiz, gFields.context, (jlong)tuner.get());
+
+ return old;
+}
+
+static sp<JTuner> getTuner(JNIEnv *env, jobject thiz) {
+ return (JTuner *)env->GetLongField(thiz, gFields.context);
+}
+
+static void android_media_tv_Tuner_native_init(JNIEnv *env) {
+ jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
+ CHECK(clazz != NULL);
+
+ gFields.context = env->GetFieldID(clazz, "mNativeContext", "J");
+ CHECK(gFields.context != NULL);
+}
+
+static void android_media_tv_Tuner_native_setup(JNIEnv *env, jobject thiz) {
+ sp<JTuner> tuner = new JTuner(env, thiz);
+ setTuner(env,thiz, tuner);
+}
+
+static const JNINativeMethod gMethods[] = {
+ { "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
+ { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
+};
+
+static int register_android_media_tv_Tuner(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(
+ env, "android/media/tv/tuner/Tuner", gMethods, NELEM(gMethods));
+}
+
+jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ ALOGE("ERROR: GetEnv failed\n");
+ return result;
+ }
+ assert(env != NULL);
+
+ if (register_android_media_tv_Tuner(env) != JNI_OK) {
+ ALOGE("ERROR: Tuner native registration failed\n");
+ return result;
+ }
+ return JNI_VERSION_1_4;
+}
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
new file mode 100644
index 000000000000..e7d5924a7dbe
--- /dev/null
+++ b/media/jni/android_media_tv_Tuner.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_MEDIA_TV_TUNER_H_
+#define _ANDROID_MEDIA_TV_TUNER_H_
+
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <utils/RefBase.h>
+
+#include "jni.h"
+
+using ::android::hardware::tv::tuner::V1_0::ITuner;
+
+namespace android {
+
+struct JTuner : public RefBase {
+ JTuner(JNIEnv *env, jobject thiz);
+ sp<ITuner> getTunerService();
+protected:
+ virtual ~JTuner();
+
+private:
+ jclass mClass;
+ jweak mObject;
+ static sp<ITuner> mTuner;
+};
+
+} // namespace android
+
+#endif // _ANDROID_MEDIA_TV_TUNER_H_
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 09c546ab0065..41ab6703a579 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -6,6 +6,7 @@ cc_library_shared {
"android_media_SourceDefaultEffect.cpp",
"android_media_StreamDefaultEffect.cpp",
"android_media_Visualizer.cpp",
+ "Visualizer.cpp",
],
shared_libs: [
@@ -14,10 +15,12 @@ cc_library_shared {
"libutils",
"libandroid_runtime",
"libnativehelper",
- "libmedia",
"libaudioclient",
+ "libaudioutils",
],
+ version_script: "exports.lds",
+
cflags: [
"-Wall",
"-Werror",
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
new file mode 100644
index 000000000000..83f3b6ed6217
--- /dev/null
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -0,0 +1,446 @@
+/*
+**
+** Copyright 2010, 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.
+*/
+
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Visualizer"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <limits.h>
+
+#include <audio_utils/fixedfft.h>
+#include <utils/Thread.h>
+
+#include "Visualizer.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+Visualizer::Visualizer (const String16& opPackageName,
+ int32_t priority,
+ effect_callback_t cbf,
+ void* user,
+ audio_session_t sessionId)
+ : AudioEffect(SL_IID_VISUALIZATION, opPackageName, NULL, priority, cbf, user, sessionId),
+ mCaptureRate(CAPTURE_RATE_DEF),
+ mCaptureSize(CAPTURE_SIZE_DEF),
+ mSampleRate(44100000),
+ mScalingMode(VISUALIZER_SCALING_MODE_NORMALIZED),
+ mMeasurementMode(MEASUREMENT_MODE_NONE),
+ mCaptureCallBack(NULL),
+ mCaptureCbkUser(NULL)
+{
+ initCaptureSize();
+}
+
+Visualizer::~Visualizer()
+{
+ ALOGV("Visualizer::~Visualizer()");
+ setEnabled(false);
+ setCaptureCallBack(NULL, NULL, 0, 0);
+}
+
+void Visualizer::release()
+{
+ ALOGV("Visualizer::release()");
+ setEnabled(false);
+ Mutex::Autolock _l(mCaptureLock);
+
+ mCaptureThread.clear();
+ mCaptureCallBack = NULL;
+ mCaptureCbkUser = NULL;
+ mCaptureFlags = 0;
+ mCaptureRate = 0;
+}
+
+status_t Visualizer::setEnabled(bool enabled)
+{
+ Mutex::Autolock _l(mCaptureLock);
+
+ sp<CaptureThread> t = mCaptureThread;
+ if (t != 0) {
+ if (enabled) {
+ if (t->exitPending()) {
+ mCaptureLock.unlock();
+ if (t->requestExitAndWait() == WOULD_BLOCK) {
+ mCaptureLock.lock();
+ ALOGE("Visualizer::enable() called from thread");
+ return INVALID_OPERATION;
+ }
+ mCaptureLock.lock();
+ }
+ }
+ t->mLock.lock();
+ }
+
+ status_t status = AudioEffect::setEnabled(enabled);
+
+ if (t != 0) {
+ if (enabled && status == NO_ERROR) {
+ t->run("Visualizer");
+ } else {
+ t->requestExit();
+ }
+ }
+
+ if (t != 0) {
+ t->mLock.unlock();
+ }
+
+ return status;
+}
+
+status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags,
+ uint32_t rate)
+{
+ if (rate > CAPTURE_RATE_MAX) {
+ return BAD_VALUE;
+ }
+ Mutex::Autolock _l(mCaptureLock);
+
+ if (mEnabled) {
+ return INVALID_OPERATION;
+ }
+
+ if (mCaptureThread != 0) {
+ mCaptureLock.unlock();
+ mCaptureThread->requestExitAndWait();
+ mCaptureLock.lock();
+ }
+
+ mCaptureThread.clear();
+ mCaptureCallBack = cbk;
+ mCaptureCbkUser = user;
+ mCaptureFlags = flags;
+ mCaptureRate = rate;
+
+ if (cbk != NULL) {
+ mCaptureThread = new CaptureThread(this, rate, ((flags & CAPTURE_CALL_JAVA) != 0));
+ }
+ ALOGV("setCaptureCallBack() rate: %d thread %p flags 0x%08x",
+ rate, mCaptureThread.get(), mCaptureFlags);
+ return NO_ERROR;
+}
+
+status_t Visualizer::setCaptureSize(uint32_t size)
+{
+ if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
+ size < VISUALIZER_CAPTURE_SIZE_MIN ||
+ popcount(size) != 1) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mCaptureLock);
+ if (mEnabled) {
+ return INVALID_OPERATION;
+ }
+
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t *p = (effect_param_t *)buf32;
+
+ p->psize = sizeof(uint32_t);
+ p->vsize = sizeof(uint32_t);
+ *(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
+ *((int32_t *)p->data + 1)= size;
+ status_t status = setParameter(p);
+
+ ALOGV("setCaptureSize size %d status %d p->status %d", size, status, p->status);
+
+ if (status == NO_ERROR) {
+ status = p->status;
+ if (status == NO_ERROR) {
+ mCaptureSize = size;
+ }
+ }
+
+ return status;
+}
+
+status_t Visualizer::setScalingMode(uint32_t mode) {
+ if ((mode != VISUALIZER_SCALING_MODE_NORMALIZED)
+ && (mode != VISUALIZER_SCALING_MODE_AS_PLAYED)) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mCaptureLock);
+
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t *p = (effect_param_t *)buf32;
+
+ p->psize = sizeof(uint32_t);
+ p->vsize = sizeof(uint32_t);
+ *(int32_t *)p->data = VISUALIZER_PARAM_SCALING_MODE;
+ *((int32_t *)p->data + 1)= mode;
+ status_t status = setParameter(p);
+
+ ALOGV("setScalingMode mode %d status %d p->status %d", mode, status, p->status);
+
+ if (status == NO_ERROR) {
+ status = p->status;
+ if (status == NO_ERROR) {
+ mScalingMode = mode;
+ }
+ }
+
+ return status;
+}
+
+status_t Visualizer::setMeasurementMode(uint32_t mode) {
+ if ((mode != MEASUREMENT_MODE_NONE)
+ //Note: needs to be handled as a mask when more measurement modes are added
+ && ((mode & MEASUREMENT_MODE_PEAK_RMS) != mode)) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mCaptureLock);
+
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t *p = (effect_param_t *)buf32;
+
+ p->psize = sizeof(uint32_t);
+ p->vsize = sizeof(uint32_t);
+ *(int32_t *)p->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
+ *((int32_t *)p->data + 1)= mode;
+ status_t status = setParameter(p);
+
+ ALOGV("setMeasurementMode mode %d status %d p->status %d", mode, status, p->status);
+
+ if (status == NO_ERROR) {
+ status = p->status;
+ if (status == NO_ERROR) {
+ mMeasurementMode = mode;
+ }
+ }
+ return status;
+}
+
+status_t Visualizer::getIntMeasurements(uint32_t type, uint32_t number, int32_t *measurements) {
+ if (mMeasurementMode == MEASUREMENT_MODE_NONE) {
+ ALOGE("Cannot retrieve int measurements, no measurement mode set");
+ return INVALID_OPERATION;
+ }
+ if (!(mMeasurementMode & type)) {
+ // measurement type has not been set on this Visualizer
+ ALOGE("Cannot retrieve int measurements, requested measurement mode 0x%x not set(0x%x)",
+ type, mMeasurementMode);
+ return INVALID_OPERATION;
+ }
+ // only peak+RMS measurement supported
+ if ((type != MEASUREMENT_MODE_PEAK_RMS)
+ // for peak+RMS measurement, the results are 2 int32_t values
+ || (number != 2)) {
+ ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d",
+ number);
+ return BAD_VALUE;
+ }
+
+ status_t status = NO_ERROR;
+ if (mEnabled) {
+ uint32_t replySize = number * sizeof(int32_t);
+ status = command(VISUALIZER_CMD_MEASURE,
+ sizeof(uint32_t) /*cmdSize*/,
+ &type /*cmdData*/,
+ &replySize, measurements);
+ ALOGV("getMeasurements() command returned %d", status);
+ if ((status == NO_ERROR) && (replySize == 0)) {
+ status = NOT_ENOUGH_DATA;
+ }
+ } else {
+ ALOGV("getMeasurements() disabled");
+ return INVALID_OPERATION;
+ }
+ return status;
+}
+
+status_t Visualizer::getWaveForm(uint8_t *waveform)
+{
+ if (waveform == NULL) {
+ return BAD_VALUE;
+ }
+ if (mCaptureSize == 0) {
+ return NO_INIT;
+ }
+
+ status_t status = NO_ERROR;
+ if (mEnabled) {
+ uint32_t replySize = mCaptureSize;
+ status = command(VISUALIZER_CMD_CAPTURE, 0, NULL, &replySize, waveform);
+ ALOGV("getWaveForm() command returned %d", status);
+ if ((status == NO_ERROR) && (replySize == 0)) {
+ status = NOT_ENOUGH_DATA;
+ }
+ } else {
+ ALOGV("getWaveForm() disabled");
+ memset(waveform, 0x80, mCaptureSize);
+ }
+ return status;
+}
+
+status_t Visualizer::getFft(uint8_t *fft)
+{
+ if (fft == NULL) {
+ return BAD_VALUE;
+ }
+ if (mCaptureSize == 0) {
+ return NO_INIT;
+ }
+
+ status_t status = NO_ERROR;
+ if (mEnabled) {
+ uint8_t buf[mCaptureSize];
+ status = getWaveForm(buf);
+ if (status == NO_ERROR) {
+ status = doFft(fft, buf);
+ }
+ } else {
+ memset(fft, 0, mCaptureSize);
+ }
+ return status;
+}
+
+status_t Visualizer::doFft(uint8_t *fft, uint8_t *waveform)
+{
+ int32_t workspace[mCaptureSize >> 1];
+ int32_t nonzero = 0;
+
+ for (uint32_t i = 0; i < mCaptureSize; i += 2) {
+ workspace[i >> 1] =
+ ((waveform[i] ^ 0x80) << 24) | ((waveform[i + 1] ^ 0x80) << 8);
+ nonzero |= workspace[i >> 1];
+ }
+
+ if (nonzero) {
+ fixed_fft_real(mCaptureSize >> 1, workspace);
+ }
+
+ for (uint32_t i = 0; i < mCaptureSize; i += 2) {
+ short tmp = workspace[i >> 1] >> 21;
+ while (tmp > 127 || tmp < -128) tmp >>= 1;
+ fft[i] = tmp;
+ tmp = workspace[i >> 1];
+ tmp >>= 5;
+ while (tmp > 127 || tmp < -128) tmp >>= 1;
+ fft[i + 1] = tmp;
+ }
+
+ return NO_ERROR;
+}
+
+void Visualizer::periodicCapture()
+{
+ Mutex::Autolock _l(mCaptureLock);
+ ALOGV("periodicCapture() %p mCaptureCallBack %p mCaptureFlags 0x%08x",
+ this, mCaptureCallBack, mCaptureFlags);
+ if (mCaptureCallBack != NULL &&
+ (mCaptureFlags & (CAPTURE_WAVEFORM|CAPTURE_FFT)) &&
+ mCaptureSize != 0) {
+ uint8_t waveform[mCaptureSize];
+ status_t status = getWaveForm(waveform);
+ if (status != NO_ERROR) {
+ return;
+ }
+ uint8_t fft[mCaptureSize];
+ if (mCaptureFlags & CAPTURE_FFT) {
+ status = doFft(fft, waveform);
+ }
+ if (status != NO_ERROR) {
+ return;
+ }
+ uint8_t *wavePtr = NULL;
+ uint8_t *fftPtr = NULL;
+ uint32_t waveSize = 0;
+ uint32_t fftSize = 0;
+ if (mCaptureFlags & CAPTURE_WAVEFORM) {
+ wavePtr = waveform;
+ waveSize = mCaptureSize;
+ }
+ if (mCaptureFlags & CAPTURE_FFT) {
+ fftPtr = fft;
+ fftSize = mCaptureSize;
+ }
+ mCaptureCallBack(mCaptureCbkUser, waveSize, wavePtr, fftSize, fftPtr, mSampleRate);
+ }
+}
+
+uint32_t Visualizer::initCaptureSize()
+{
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t *p = (effect_param_t *)buf32;
+
+ p->psize = sizeof(uint32_t);
+ p->vsize = sizeof(uint32_t);
+ *(int32_t *)p->data = VISUALIZER_PARAM_CAPTURE_SIZE;
+ status_t status = getParameter(p);
+
+ if (status == NO_ERROR) {
+ status = p->status;
+ }
+
+ uint32_t size = 0;
+ if (status == NO_ERROR) {
+ size = *((int32_t *)p->data + 1);
+ }
+ mCaptureSize = size;
+
+ ALOGV("initCaptureSize size %d status %d", mCaptureSize, status);
+
+ return size;
+}
+
+void Visualizer::controlStatusChanged(bool controlGranted) {
+ if (controlGranted) {
+ // this Visualizer instance regained control of the effect, reset the scaling mode
+ // and capture size as has been cached through it.
+ ALOGV("controlStatusChanged(true) causes effect parameter reset:");
+ ALOGV(" scaling mode reset to %d", mScalingMode);
+ setScalingMode(mScalingMode);
+ ALOGV(" capture size reset to %d", mCaptureSize);
+ setCaptureSize(mCaptureSize);
+ }
+ AudioEffect::controlStatusChanged(controlGranted);
+}
+
+//-------------------------------------------------------------------------
+
+Visualizer::CaptureThread::CaptureThread(Visualizer* receiver, uint32_t captureRate,
+ bool bCanCallJava)
+ : Thread(bCanCallJava), mReceiver(receiver)
+{
+ mSleepTimeUs = 1000000000 / captureRate;
+ ALOGV("CaptureThread cstor %p captureRate %d mSleepTimeUs %d", this, captureRate, mSleepTimeUs);
+}
+
+bool Visualizer::CaptureThread::threadLoop()
+{
+ ALOGV("CaptureThread %p enter", this);
+ sp<Visualizer> receiver = mReceiver.promote();
+ if (receiver == NULL) {
+ return false;
+ }
+ while (!exitPending())
+ {
+ usleep(mSleepTimeUs);
+ receiver->periodicCapture();
+ }
+ ALOGV("CaptureThread %p exiting", this);
+ return false;
+}
+
+} // namespace android
diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h
new file mode 100644
index 000000000000..8078e369ee82
--- /dev/null
+++ b/media/jni/audioeffect/Visualizer.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef ANDROID_MEDIA_VISUALIZER_H
+#define ANDROID_MEDIA_VISUALIZER_H
+
+#include <media/AudioEffect.h>
+#include <system/audio_effects/effect_visualizer.h>
+#include <utils/Thread.h>
+
+/**
+ * The Visualizer class enables application to retrieve part of the currently playing audio for
+ * visualization purpose. It is not an audio recording interface and only returns partial and low
+ * quality audio content. However, to protect privacy of certain audio data (e.g voice mail) the use
+ * of the visualizer requires the permission android.permission.RECORD_AUDIO.
+ * The audio session ID passed to the constructor indicates which audio content should be
+ * visualized:
+ * - If the session is 0, the audio output mix is visualized
+ * - If the session is not 0, the audio from a particular MediaPlayer or AudioTrack
+ * using this audio session is visualized
+ * Two types of representation of audio content can be captured:
+ * - Waveform data: consecutive 8-bit (unsigned) mono samples by using the getWaveForm() method
+ * - Frequency data: 8-bit magnitude FFT by using the getFft() method
+ *
+ * The length of the capture can be retrieved or specified by calling respectively
+ * getCaptureSize() and setCaptureSize() methods. Note that the size of the FFT
+ * is half of the specified capture size but both sides of the spectrum are returned yielding in a
+ * number of bytes equal to the capture size. The capture size must be a power of 2 in the range
+ * returned by getMinCaptureSize() and getMaxCaptureSize().
+ * In addition to the polling capture mode, a callback mode is also available by installing a
+ * callback function by use of the setCaptureCallBack() method. The rate at which the callback
+ * is called as well as the type of data returned is specified.
+ * Before capturing data, the Visualizer must be enabled by calling the setEnabled() method.
+ * When data capture is not needed any more, the Visualizer should be disabled.
+ */
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class Visualizer: public AudioEffect {
+public:
+
+ enum callback_flags {
+ CAPTURE_WAVEFORM = 0x00000001, // capture callback returns a PCM wave form
+ CAPTURE_FFT = 0x00000002, // apture callback returns a frequency representation
+ CAPTURE_CALL_JAVA = 0x00000004 // the callback thread can call java
+ };
+
+
+ /* Constructor.
+ * See AudioEffect constructor for details on parameters.
+ */
+ Visualizer(const String16& opPackageName,
+ int32_t priority = 0,
+ effect_callback_t cbf = NULL,
+ void* user = NULL,
+ audio_session_t sessionId = AUDIO_SESSION_OUTPUT_MIX);
+
+ ~Visualizer();
+
+ virtual status_t setEnabled(bool enabled);
+
+ // maximum capture size in samples
+ static uint32_t getMaxCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MAX; }
+ // minimum capture size in samples
+ static uint32_t getMinCaptureSize() { return VISUALIZER_CAPTURE_SIZE_MIN; }
+ // maximum capture rate in millihertz
+ static uint32_t getMaxCaptureRate() { return CAPTURE_RATE_MAX; }
+
+ // callback used to return periodic PCM or FFT captures to the application. Either one or both
+ // types of data are returned (PCM and FFT) according to flags indicated when installing the
+ // callback. When a type of data is not present, the corresponding size (waveformSize or
+ // fftSize) is 0.
+ typedef void (*capture_cbk_t)(void* user,
+ uint32_t waveformSize,
+ uint8_t *waveform,
+ uint32_t fftSize,
+ uint8_t *fft,
+ uint32_t samplingrate);
+
+ // install a callback to receive periodic captures. The capture rate is specified in milliHertz
+ // and the capture format is according to flags (see callback_flags).
+ status_t setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t flags, uint32_t rate);
+
+ // set the capture size capture size must be a power of two in the range
+ // [VISUALIZER_CAPTURE_SIZE_MAX. VISUALIZER_CAPTURE_SIZE_MIN]
+ // must be called when the visualizer is not enabled
+ status_t setCaptureSize(uint32_t size);
+ uint32_t getCaptureSize() { return mCaptureSize; }
+
+ // returns the capture rate indicated when installing the callback
+ uint32_t getCaptureRate() { return mCaptureRate; }
+
+ // returns the sampling rate of the audio being captured
+ uint32_t getSamplingRate() { return mSampleRate; }
+
+ // set the way volume affects the captured data
+ // mode must one of VISUALIZER_SCALING_MODE_NORMALIZED,
+ // VISUALIZER_SCALING_MODE_AS_PLAYED
+ status_t setScalingMode(uint32_t mode);
+ uint32_t getScalingMode() { return mScalingMode; }
+
+ // set which measurements are done on the audio buffers processed by the effect.
+ // valid measurements (mask): MEASUREMENT_MODE_PEAK_RMS
+ status_t setMeasurementMode(uint32_t mode);
+ uint32_t getMeasurementMode() { return mMeasurementMode; }
+
+ // return a set of int32_t measurements
+ status_t getIntMeasurements(uint32_t type, uint32_t number, int32_t *measurements);
+
+ // return a capture in PCM 8 bit unsigned format. The size of the capture is equal to
+ // getCaptureSize()
+ status_t getWaveForm(uint8_t *waveform);
+
+ // return a capture in FFT 8 bit signed format. The size of the capture is equal to
+ // getCaptureSize() but the length of the FFT is half of the size (both parts of the spectrum
+ // are returned
+ status_t getFft(uint8_t *fft);
+ void release();
+
+protected:
+ // from IEffectClient
+ virtual void controlStatusChanged(bool controlGranted);
+
+private:
+
+ static const uint32_t CAPTURE_RATE_MAX = 20000;
+ static const uint32_t CAPTURE_RATE_DEF = 10000;
+ static const uint32_t CAPTURE_SIZE_DEF = VISUALIZER_CAPTURE_SIZE_MAX;
+
+ /* internal class to handle the callback */
+ class CaptureThread : public Thread
+ {
+ public:
+ CaptureThread(Visualizer* visualizer, uint32_t captureRate, bool bCanCallJava = false);
+
+ private:
+ friend class Visualizer;
+ virtual bool threadLoop();
+ wp<Visualizer> mReceiver;
+ Mutex mLock;
+ uint32_t mSleepTimeUs;
+ };
+
+ status_t doFft(uint8_t *fft, uint8_t *waveform);
+ void periodicCapture();
+ uint32_t initCaptureSize();
+
+ Mutex mCaptureLock;
+ uint32_t mCaptureRate;
+ uint32_t mCaptureSize;
+ uint32_t mSampleRate;
+ uint32_t mScalingMode;
+ uint32_t mMeasurementMode;
+ capture_cbk_t mCaptureCallBack;
+ void *mCaptureCbkUser;
+ sp<CaptureThread> mCaptureThread;
+ uint32_t mCaptureFlags;
+};
+
+
+}; // namespace android
+
+#endif // ANDROID_MEDIA_VISUALIZER_H
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 45de36ed3e4d..1362433bb26e 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -24,7 +24,7 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/threads.h>
-#include "media/Visualizer.h"
+#include "Visualizer.h"
#include <nativehelper/ScopedUtfChars.h>
diff --git a/media/jni/audioeffect/exports.lds b/media/jni/audioeffect/exports.lds
new file mode 100644
index 000000000000..70491f451c0c
--- /dev/null
+++ b/media/jni/audioeffect/exports.lds
@@ -0,0 +1,6 @@
+{
+ global:
+ *;
+ local:
+ *android10Visualizer*;
+};
diff --git a/media/jni/soundpool/Android.bp b/media/jni/soundpool/Android.bp
index 35b7b01e207c..0be2514a7936 100644
--- a/media/jni/soundpool/Android.bp
+++ b/media/jni/soundpool/Android.bp
@@ -3,11 +3,20 @@ cc_library_shared {
srcs: [
"android_media_SoundPool.cpp",
+ "Sound.cpp",
+ "SoundDecoder.cpp",
+ "SoundManager.cpp",
"SoundPool.cpp",
- "SoundPoolThread.cpp",
+ "Stream.cpp",
+ "StreamManager.cpp",
+ ],
+
+ header_libs: [
+ "libmedia_headers",
],
shared_libs: [
+ "libaudioutils",
"liblog",
"libcutils",
"libutils",
diff --git a/media/jni/soundpool/Sound.cpp b/media/jni/soundpool/Sound.cpp
new file mode 100644
index 000000000000..0bbc3e46b044
--- /dev/null
+++ b/media/jni/soundpool/Sound.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool::Sound"
+#include <utils/Log.h>
+
+#include "Sound.h"
+
+#include <media/NdkMediaCodec.h>
+#include <media/NdkMediaExtractor.h>
+#include <media/NdkMediaFormat.h>
+
+namespace android::soundpool {
+
+constexpr uint32_t kMaxSampleRate = 192000;
+constexpr size_t kDefaultHeapSize = 1024 * 1024; // 1MB (compatible with low mem devices)
+
+Sound::Sound(int32_t soundID, int fd, int64_t offset, int64_t length)
+ : mSoundID(soundID)
+ , mFd(dup(fd))
+ , mOffset(offset)
+ , mLength(length)
+{
+ ALOGV("%s(soundID=%d, fd=%d, offset=%lld, length=%lld)",
+ __func__, soundID, fd, (long long)offset, (long long)length);
+ ALOGW_IF(mFd == -1, "Unable to dup descriptor %d", fd);
+}
+
+Sound::~Sound()
+{
+ ALOGV("%s(soundID=%d, fd=%d)", __func__, mSoundID, mFd.get());
+}
+
+static status_t decode(int fd, int64_t offset, int64_t length,
+ uint32_t *rate, int32_t *channelCount, audio_format_t *audioFormat,
+ audio_channel_mask_t *channelMask, sp<MemoryHeapBase> heap,
+ size_t *sizeInBytes) {
+ ALOGV("%s(fd=%d, offset=%lld, length=%lld, ...)",
+ __func__, fd, (long long)offset, (long long)length);
+ std::unique_ptr<AMediaExtractor, decltype(&AMediaExtractor_delete)> ex{
+ AMediaExtractor_new(), &AMediaExtractor_delete};
+ status_t err = AMediaExtractor_setDataSourceFd(ex.get(), fd, offset, length);
+
+ if (err != AMEDIA_OK) {
+ return err;
+ }
+
+ *audioFormat = AUDIO_FORMAT_PCM_16_BIT; // default format for audio codecs.
+ const size_t numTracks = AMediaExtractor_getTrackCount(ex.get());
+ for (size_t i = 0; i < numTracks; i++) {
+ std::unique_ptr<AMediaFormat, decltype(&AMediaFormat_delete)> format{
+ AMediaExtractor_getTrackFormat(ex.get(), i), &AMediaFormat_delete};
+ const char *mime;
+ if (!AMediaFormat_getString(format.get(), AMEDIAFORMAT_KEY_MIME, &mime)) {
+ return UNKNOWN_ERROR;
+ }
+ if (strncmp(mime, "audio/", 6) == 0) {
+ std::unique_ptr<AMediaCodec, decltype(&AMediaCodec_delete)> codec{
+ AMediaCodec_createDecoderByType(mime), &AMediaCodec_delete};
+ if (codec == nullptr
+ || AMediaCodec_configure(codec.get(), format.get(),
+ nullptr /* window */, nullptr /* drm */, 0 /* flags */) != AMEDIA_OK
+ || AMediaCodec_start(codec.get()) != AMEDIA_OK
+ || AMediaExtractor_selectTrack(ex.get(), i) != AMEDIA_OK) {
+ return UNKNOWN_ERROR;
+ }
+
+ bool sawInputEOS = false;
+ bool sawOutputEOS = false;
+ uint8_t* writePos = static_cast<uint8_t*>(heap->getBase());
+ size_t available = heap->getSize();
+ size_t written = 0;
+ format.reset(AMediaCodec_getOutputFormat(codec.get())); // update format.
+
+ while (!sawOutputEOS) {
+ if (!sawInputEOS) {
+ ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec.get(), 5000);
+ ALOGV("%s: input buffer %zd", __func__, bufidx);
+ if (bufidx >= 0) {
+ size_t bufsize;
+ uint8_t * const buf = AMediaCodec_getInputBuffer(
+ codec.get(), bufidx, &bufsize);
+ if (buf == nullptr) {
+ ALOGE("%s: AMediaCodec_getInputBuffer returned nullptr, short decode",
+ __func__);
+ break;
+ }
+ int sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
+ ALOGV("%s: read %d", __func__, sampleSize);
+ if (sampleSize < 0) {
+ sampleSize = 0;
+ sawInputEOS = true;
+ ALOGV("%s: EOS", __func__);
+ }
+ const int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex.get());
+
+ const media_status_t mstatus = AMediaCodec_queueInputBuffer(
+ codec.get(), bufidx,
+ 0 /* offset */, sampleSize, presentationTimeUs,
+ sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+ if (mstatus != AMEDIA_OK) {
+ // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
+ ALOGE("%s: AMediaCodec_queueInputBuffer returned status %d,"
+ "short decode",
+ __func__, (int)mstatus);
+ break;
+ }
+ (void)AMediaExtractor_advance(ex.get());
+ }
+ }
+
+ AMediaCodecBufferInfo info;
+ const int status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
+ ALOGV("%s: dequeueoutput returned: %d", __func__, status);
+ if (status >= 0) {
+ if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+ ALOGV("%s: output EOS", __func__);
+ sawOutputEOS = true;
+ }
+ ALOGV("%s: got decoded buffer size %d", __func__, info.size);
+
+ const uint8_t * const buf = AMediaCodec_getOutputBuffer(
+ codec.get(), status, nullptr /* out_size */);
+ if (buf == nullptr) {
+ ALOGE("%s: AMediaCodec_getOutputBuffer returned nullptr, short decode",
+ __func__);
+ break;
+ }
+ const size_t dataSize = std::min((size_t)info.size, available);
+ memcpy(writePos, buf + info.offset, dataSize);
+ writePos += dataSize;
+ written += dataSize;
+ available -= dataSize;
+ const media_status_t mstatus = AMediaCodec_releaseOutputBuffer(
+ codec.get(), status, false /* render */);
+ if (mstatus != AMEDIA_OK) {
+ // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
+ ALOGE("%s: AMediaCodec_releaseOutputBuffer"
+ " returned status %d, short decode",
+ __func__, (int)mstatus);
+ break;
+ }
+ if (available == 0) {
+ // there might be more data, but there's no space for it
+ sawOutputEOS = true;
+ }
+ } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ ALOGV("%s: output buffers changed", __func__);
+ } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ format.reset(AMediaCodec_getOutputFormat(codec.get())); // update format
+ ALOGV("%s: format changed to: %s",
+ __func__, AMediaFormat_toString(format.get()));
+ } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ ALOGV("%s: no output buffer right now", __func__);
+ } else if (status <= AMEDIA_ERROR_BASE) {
+ ALOGE("%s: decode error: %d", __func__, status);
+ break;
+ } else {
+ ALOGV("%s: unexpected info code: %d", __func__, status);
+ }
+ }
+
+ (void)AMediaCodec_stop(codec.get());
+ if (!AMediaFormat_getInt32(
+ format.get(), AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) rate) ||
+ !AMediaFormat_getInt32(
+ format.get(), AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount)) {
+ return UNKNOWN_ERROR;
+ }
+ if (!AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_CHANNEL_MASK,
+ (int32_t*) channelMask)) {
+ *channelMask = AUDIO_CHANNEL_NONE;
+ }
+ *sizeInBytes = written;
+ return OK;
+ }
+ }
+ return UNKNOWN_ERROR;
+}
+
+status_t Sound::doLoad()
+{
+ ALOGV("%s()", __func__);
+ status_t status = NO_INIT;
+ if (mFd.get() != -1) {
+ mHeap = new MemoryHeapBase(kDefaultHeapSize);
+
+ ALOGV("%s: start decode", __func__);
+ uint32_t sampleRate;
+ int32_t channelCount;
+ audio_format_t format;
+ audio_channel_mask_t channelMask;
+ status_t status = decode(mFd.get(), mOffset, mLength, &sampleRate, &channelCount, &format,
+ &channelMask, mHeap, &mSizeInBytes);
+ ALOGV("%s: close(%d)", __func__, mFd.get());
+ mFd.reset(); // close
+
+ if (status != NO_ERROR) {
+ ALOGE("%s: unable to load sound", __func__);
+ } else if (sampleRate > kMaxSampleRate) {
+ ALOGE("%s: sample rate (%u) out of range", __func__, sampleRate);
+ status = BAD_VALUE;
+ } else if (channelCount < 1 || channelCount > FCC_8) {
+ ALOGE("%s: sample channel count (%d) out of range", __func__, channelCount);
+ status = BAD_VALUE;
+ } else {
+ // Correctly loaded, proper parameters
+ ALOGV("%s: pointer = %p, sizeInBytes = %zu, sampleRate = %u, channelCount = %d",
+ __func__, mHeap->getBase(), mSizeInBytes, sampleRate, channelCount);
+ mData = new MemoryBase(mHeap, 0, mSizeInBytes);
+ mSampleRate = sampleRate;
+ mChannelCount = channelCount;
+ mFormat = format;
+ mChannelMask = channelMask;
+ mState = READY; // this should be last, as it is an atomic sync point
+ return NO_ERROR;
+ }
+ } else {
+ ALOGE("%s: uninitialized fd, dup failed", __func__);
+ }
+ // ERROR handling
+ mHeap.clear();
+ mState = DECODE_ERROR; // this should be last, as it is an atomic sync point
+ return status;
+}
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/Sound.h b/media/jni/soundpool/Sound.h
new file mode 100644
index 000000000000..efe940a7bd68
--- /dev/null
+++ b/media/jni/soundpool/Sound.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <system/audio.h>
+
+namespace android::soundpool {
+
+class SoundDecoder;
+
+/**
+ * Sound is a resource used by SoundPool, referenced by soundID.
+ *
+ * After loading, it is effectively const so no locking required.
+ * However, in order to guarantee that all the values have been
+ * written properly and read properly, we use the mState as an atomic synchronization
+ * point. So if getState() shows READY, then all the other getters may
+ * be safely read.
+ *
+ * Technical details:
+ * We access the mState atomic value through memory_order_seq_cst
+ *
+ * https://en.cppreference.com/w/cpp/atomic/memory_order
+ *
+ * which provides memory barriers. So if the last value written by the SoundDecoder
+ * is mState, then the compiler ensures no other prior writes by SoundDecoder will be
+ * reordered afterwards, and memory barrier is placed (as necessary) to ensure the
+ * cache is visible to other processors.
+ *
+ * Likewise, if the first value read by SoundPool is mState,
+ * the compiler ensures no reads for that thread will be reordered before mState is read,
+ * and a memory barrier is placed (as necessary) to ensure that the cache is properly
+ * updated with other processor's writes before reading.
+ *
+ * See https://developer.android.com/training/articles/smp for discussions about
+ * the variant load-acquire, store-release semantics.
+ */
+class Sound {
+ friend SoundDecoder; // calls doLoad().
+
+public:
+ enum sound_state : int32_t { LOADING, READY, DECODE_ERROR };
+ // A sound starts in the LOADING state and transitions only once
+ // to either READY or DECODE_ERROR when doLoad() is called.
+
+ Sound(int soundID, int fd, int64_t offset, int64_t length);
+ ~Sound();
+
+ int32_t getSoundID() const { return mSoundID; }
+ int32_t getChannelCount() const { return mChannelCount; }
+ uint32_t getSampleRate() const { return mSampleRate; }
+ audio_format_t getFormat() const { return mFormat; }
+ audio_channel_mask_t getChannelMask() const { return mChannelMask; }
+ size_t getSizeInBytes() const { return mSizeInBytes; }
+ sound_state getState() const { return mState; }
+ uint8_t* getData() const { return static_cast<uint8_t*>(mData->unsecurePointer()); }
+ sp<IMemory> getIMemory() const { return mData; }
+
+private:
+ status_t doLoad(); // only SoundDecoder accesses this.
+
+ size_t mSizeInBytes = 0;
+ const int32_t mSoundID;
+ uint32_t mSampleRate = 0;
+ std::atomic<sound_state> mState = LOADING; // used as synchronization point
+ int32_t mChannelCount = 0;
+ audio_format_t mFormat = AUDIO_FORMAT_INVALID;
+ audio_channel_mask_t mChannelMask = AUDIO_CHANNEL_NONE;
+ base::unique_fd mFd; // initialized in constructor, reset to -1 after loading
+ const int64_t mOffset; // int64_t to match java long, see off64_t
+ const int64_t mLength; // int64_t to match java long, see off64_t
+ sp<IMemory> mData;
+ sp<MemoryHeapBase> mHeap;
+};
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/SoundDecoder.cpp b/media/jni/soundpool/SoundDecoder.cpp
new file mode 100644
index 000000000000..12200ef03aad
--- /dev/null
+++ b/media/jni/soundpool/SoundDecoder.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool::SoundDecoder"
+#include "utils/Log.h"
+
+#include "SoundDecoder.h"
+
+namespace android::soundpool {
+
+// Maximum Samples that can be background decoded before we block the caller.
+static constexpr size_t kMaxQueueSize = 128;
+
+// The amount of time we wait for a new Sound decode request
+// before the SoundDecoder thread closes.
+static constexpr int32_t kWaitTimeBeforeCloseMs = 1000;
+
+SoundDecoder::SoundDecoder(SoundManager* soundManager, size_t threads)
+ : mSoundManager(soundManager)
+{
+ ALOGV("%s(%p, %zu)", __func__, soundManager, threads);
+ // ThreadPool is created, but we don't launch any threads.
+ mThreadPool = std::make_unique<ThreadPool>(
+ std::min(threads, (size_t)std::thread::hardware_concurrency()),
+ "SoundDecoder_");
+}
+
+SoundDecoder::~SoundDecoder()
+{
+ ALOGV("%s()", __func__);
+ quit();
+}
+
+void SoundDecoder::quit()
+{
+ ALOGV("%s()", __func__);
+ {
+ std::lock_guard lock(mLock);
+ mQuit = true;
+ mQueueSpaceAvailable.notify_all(); // notify all load waiters
+ mQueueDataAvailable.notify_all(); // notify all worker threads
+ }
+ mThreadPool->quit();
+}
+
+void SoundDecoder::run(int32_t id __unused /* ALOGV only */)
+{
+ ALOGV("%s(%d): entering", __func__, id);
+ std::unique_lock lock(mLock);
+ while (!mQuit) {
+ if (mSoundIDs.size() == 0) {
+ ALOGV("%s(%d): waiting", __func__, id);
+ mQueueDataAvailable.wait_for(
+ lock, std::chrono::duration<int32_t, std::milli>(kWaitTimeBeforeCloseMs));
+ if (mSoundIDs.size() == 0) {
+ break; // no new sound, exit this thread.
+ }
+ continue;
+ }
+ const int32_t soundID = mSoundIDs.front();
+ mSoundIDs.pop_front();
+ mQueueSpaceAvailable.notify_one();
+ ALOGV("%s(%d): processing soundID: %d size: %zu", __func__, id, soundID, mSoundIDs.size());
+ lock.unlock();
+ std::shared_ptr<Sound> sound = mSoundManager->findSound(soundID);
+ status_t status = NO_INIT;
+ if (sound.get() != nullptr) {
+ status = sound->doLoad();
+ }
+ ALOGV("%s(%d): notifying loaded soundID:%d status:%d", __func__, id, soundID, status);
+ mSoundManager->notify(SoundPoolEvent(SoundPoolEvent::SOUND_LOADED, soundID, status));
+ lock.lock();
+ }
+ ALOGV("%s(%d): exiting", __func__, id);
+}
+
+void SoundDecoder::loadSound(int32_t soundID)
+{
+ ALOGV("%s(%d)", __func__, soundID);
+ size_t pendingSounds;
+ {
+ std::unique_lock lock(mLock);
+ while (mSoundIDs.size() == kMaxQueueSize) {
+ if (mQuit) return;
+ ALOGV("%s: waiting soundID: %d size: %zu", __func__, soundID, mSoundIDs.size());
+ mQueueSpaceAvailable.wait(lock);
+ }
+ if (mQuit) return;
+ mSoundIDs.push_back(soundID);
+ mQueueDataAvailable.notify_one();
+ ALOGV("%s: adding soundID: %d size: %zu", __func__, soundID, mSoundIDs.size());
+ pendingSounds = mSoundIDs.size();
+ }
+ // Launch threads as needed. The "as needed" is weakly consistent as we release mLock.
+ if (pendingSounds > mThreadPool->getActiveThreadCount()) {
+ const int32_t id __unused = mThreadPool->launch([this](int32_t id) { run(id); });
+ ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
+ }
+}
+
+} // end namespace android::soundpool
diff --git a/media/jni/soundpool/SoundDecoder.h b/media/jni/soundpool/SoundDecoder.h
new file mode 100644
index 000000000000..1288943e86e3
--- /dev/null
+++ b/media/jni/soundpool/SoundDecoder.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SoundPool.h"
+
+#include <deque>
+#include <mutex>
+
+namespace android::soundpool {
+
+/**
+ * SoundDecoder handles background decoding tasks.
+ */
+class SoundDecoder {
+public:
+ SoundDecoder(SoundManager* soundManager, size_t threads);
+ ~SoundDecoder();
+ void loadSound(int32_t soundID);
+ void quit();
+
+private:
+ void run(int32_t id); // The decode thread function.
+
+ SoundManager* const mSoundManager; // set in constructor, has own lock
+ std::unique_ptr<ThreadPool> mThreadPool; // set in constructor, has own lock
+
+ std::mutex mLock;
+ std::condition_variable mQueueSpaceAvailable;
+ std::condition_variable mQueueDataAvailable;
+
+ std::deque<int32_t> mSoundIDs; // GUARDED_BY(mLock);
+ bool mQuit = false; // GUARDED_BY(mLock);
+};
+
+} // end namespace android::soundpool
+
diff --git a/media/jni/soundpool/SoundManager.cpp b/media/jni/soundpool/SoundManager.cpp
new file mode 100644
index 000000000000..3c625bf3bb7f
--- /dev/null
+++ b/media/jni/soundpool/SoundManager.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool::SoundManager"
+#include <utils/Log.h>
+
+#include "SoundManager.h"
+
+#include <thread>
+
+#include "SoundDecoder.h"
+
+namespace android::soundpool {
+
+static const size_t kDecoderThreads = std::thread::hardware_concurrency() >= 4 ? 2 : 1;
+
+SoundManager::SoundManager()
+ : mDecoder{std::make_unique<SoundDecoder>(this, kDecoderThreads)}
+{
+ ALOGV("%s()", __func__);
+}
+
+SoundManager::~SoundManager()
+{
+ ALOGV("%s()", __func__);
+ mDecoder->quit();
+
+ std::lock_guard lock(mSoundManagerLock);
+ mSounds.clear();
+}
+
+int32_t SoundManager::load(int fd, int64_t offset, int64_t length, int32_t priority __unused)
+{
+ ALOGV("%s(fd=%d, offset=%lld, length=%lld, priority=%d)",
+ __func__, fd, (long long)offset, (long long)length, priority);
+ int32_t soundID;
+ {
+ std::lock_guard lock(mSoundManagerLock);
+ // mNextSoundID is always positive and does not "integer overflow"
+ do {
+ mNextSoundID = mNextSoundID == INT32_MAX ? 1 : mNextSoundID + 1;
+ } while (findSound_l(mNextSoundID) != nullptr);
+ soundID = mNextSoundID;
+ auto sound = std::make_shared<Sound>(soundID, fd, offset, length);
+ mSounds.emplace(soundID, sound);
+ }
+ // mDecoder->loadSound() must be called outside of mSoundManagerLock.
+ // mDecoder->loadSound() may block on mDecoder message queue space;
+ // the message queue emptying may block on SoundManager::findSound().
+ //
+ // It is theoretically possible that sound loads might decode out-of-order.
+ mDecoder->loadSound(soundID);
+ return soundID;
+}
+
+bool SoundManager::unload(int32_t soundID)
+{
+ ALOGV("%s(soundID=%d)", __func__, soundID);
+ std::lock_guard lock(mSoundManagerLock);
+ return mSounds.erase(soundID) > 0; // erase() returns number of sounds removed.
+}
+
+std::shared_ptr<Sound> SoundManager::findSound(int32_t soundID) const
+{
+ std::lock_guard lock(mSoundManagerLock);
+ return findSound_l(soundID);
+}
+
+std::shared_ptr<Sound> SoundManager::findSound_l(int32_t soundID) const
+{
+ auto it = mSounds.find(soundID);
+ return it != mSounds.end() ? it->second : nullptr;
+}
+
+void SoundManager::setCallback(SoundPool *soundPool, SoundPoolCallback* callback, void* user)
+{
+ mCallbackHandler.setCallback(soundPool, callback, user);
+}
+
+void SoundManager::notify(SoundPoolEvent event)
+{
+ mCallbackHandler.notify(event);
+}
+
+void* SoundManager::getUserData() const
+{
+ return mCallbackHandler.getUserData();
+}
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/SoundManager.h b/media/jni/soundpool/SoundManager.h
new file mode 100644
index 000000000000..9201e78132f4
--- /dev/null
+++ b/media/jni/soundpool/SoundManager.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Sound.h"
+
+#include <mutex>
+#include <unordered_map>
+
+namespace android {
+
+class SoundPool;
+
+// for queued events
+class SoundPoolEvent {
+public:
+ explicit SoundPoolEvent(int msg, int arg1 = 0, int arg2 = 0) :
+ mMsg(msg), mArg1(arg1), mArg2(arg2) {}
+ const int mMsg; // MessageType
+ const int mArg1; // soundID
+ const int mArg2; // status
+ enum MessageType { INVALID, SOUND_LOADED };
+};
+
+// callback function prototype
+typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user);
+
+} // namespace android
+
+namespace android::soundpool {
+
+// This class manages Sounds for the SoundPool.
+class SoundManager {
+public:
+ SoundManager();
+ ~SoundManager();
+
+ // Matches corresponding SoundPool API functions
+ int32_t load(int fd, int64_t offset, int64_t length, int32_t priority);
+ bool unload(int32_t soundID);
+ void setCallback(SoundPool* soundPool, SoundPoolCallback* callback, void* user);
+ void* getUserData() const;
+
+ // SoundPool and SoundDecoder access
+ std::shared_ptr<Sound> findSound(int32_t soundID) const;
+
+ // from the SoundDecoder
+ void notify(SoundPoolEvent event);
+
+private:
+
+ // CallbackHandler is used to manage notifications back to the app when a sound
+ // has been loaded. It uses a recursive lock to allow setting the callback
+ // during the callback.
+ class CallbackHandler {
+ public:
+ void setCallback(SoundPool *soundPool, SoundPoolCallback* callback, void* userData)
+ {
+ std::lock_guard<std::recursive_mutex> lock(mCallbackLock);
+ mSoundPool = soundPool;
+ mCallback = callback;
+ mUserData = userData;
+ }
+ void notify(SoundPoolEvent event) const
+ {
+ std::lock_guard<std::recursive_mutex> lock(mCallbackLock);
+ if (mCallback != nullptr) {
+ mCallback(event, mSoundPool, mUserData);
+ // Note: mCallback may call setCallback().
+ // so mCallback, mUserData may have changed.
+ }
+ }
+ void* getUserData() const
+ {
+ std::lock_guard<std::recursive_mutex> lock(mCallbackLock);
+ return mUserData;
+ }
+ private:
+ mutable std::recursive_mutex mCallbackLock; // allow mCallback to setCallback().
+ SoundPool* mSoundPool = nullptr; // GUARDED_BY(mCallbackLock)
+ SoundPoolCallback* mCallback = nullptr; // GUARDED_BY(mCallbackLock)
+ void* mUserData = nullptr; // GUARDED_BY(mCallbackLock)
+ };
+
+ std::shared_ptr<Sound> findSound_l(int32_t soundID) const;
+
+ // The following variables are initialized in constructor and can be accessed anytime.
+ CallbackHandler mCallbackHandler; // has its own lock
+ const std::unique_ptr<SoundDecoder> mDecoder; // has its own lock
+
+ mutable std::mutex mSoundManagerLock;
+ std::unordered_map<int, std::shared_ptr<Sound>> mSounds; // GUARDED_BY(mSoundManagerLock)
+ int32_t mNextSoundID = 0; // GUARDED_BY(mSoundManagerLock)
+};
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 102bbf0e5931..ac44843859f6 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -16,1124 +16,230 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "SoundPool"
-
-#include <chrono>
-#include <inttypes.h>
-#include <thread>
#include <utils/Log.h>
-#define USE_SHARED_MEM_BUFFER
+#include <algorithm>
+#include <thread>
-#include <media/AudioTrack.h>
#include "SoundPool.h"
-#include "SoundPoolThread.h"
-#include <media/NdkMediaCodec.h>
-#include <media/NdkMediaExtractor.h>
-#include <media/NdkMediaFormat.h>
namespace android
{
-int kDefaultBufferCount = 4;
-uint32_t kMaxSampleRate = 48000;
-uint32_t kDefaultSampleRate = 44100;
-uint32_t kDefaultFrameCount = 1200;
-size_t kDefaultHeapSize = 1024 * 1024; // 1MB
-
-
-SoundPool::SoundPool(int maxChannels, const audio_attributes_t* pAttributes)
-{
- ALOGV("SoundPool constructor: maxChannels=%d, attr.usage=%d, attr.flags=0x%x, attr.tags=%s",
- maxChannels, pAttributes->usage, pAttributes->flags, pAttributes->tags);
-
- // check limits
- mMaxChannels = maxChannels;
- if (mMaxChannels < 1) {
- mMaxChannels = 1;
- }
- else if (mMaxChannels > 32) {
- mMaxChannels = 32;
- }
- ALOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels);
-
- mQuit = false;
- mMuted = false;
- mDecodeThread = 0;
- memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
- mAllocated = 0;
- mNextSampleID = 0;
- mNextChannelID = 0;
-
- mCallback = 0;
- mUserData = 0;
-
- mChannelPool = new SoundChannel[mMaxChannels];
- for (int i = 0; i < mMaxChannels; ++i) {
- mChannelPool[i].init(this);
- mChannels.push_back(&mChannelPool[i]);
- }
-
- // start decode thread
- startThreads();
-}
-
-SoundPool::~SoundPool()
-{
- ALOGV("SoundPool destructor");
- mDecodeThread->quit();
- quit();
-
- Mutex::Autolock lock(&mLock);
+// kManagerThreads = 1 historically.
+// Not really necessary to have more than one, but it does speed things up by about
+// 25% having 2 threads instead of 1 when playing many sounds. Having many threads
+// could starve other AudioFlinger clients with SoundPool activity. It may also cause
+// issues with app loading, e.g. Camera.
+static const size_t kStreamManagerThreads = std::thread::hardware_concurrency() >= 4 ? 2 : 1;
- mChannels.clear();
- if (mChannelPool)
- delete [] mChannelPool;
- // clean up samples
- ALOGV("clear samples");
- mSamples.clear();
+// kUseApiLock = true prior to R.
+// Set to true to prevent multiple users access internal to the SoundPool API.
+// Set to false to make the SoundPool methods weakly consistent. When set to false,
+// only AutoPause and AutoResume are locked, which are the only two methods that
+// require API level locking for consistency.
+static constexpr bool kUseApiLock = false;
- if (mDecodeThread)
- delete mDecodeThread;
-}
+namespace {
+// Check input arguments to SoundPool - return "true" to reject request.
-void SoundPool::addToRestartList(SoundChannel* channel)
+bool checkVolume(float *leftVolume, float *rightVolume)
{
- Mutex::Autolock lock(&mRestartLock);
- if (!mQuit) {
- mRestart.push_back(channel);
- mCondition.signal();
+ if (*leftVolume != std::clamp(*leftVolume, 0.f, 1.f) ||
+ *rightVolume != std::clamp(*rightVolume, 0.f, 1.f)) {
+ ALOGI("volume l=%f r=%f out of (0.f, 1.f) bounds, using 1.f", *leftVolume, *rightVolume);
+ // for backward compatibility use 1.f.
+ *leftVolume = *rightVolume = 1.f;
}
+ return false;
}
-void SoundPool::addToStopList(SoundChannel* channel)
+bool checkRate(float *rate)
{
- Mutex::Autolock lock(&mRestartLock);
- if (!mQuit) {
- mStop.push_back(channel);
- mCondition.signal();
+ if (*rate != std::clamp(*rate, 0.125f, 8.f)) {
+ ALOGI("rate %f out of (0.125f, 8.f) bounds, clamping", *rate);
+ // for backward compatibility just clamp
+ *rate = std::clamp(*rate, 0.125f, 8.f);
}
+ return false;
}
-int SoundPool::beginThread(void* arg)
-{
- SoundPool* p = (SoundPool*)arg;
- return p->run();
-}
-
-int SoundPool::run()
+bool checkPriority(int32_t *priority)
{
- mRestartLock.lock();
- while (!mQuit) {
- mCondition.wait(mRestartLock);
- ALOGV("awake");
- if (mQuit) break;
-
- while (!mStop.empty()) {
- SoundChannel* channel;
- ALOGV("Getting channel from stop list");
- List<SoundChannel* >::iterator iter = mStop.begin();
- channel = *iter;
- mStop.erase(iter);
- mRestartLock.unlock();
- if (channel != 0) {
- Mutex::Autolock lock(&mLock);
- channel->stop();
- }
- mRestartLock.lock();
- if (mQuit) break;
- }
-
- while (!mRestart.empty()) {
- SoundChannel* channel;
- ALOGV("Getting channel from list");
- List<SoundChannel*>::iterator iter = mRestart.begin();
- channel = *iter;
- mRestart.erase(iter);
- mRestartLock.unlock();
- if (channel != 0) {
- Mutex::Autolock lock(&mLock);
- channel->nextEvent();
- }
- mRestartLock.lock();
- if (mQuit) break;
- }
+ if (*priority < 0) {
+ ALOGI("negative priority %d, should be >= 0.", *priority);
+ // for backward compatibility, ignore.
}
-
- mStop.clear();
- mRestart.clear();
- mCondition.signal();
- mRestartLock.unlock();
- ALOGV("goodbye");
- return 0;
-}
-
-void SoundPool::quit()
-{
- mRestartLock.lock();
- mQuit = true;
- mCondition.signal();
- mCondition.wait(mRestartLock);
- ALOGV("return from quit");
- mRestartLock.unlock();
-}
-
-bool SoundPool::startThreads()
-{
- createThreadEtc(beginThread, this, "SoundPool");
- if (mDecodeThread == NULL)
- mDecodeThread = new SoundPoolThread(this);
- return mDecodeThread != NULL;
+ return false;
}
-sp<Sample> SoundPool::findSample(int sampleID)
+bool checkLoop(int32_t *loop)
{
- Mutex::Autolock lock(&mLock);
- return findSample_l(sampleID);
+ if (*loop < -1) {
+ ALOGI("loop %d, should be >= -1", *loop);
+ *loop = -1;
+ }
+ return false;
}
-sp<Sample> SoundPool::findSample_l(int sampleID)
-{
- return mSamples.valueFor(sampleID);
-}
+} // namespace
-SoundChannel* SoundPool::findChannel(int channelID)
+SoundPool::SoundPool(int32_t maxStreams, const audio_attributes_t* attributes)
+ : mStreamManager(maxStreams, kStreamManagerThreads, attributes)
{
- for (int i = 0; i < mMaxChannels; ++i) {
- if (mChannelPool[i].channelID() == channelID) {
- return &mChannelPool[i];
- }
- }
- return NULL;
+ ALOGV("%s(maxStreams=%d, attr={ content_type=%d, usage=%d, flags=0x%x, tags=%s })",
+ __func__, maxStreams,
+ attributes->content_type, attributes->usage, attributes->flags, attributes->tags);
}
-SoundChannel* SoundPool::findNextChannel(int channelID)
+SoundPool::~SoundPool()
{
- for (int i = 0; i < mMaxChannels; ++i) {
- if (mChannelPool[i].nextChannelID() == channelID) {
- return &mChannelPool[i];
- }
- }
- return NULL;
+ ALOGV("%s()", __func__);
}
-int SoundPool::load(int fd, int64_t offset, int64_t length, int priority __unused)
+int32_t SoundPool::load(int fd, int64_t offset, int64_t length, int32_t priority)
{
- ALOGV("load: fd=%d, offset=%" PRId64 ", length=%" PRId64 ", priority=%d",
- fd, offset, length, priority);
- int sampleID;
- {
- Mutex::Autolock lock(&mLock);
- sampleID = ++mNextSampleID;
- sp<Sample> sample = new Sample(sampleID, fd, offset, length);
- mSamples.add(sampleID, sample);
- sample->startLoad();
- }
- // mDecodeThread->loadSample() must be called outside of mLock.
- // mDecodeThread->loadSample() may block on mDecodeThread message queue space;
- // the message queue emptying may block on SoundPool::findSample().
- //
- // It theoretically possible that sample loads might decode out-of-order.
- mDecodeThread->loadSample(sampleID);
- return sampleID;
+ ALOGV("%s(fd=%d, offset=%lld, length=%lld, priority=%d)",
+ __func__, fd, (long long)offset, (long long)length, priority);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ return mSoundManager.load(fd, offset, length, priority);
}
-bool SoundPool::unload(int sampleID)
+bool SoundPool::unload(int32_t soundID)
{
- ALOGV("unload: sampleID=%d", sampleID);
- Mutex::Autolock lock(&mLock);
- return mSamples.removeItem(sampleID) >= 0; // removeItem() returns index or BAD_VALUE
+ ALOGV("%s(%d)", __func__, soundID);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ return mSoundManager.unload(soundID);
}
-int SoundPool::play(int sampleID, float leftVolume, float rightVolume,
- int priority, int loop, float rate)
+int32_t SoundPool::play(int32_t soundID, float leftVolume, float rightVolume,
+ int32_t priority, int32_t loop, float rate)
{
- ALOGV("play sampleID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f",
- sampleID, leftVolume, rightVolume, priority, loop, rate);
- SoundChannel* channel;
- int channelID;
-
- Mutex::Autolock lock(&mLock);
-
- if (mQuit) {
- return 0;
- }
- // is sample ready?
- sp<Sample> sample(findSample_l(sampleID));
- if ((sample == 0) || (sample->state() != Sample::READY)) {
- ALOGW(" sample %d not READY", sampleID);
- return 0;
- }
+ ALOGV("%s(soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)",
+ __func__, soundID, leftVolume, rightVolume, priority, loop, rate);
- dump();
+ // New for R: check arguments to ensure track can be created.
+ // If SoundPool defers the creation of the AudioTrack to the StreamManager thread,
+ // the failure to create may not be visible to the caller, so this precheck is needed.
+ if (checkVolume(&leftVolume, &rightVolume)
+ || checkPriority(&priority)
+ || checkLoop(&loop)
+ || checkRate(&rate)) return 0;
- // allocate a channel
- channel = allocateChannel_l(priority, sampleID);
-
- // no channel allocated - return 0
- if (!channel) {
- ALOGV("No channel allocated");
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ const std::shared_ptr<soundpool::Sound> sound = mSoundManager.findSound(soundID);
+ if (sound == nullptr || sound->getState() != soundpool::Sound::READY) {
+ ALOGW("%s soundID %d not READY", __func__, soundID);
return 0;
}
- channelID = ++mNextChannelID;
-
- ALOGV("play channel %p state = %d", channel, channel->state());
- channel->play(sample, channelID, leftVolume, rightVolume, priority, loop, rate);
- return channelID;
-}
-
-SoundChannel* SoundPool::allocateChannel_l(int priority, int sampleID)
-{
- List<SoundChannel*>::iterator iter;
- SoundChannel* channel = NULL;
-
- // check if channel for given sampleID still available
- if (!mChannels.empty()) {
- for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
- if (sampleID == (*iter)->getPrevSampleID() && (*iter)->state() == SoundChannel::IDLE) {
- channel = *iter;
- mChannels.erase(iter);
- ALOGV("Allocated recycled channel for same sampleID");
- break;
- }
- }
- }
-
- // allocate any channel
- if (!channel && !mChannels.empty()) {
- iter = mChannels.begin();
- if (priority >= (*iter)->priority()) {
- channel = *iter;
- mChannels.erase(iter);
- ALOGV("Allocated active channel");
- }
- }
-
- // update priority and put it back in the list
- if (channel) {
- channel->setPriority(priority);
- for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
- if (priority < (*iter)->priority()) {
- break;
- }
- }
- mChannels.insert(iter, channel);
- }
- return channel;
-}
-
-// move a channel from its current position to the front of the list
-void SoundPool::moveToFront_l(SoundChannel* channel)
-{
- for (List<SoundChannel*>::iterator iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
- if (*iter == channel) {
- mChannels.erase(iter);
- mChannels.push_front(channel);
- break;
- }
- }
-}
-
-void SoundPool::pause(int channelID)
-{
- ALOGV("pause(%d)", channelID);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->pause();
- }
+ const int32_t streamID = mStreamManager.queueForPlay(
+ sound, soundID, leftVolume, rightVolume, priority, loop, rate);
+ ALOGV("%s returned %d", __func__, streamID);
+ return streamID;
}
void SoundPool::autoPause()
{
- ALOGV("autoPause()");
- Mutex::Autolock lock(&mLock);
- for (int i = 0; i < mMaxChannels; ++i) {
- SoundChannel* channel = &mChannelPool[i];
- channel->autoPause();
- }
+ ALOGV("%s()", __func__);
+ auto apiLock = std::make_unique<std::lock_guard<std::mutex>>(mApiLock);
+ mStreamManager.forEach([](soundpool::Stream *stream) { stream->autoPause(); });
}
-void SoundPool::resume(int channelID)
+void SoundPool::autoResume()
{
- ALOGV("resume(%d)", channelID);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->resume();
- }
+ ALOGV("%s()", __func__);
+ auto apiLock = std::make_unique<std::lock_guard<std::mutex>>(mApiLock);
+ mStreamManager.forEach([](soundpool::Stream *stream) { stream->autoResume(); });
}
void SoundPool::mute(bool muting)
{
- ALOGV("mute(%d)", muting);
- Mutex::Autolock lock(&mLock);
- mMuted = muting;
- if (!mChannels.empty()) {
- for (List<SoundChannel*>::iterator iter = mChannels.begin();
- iter != mChannels.end(); ++iter) {
- (*iter)->mute(muting);
- }
- }
+ ALOGV("%s(%d)", __func__, muting);
+ auto apiLock = std::make_unique<std::lock_guard<std::mutex>>(mApiLock);
+ mStreamManager.forEach([=](soundpool::Stream *stream) { stream->mute(muting); });
}
-void SoundPool::autoResume()
+void SoundPool::pause(int32_t streamID)
{
- ALOGV("autoResume()");
- Mutex::Autolock lock(&mLock);
- for (int i = 0; i < mMaxChannels; ++i) {
- SoundChannel* channel = &mChannelPool[i];
- channel->autoResume();
+ ALOGV("%s(%d)", __func__, streamID);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->pause(streamID);
}
}
-void SoundPool::stop(int channelID)
+void SoundPool::resume(int32_t streamID)
{
- ALOGV("stop(%d)", channelID);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->stop();
- } else {
- channel = findNextChannel(channelID);
- if (channel)
- channel->clearNextEvent();
+ ALOGV("%s(%d)", __func__, streamID);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->resume(streamID);
}
}
-void SoundPool::setVolume(int channelID, float leftVolume, float rightVolume)
+void SoundPool::stop(int32_t streamID)
{
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->setVolume(leftVolume, rightVolume);
+ ALOGV("%s(%d)", __func__, streamID);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ soundpool::Stream* stream = mStreamManager.findStream(streamID);
+ if (stream != nullptr && stream->requestStop(streamID)) {
+ mStreamManager.moveToRestartQueue(stream);
}
}
-void SoundPool::setPriority(int channelID, int priority)
+void SoundPool::setVolume(int32_t streamID, float leftVolume, float rightVolume)
{
- ALOGV("setPriority(%d, %d)", channelID, priority);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->setPriority(priority);
+ ALOGV("%s(%d, %f %f)", __func__, streamID, leftVolume, rightVolume);
+ if (checkVolume(&leftVolume, &rightVolume)) return;
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->setVolume(streamID, leftVolume, rightVolume);
}
}
-void SoundPool::setLoop(int channelID, int loop)
+void SoundPool::setPriority(int32_t streamID, int32_t priority)
{
- ALOGV("setLoop(%d, %d)", channelID, loop);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->setLoop(loop);
+ ALOGV("%s(%d, %d)", __func__, streamID, priority);
+ if (checkPriority(&priority)) return;
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->setPriority(streamID, priority);
}
}
-void SoundPool::setRate(int channelID, float rate)
+void SoundPool::setLoop(int32_t streamID, int32_t loop)
{
- ALOGV("setRate(%d, %f)", channelID, rate);
- Mutex::Autolock lock(&mLock);
- SoundChannel* channel = findChannel(channelID);
- if (channel) {
- channel->setRate(rate);
+ ALOGV("%s(%d, %d)", __func__, streamID, loop);
+ if (checkLoop(&loop)) return;
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->setLoop(streamID, loop);
}
}
-// call with lock held
-void SoundPool::done_l(SoundChannel* channel)
+void SoundPool::setRate(int32_t streamID, float rate)
{
- ALOGV("done_l(%d)", channel->channelID());
- // if "stolen", play next event
- if (channel->nextChannelID() != 0) {
- ALOGV("add to restart list");
- addToRestartList(channel);
- }
-
- // return to idle state
- else {
- ALOGV("move to front");
- moveToFront_l(channel);
+ ALOGV("%s(%d, %f)", __func__, streamID, rate);
+ if (checkRate(&rate)) return;
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ if (soundpool::Stream* stream = mStreamManager.findStream(streamID)) {
+ stream->setRate(streamID, rate);
}
}
void SoundPool::setCallback(SoundPoolCallback* callback, void* user)
{
- Mutex::Autolock lock(&mCallbackLock);
- mCallback = callback;
- mUserData = user;
-}
-
-void SoundPool::notify(SoundPoolEvent event)
-{
- Mutex::Autolock lock(&mCallbackLock);
- if (mCallback != NULL) {
- mCallback(event, this, mUserData);
- }
-}
-
-void SoundPool::dump()
-{
- for (int i = 0; i < mMaxChannels; ++i) {
- mChannelPool[i].dump();
- }
-}
-
-
-Sample::Sample(int sampleID, int fd, int64_t offset, int64_t length)
-{
- init();
- mSampleID = sampleID;
- mFd = dup(fd);
- mOffset = offset;
- mLength = length;
- ALOGV("create sampleID=%d, fd=%d, offset=%" PRId64 " length=%" PRId64,
- mSampleID, mFd, mLength, mOffset);
-}
-
-void Sample::init()
-{
- mSize = 0;
- mRefCount = 0;
- mSampleID = 0;
- mState = UNLOADED;
- mFd = -1;
- mOffset = 0;
- mLength = 0;
-}
-
-Sample::~Sample()
-{
- ALOGV("Sample::destructor sampleID=%d, fd=%d", mSampleID, mFd);
- if (mFd > 0) {
- ALOGV("close(%d)", mFd);
- ::close(mFd);
- }
-}
-
-static status_t decode(int fd, int64_t offset, int64_t length,
- uint32_t *rate, int *numChannels, audio_format_t *audioFormat,
- audio_channel_mask_t *channelMask, sp<MemoryHeapBase> heap,
- size_t *memsize) {
-
- ALOGV("fd %d, offset %" PRId64 ", size %" PRId64, fd, offset, length);
- AMediaExtractor *ex = AMediaExtractor_new();
- status_t err = AMediaExtractor_setDataSourceFd(ex, fd, offset, length);
-
- if (err != AMEDIA_OK) {
- AMediaExtractor_delete(ex);
- return err;
- }
-
- *audioFormat = AUDIO_FORMAT_PCM_16_BIT;
-
- size_t numTracks = AMediaExtractor_getTrackCount(ex);
- for (size_t i = 0; i < numTracks; i++) {
- AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
- const char *mime;
- if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
- AMediaExtractor_delete(ex);
- AMediaFormat_delete(format);
- return UNKNOWN_ERROR;
- }
- if (strncmp(mime, "audio/", 6) == 0) {
-
- AMediaCodec *codec = AMediaCodec_createDecoderByType(mime);
- if (codec == NULL
- || AMediaCodec_configure(codec, format,
- NULL /* window */, NULL /* drm */, 0 /* flags */) != AMEDIA_OK
- || AMediaCodec_start(codec) != AMEDIA_OK
- || AMediaExtractor_selectTrack(ex, i) != AMEDIA_OK) {
- AMediaExtractor_delete(ex);
- AMediaCodec_delete(codec);
- AMediaFormat_delete(format);
- return UNKNOWN_ERROR;
- }
-
- bool sawInputEOS = false;
- bool sawOutputEOS = false;
- uint8_t* writePos = static_cast<uint8_t*>(heap->getBase());
- size_t available = heap->getSize();
- size_t written = 0;
-
- AMediaFormat_delete(format);
- format = AMediaCodec_getOutputFormat(codec);
-
- while (!sawOutputEOS) {
- if (!sawInputEOS) {
- ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec, 5000);
- ALOGV("input buffer %zd", bufidx);
- if (bufidx >= 0) {
- size_t bufsize;
- uint8_t *buf = AMediaCodec_getInputBuffer(codec, bufidx, &bufsize);
- if (buf == nullptr) {
- ALOGE("AMediaCodec_getInputBuffer returned nullptr, short decode");
- break;
- }
- int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
- ALOGV("read %d", sampleSize);
- if (sampleSize < 0) {
- sampleSize = 0;
- sawInputEOS = true;
- ALOGV("EOS");
- }
- int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
-
- media_status_t mstatus = AMediaCodec_queueInputBuffer(codec, bufidx,
- 0 /* offset */, sampleSize, presentationTimeUs,
- sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
- if (mstatus != AMEDIA_OK) {
- // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
- ALOGE("AMediaCodec_queueInputBuffer returned status %d, short decode",
- (int)mstatus);
- break;
- }
- (void)AMediaExtractor_advance(ex);
- }
- }
-
- AMediaCodecBufferInfo info;
- int status = AMediaCodec_dequeueOutputBuffer(codec, &info, 1);
- ALOGV("dequeueoutput returned: %d", status);
- if (status >= 0) {
- if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
- ALOGV("output EOS");
- sawOutputEOS = true;
- }
- ALOGV("got decoded buffer size %d", info.size);
-
- uint8_t *buf = AMediaCodec_getOutputBuffer(codec, status, NULL /* out_size */);
- if (buf == nullptr) {
- ALOGE("AMediaCodec_getOutputBuffer returned nullptr, short decode");
- break;
- }
- size_t dataSize = info.size;
- if (dataSize > available) {
- dataSize = available;
- }
- memcpy(writePos, buf + info.offset, dataSize);
- writePos += dataSize;
- written += dataSize;
- available -= dataSize;
- media_status_t mstatus = AMediaCodec_releaseOutputBuffer(
- codec, status, false /* render */);
- if (mstatus != AMEDIA_OK) {
- // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
- ALOGE("AMediaCodec_releaseOutputBuffer returned status %d, short decode",
- (int)mstatus);
- break;
- }
- if (available == 0) {
- // there might be more data, but there's no space for it
- sawOutputEOS = true;
- }
- } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
- ALOGV("output buffers changed");
- } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
- AMediaFormat_delete(format);
- format = AMediaCodec_getOutputFormat(codec);
- ALOGV("format changed to: %s", AMediaFormat_toString(format));
- } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
- ALOGV("no output buffer right now");
- } else if (status <= AMEDIA_ERROR_BASE) {
- ALOGE("decode error: %d", status);
- break;
- } else {
- ALOGV("unexpected info code: %d", status);
- }
- }
-
- (void)AMediaCodec_stop(codec);
- (void)AMediaCodec_delete(codec);
- (void)AMediaExtractor_delete(ex);
- if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) rate) ||
- !AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, numChannels)) {
- (void)AMediaFormat_delete(format);
- return UNKNOWN_ERROR;
- }
- if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_MASK,
- (int32_t*) channelMask)) {
- *channelMask = AUDIO_CHANNEL_NONE;
- }
- (void)AMediaFormat_delete(format);
- *memsize = written;
- return OK;
- }
- (void)AMediaFormat_delete(format);
- }
- (void)AMediaExtractor_delete(ex);
- return UNKNOWN_ERROR;
-}
-
-status_t Sample::doLoad()
-{
- uint32_t sampleRate;
- int numChannels;
- audio_format_t format;
- audio_channel_mask_t channelMask;
- status_t status;
- mHeap = new MemoryHeapBase(kDefaultHeapSize);
-
- ALOGV("Start decode");
- status = decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format,
- &channelMask, mHeap, &mSize);
- ALOGV("close(%d)", mFd);
- ::close(mFd);
- mFd = -1;
- if (status != NO_ERROR) {
- ALOGE("Unable to load sample");
- goto error;
- }
- ALOGV("pointer = %p, size = %zu, sampleRate = %u, numChannels = %d",
- mHeap->getBase(), mSize, sampleRate, numChannels);
-
- if (sampleRate > kMaxSampleRate) {
- ALOGE("Sample rate (%u) out of range", sampleRate);
- status = BAD_VALUE;
- goto error;
- }
-
- if ((numChannels < 1) || (numChannels > FCC_8)) {
- ALOGE("Sample channel count (%d) out of range", numChannels);
- status = BAD_VALUE;
- goto error;
- }
-
- mData = new MemoryBase(mHeap, 0, mSize);
- mSampleRate = sampleRate;
- mNumChannels = numChannels;
- mFormat = format;
- mChannelMask = channelMask;
- mState = READY;
- return NO_ERROR;
-
-error:
- mHeap.clear();
- return status;
-}
-
-
-void SoundChannel::init(SoundPool* soundPool)
-{
- mSoundPool = soundPool;
- mPrevSampleID = -1;
-}
-
-// call with sound pool lock held
-void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume,
- float rightVolume, int priority, int loop, float rate)
-{
- sp<AudioTrack> oldTrack;
- sp<AudioTrack> newTrack;
- status_t status = NO_ERROR;
-
- { // scope for the lock
- Mutex::Autolock lock(&mLock);
-
- ALOGV("SoundChannel::play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f,"
- " priority=%d, loop=%d, rate=%f",
- this, sample->sampleID(), nextChannelID, leftVolume, rightVolume,
- priority, loop, rate);
-
- // if not idle, this voice is being stolen
- if (mState != IDLE) {
- ALOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID);
- mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
- stop_l();
- return;
- }
-
- // initialize track
- size_t afFrameCount;
- uint32_t afSampleRate;
- audio_stream_type_t streamType =
- AudioSystem::attributesToStreamType(*mSoundPool->attributes());
- if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
- afFrameCount = kDefaultFrameCount;
- }
- if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
- afSampleRate = kDefaultSampleRate;
- }
- int numChannels = sample->numChannels();
- uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5);
- size_t frameCount = 0;
-
- if (loop) {
- const audio_format_t format = sample->format();
- const size_t frameSize = audio_is_linear_pcm(format)
- ? numChannels * audio_bytes_per_sample(format) : 1;
- frameCount = sample->size() / frameSize;
- }
-
-#ifndef USE_SHARED_MEM_BUFFER
- uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate;
- // Ensure minimum audio buffer size in case of short looped sample
- if(frameCount < totalFrames) {
- frameCount = totalFrames;
- }
-#endif
-
- // check if the existing track has the same sample id.
- if (mAudioTrack != 0 && mPrevSampleID == sample->sampleID()) {
- // the sample rate may fail to change if the audio track is a fast track.
- if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
- newTrack = mAudioTrack;
- ALOGV("reusing track %p for sample %d", mAudioTrack.get(), sample->sampleID());
- }
- }
- if (newTrack == 0) {
- // mToggle toggles each time a track is started on a given channel.
- // The toggle is concatenated with the SoundChannel address and passed to AudioTrack
- // as callback user data. This enables the detection of callbacks received from the old
- // audio track while the new one is being started and avoids processing them with
- // wrong audio audio buffer size (mAudioBufferSize)
- unsigned long toggle = mToggle ^ 1;
- void *userData = (void *)((unsigned long)this | toggle);
- audio_channel_mask_t sampleChannelMask = sample->channelMask();
- // When sample contains a not none channel mask, use it as is.
- // Otherwise, use channel count to calculate channel mask.
- audio_channel_mask_t channelMask = sampleChannelMask != AUDIO_CHANNEL_NONE
- ? sampleChannelMask : audio_channel_out_mask_from_count(numChannels);
-
- // do not create a new audio track if current track is compatible with sample parameters
- #ifdef USE_SHARED_MEM_BUFFER
- newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
- channelMask, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData,
- 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
- AudioTrack::TRANSFER_DEFAULT,
- NULL /*offloadInfo*/, -1 /*uid*/, -1 /*pid*/, mSoundPool->attributes());
- #else
- uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount;
- newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
- channelMask, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData,
- bufferFrames, AUDIO_SESSION_ALLOCATE, AudioTrack::TRANSFER_DEFAULT,
- NULL /*offloadInfo*/, -1 /*uid*/, -1 /*pid*/, mSoundPool->attributes());
- #endif
- oldTrack = mAudioTrack;
- status = newTrack->initCheck();
- if (status != NO_ERROR) {
- ALOGE("Error creating AudioTrack");
- // newTrack goes out of scope, so reference count drops to zero
- goto exit;
- }
- // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
- mToggle = toggle;
- mAudioTrack = newTrack;
- ALOGV("using new track %p for sample %d", newTrack.get(), sample->sampleID());
- }
- if (mMuted) {
- newTrack->setVolume(0.0f, 0.0f);
- } else {
- newTrack->setVolume(leftVolume, rightVolume);
- }
- newTrack->setLoop(0, frameCount, loop);
- mPos = 0;
- mSample = sample;
- mChannelID = nextChannelID;
- mPriority = priority;
- mLoop = loop;
- mLeftVolume = leftVolume;
- mRightVolume = rightVolume;
- mNumChannels = numChannels;
- mRate = rate;
- clearNextEvent();
- mState = PLAYING;
- mAudioTrack->start();
- mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize();
- }
-
-exit:
- ALOGV("delete oldTrack %p", oldTrack.get());
- if (status != NO_ERROR) {
- mAudioTrack.clear();
- }
-}
-
-void SoundChannel::nextEvent()
-{
- sp<Sample> sample;
- int nextChannelID;
- float leftVolume;
- float rightVolume;
- int priority;
- int loop;
- float rate;
-
- // check for valid event
- {
- Mutex::Autolock lock(&mLock);
- nextChannelID = mNextEvent.channelID();
- if (nextChannelID == 0) {
- ALOGV("stolen channel has no event");
- return;
- }
-
- sample = mNextEvent.sample();
- leftVolume = mNextEvent.leftVolume();
- rightVolume = mNextEvent.rightVolume();
- priority = mNextEvent.priority();
- loop = mNextEvent.loop();
- rate = mNextEvent.rate();
- }
-
- ALOGV("Starting stolen channel %d -> %d", channelID(), nextChannelID);
- play(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
-}
-
-void SoundChannel::callback(int event, void* user, void *info)
-{
- SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1));
-
- channel->process(event, info, (unsigned long)user & 1);
-}
-
-void SoundChannel::process(int event, void *info, unsigned long toggle)
-{
- //ALOGV("process(%d)", mChannelID);
-
- Mutex::Autolock lock(&mLock);
-
- AudioTrack::Buffer* b = NULL;
- if (event == AudioTrack::EVENT_MORE_DATA) {
- b = static_cast<AudioTrack::Buffer *>(info);
- }
-
- if (mToggle != toggle) {
- ALOGV("process wrong toggle %p channel %d", this, mChannelID);
- if (b != NULL) {
- b->size = 0;
- }
- return;
- }
-
- sp<Sample> sample = mSample;
-
-// ALOGV("SoundChannel::process event %d", event);
-
- if (event == AudioTrack::EVENT_MORE_DATA) {
-
- // check for stop state
- if (b->size == 0) return;
-
- if (mState == IDLE) {
- b->size = 0;
- return;
- }
-
- if (sample != 0) {
- // fill buffer
- uint8_t* q = (uint8_t*) b->i8;
- size_t count = 0;
-
- if (mPos < (int)sample->size()) {
- uint8_t* p = sample->data() + mPos;
- count = sample->size() - mPos;
- if (count > b->size) {
- count = b->size;
- }
- memcpy(q, p, count);
-// ALOGV("fill: q=%p, p=%p, mPos=%u, b->size=%u, count=%d", q, p, mPos, b->size,
-// count);
- } else if (mPos < mAudioBufferSize) {
- count = mAudioBufferSize - mPos;
- if (count > b->size) {
- count = b->size;
- }
- memset(q, 0, count);
-// ALOGV("fill extra: q=%p, mPos=%u, b->size=%u, count=%d", q, mPos, b->size, count);
- }
-
- mPos += count;
- b->size = count;
- //ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]);
- }
- } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END) {
- ALOGV("process %p channel %d event %s",
- this, mChannelID, (event == AudioTrack::EVENT_UNDERRUN) ? "UNDERRUN" :
- "BUFFER_END");
- // Only BUFFER_END should happen as we use static tracks.
- setVolume_l(0.f, 0.f); // set volume to 0 to indicate no need to ramp volume down.
- mSoundPool->addToStopList(this);
- } else if (event == AudioTrack::EVENT_LOOP_END) {
- ALOGV("End loop %p channel %d", this, mChannelID);
- } else if (event == AudioTrack::EVENT_NEW_IAUDIOTRACK) {
- ALOGV("process %p channel %d NEW_IAUDIOTRACK", this, mChannelID);
- } else {
- ALOGW("SoundChannel::process unexpected event %d", event);
- }
-}
-
-
-// call with lock held
-bool SoundChannel::doStop_l()
-{
- if (mState != IDLE) {
- ALOGV("stop");
- if (mLeftVolume != 0.f || mRightVolume != 0.f) {
- setVolume_l(0.f, 0.f);
- if (mSoundPool->attributes()->usage != AUDIO_USAGE_GAME) {
- // Since we're forcibly halting the previously playing content,
- // we sleep here to ensure the volume is ramped down before we stop the track.
- // Ideally the sleep time is the mixer period, or an approximation thereof
- // (Fast vs Normal tracks are different).
- ALOGV("sleeping: ChannelID:%d SampleID:%d", mChannelID, mSample->sampleID());
- std::this_thread::sleep_for(std::chrono::milliseconds(20));
- }
- }
- mAudioTrack->stop();
- mPrevSampleID = mSample->sampleID();
- mSample.clear();
- mState = IDLE;
- mPriority = IDLE_PRIORITY;
- return true;
- }
- return false;
-}
-
-// call with lock held and sound pool lock held
-void SoundChannel::stop_l()
-{
- if (doStop_l()) {
- mSoundPool->done_l(this);
- }
-}
-
-// call with sound pool lock held
-void SoundChannel::stop()
-{
- bool stopped;
- {
- Mutex::Autolock lock(&mLock);
- stopped = doStop_l();
- }
-
- if (stopped) {
- mSoundPool->done_l(this);
- }
-}
-
-//FIXME: Pause is a little broken right now
-void SoundChannel::pause()
-{
- Mutex::Autolock lock(&mLock);
- if (mState == PLAYING) {
- ALOGV("pause track");
- mState = PAUSED;
- mAudioTrack->pause();
- }
-}
-
-void SoundChannel::autoPause()
-{
- Mutex::Autolock lock(&mLock);
- if (mState == PLAYING) {
- ALOGV("pause track");
- mState = PAUSED;
- mAutoPaused = true;
- mAudioTrack->pause();
- }
-}
-
-void SoundChannel::resume()
-{
- Mutex::Autolock lock(&mLock);
- if (mState == PAUSED) {
- ALOGV("resume track");
- mState = PLAYING;
- mAutoPaused = false;
- mAudioTrack->start();
- }
-}
-
-void SoundChannel::autoResume()
-{
- Mutex::Autolock lock(&mLock);
- if (mAutoPaused && (mState == PAUSED)) {
- ALOGV("resume track");
- mState = PLAYING;
- mAutoPaused = false;
- mAudioTrack->start();
- }
-}
-
-void SoundChannel::setRate(float rate)
-{
- Mutex::Autolock lock(&mLock);
- if (mAudioTrack != NULL && mSample != 0) {
- uint32_t sampleRate = uint32_t(float(mSample->sampleRate()) * rate + 0.5);
- mAudioTrack->setSampleRate(sampleRate);
- mRate = rate;
- }
-}
-
-// call with lock held
-void SoundChannel::setVolume_l(float leftVolume, float rightVolume)
-{
- mLeftVolume = leftVolume;
- mRightVolume = rightVolume;
- if (mAudioTrack != NULL && !mMuted)
- mAudioTrack->setVolume(leftVolume, rightVolume);
-}
-
-void SoundChannel::setVolume(float leftVolume, float rightVolume)
-{
- Mutex::Autolock lock(&mLock);
- setVolume_l(leftVolume, rightVolume);
-}
-
-void SoundChannel::mute(bool muting)
-{
- Mutex::Autolock lock(&mLock);
- mMuted = muting;
- if (mAudioTrack != NULL) {
- if (mMuted) {
- mAudioTrack->setVolume(0.0f, 0.0f);
- } else {
- mAudioTrack->setVolume(mLeftVolume, mRightVolume);
- }
- }
-}
-
-void SoundChannel::setLoop(int loop)
-{
- Mutex::Autolock lock(&mLock);
- if (mAudioTrack != NULL && mSample != 0) {
- uint32_t loopEnd = mSample->size()/mNumChannels/
- ((mSample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
- mAudioTrack->setLoop(0, loopEnd, loop);
- mLoop = loop;
- }
-}
-
-SoundChannel::~SoundChannel()
-{
- ALOGV("SoundChannel destructor %p", this);
- {
- Mutex::Autolock lock(&mLock);
- clearNextEvent();
- doStop_l();
- }
- // do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack
- // callback thread to exit which may need to execute process() and acquire the mLock.
- mAudioTrack.clear();
-}
-
-void SoundChannel::dump()
-{
- ALOGV("mState = %d mChannelID=%d, mNumChannels=%d, mPos = %d, mPriority=%d, mLoop=%d",
- mState, mChannelID, mNumChannels, mPos, mPriority, mLoop);
+ ALOGV("%s(%p, %p)", __func__, callback, user);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ mSoundManager.setCallback(this, callback, user);
}
-void SoundEvent::set(const sp<Sample>& sample, int channelID, float leftVolume,
- float rightVolume, int priority, int loop, float rate)
+void* SoundPool::getUserData() const
{
- mSample = sample;
- mChannelID = channelID;
- mLeftVolume = leftVolume;
- mRightVolume = rightVolume;
- mPriority = priority;
- mLoop = loop;
- mRate =rate;
+ ALOGV("%s()", __func__);
+ auto apiLock = kUseApiLock ? std::make_unique<std::lock_guard<std::mutex>>(mApiLock) : nullptr;
+ return mSoundManager.getUserData();
}
} // end namespace android
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
index 01e4faae6f6c..d5b16ef629cd 100644
--- a/media/jni/soundpool/SoundPool.h
+++ b/media/jni/soundpool/SoundPool.h
@@ -14,227 +14,59 @@
* limitations under the License.
*/
-#ifndef SOUNDPOOL_H_
-#define SOUNDPOOL_H_
+#pragma once
-#include <utils/threads.h>
-#include <utils/List.h>
-#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-#include <media/AudioTrack.h>
-#include <binder/MemoryHeapBase.h>
-#include <binder/MemoryBase.h>
+#include "SoundManager.h"
+#include "StreamManager.h"
namespace android {
-static const int IDLE_PRIORITY = -1;
-
-// forward declarations
-class SoundEvent;
-class SoundPoolThread;
-class SoundPool;
-
-// for queued events
-class SoundPoolEvent {
-public:
- explicit SoundPoolEvent(int msg, int arg1=0, int arg2=0) :
- mMsg(msg), mArg1(arg1), mArg2(arg2) {}
- int mMsg;
- int mArg1;
- int mArg2;
- enum MessageType { INVALID, SAMPLE_LOADED };
-};
-
-// callback function prototype
-typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user);
-
-// tracks samples used by application
-class Sample : public RefBase {
-public:
- enum sample_state { UNLOADED, LOADING, READY, UNLOADING };
- Sample(int sampleID, int fd, int64_t offset, int64_t length);
- ~Sample();
- int sampleID() { return mSampleID; }
- int numChannels() { return mNumChannels; }
- int sampleRate() { return mSampleRate; }
- audio_format_t format() { return mFormat; }
- audio_channel_mask_t channelMask() { return mChannelMask; }
- size_t size() { return mSize; }
- int state() { return mState; }
- uint8_t* data() { return static_cast<uint8_t*>(mData->unsecurePointer()); }
- status_t doLoad();
- void startLoad() { mState = LOADING; }
- sp<IMemory> getIMemory() { return mData; }
-
-private:
- void init();
-
- size_t mSize;
- volatile int32_t mRefCount;
- uint16_t mSampleID;
- uint16_t mSampleRate;
- uint8_t mState;
- uint8_t mNumChannels;
- audio_format_t mFormat;
- audio_channel_mask_t mChannelMask;
- int mFd;
- int64_t mOffset;
- int64_t mLength;
- sp<IMemory> mData;
- sp<MemoryHeapBase> mHeap;
-};
-
-// stores pending events for stolen channels
-class SoundEvent
-{
-public:
- SoundEvent() : mChannelID(0), mLeftVolume(0), mRightVolume(0),
- mPriority(IDLE_PRIORITY), mLoop(0), mRate(0) {}
- void set(const sp<Sample>& sample, int channelID, float leftVolume,
- float rightVolume, int priority, int loop, float rate);
- sp<Sample> sample() { return mSample; }
- int channelID() { return mChannelID; }
- float leftVolume() { return mLeftVolume; }
- float rightVolume() { return mRightVolume; }
- int priority() { return mPriority; }
- int loop() { return mLoop; }
- float rate() { return mRate; }
- void clear() { mChannelID = 0; mSample.clear(); }
-
-protected:
- sp<Sample> mSample;
- int mChannelID;
- float mLeftVolume;
- float mRightVolume;
- int mPriority;
- int mLoop;
- float mRate;
-};
-
-// for channels aka AudioTracks
-class SoundChannel : public SoundEvent {
-public:
- enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING };
- SoundChannel() : mState(IDLE), mNumChannels(1),
- mPos(0), mToggle(0), mAutoPaused(false), mMuted(false) {}
- ~SoundChannel();
- void init(SoundPool* soundPool);
- void play(const sp<Sample>& sample, int channelID, float leftVolume, float rightVolume,
- int priority, int loop, float rate);
- void setVolume_l(float leftVolume, float rightVolume);
- void setVolume(float leftVolume, float rightVolume);
- void mute(bool muting);
- void stop_l();
- void stop();
- void pause();
- void autoPause();
- void resume();
- void autoResume();
- void setRate(float rate);
- int state() { return mState; }
- void setPriority(int priority) { mPriority = priority; }
- void setLoop(int loop);
- int numChannels() { return mNumChannels; }
- void clearNextEvent() { mNextEvent.clear(); }
- void nextEvent();
- int nextChannelID() { return mNextEvent.channelID(); }
- void dump();
- int getPrevSampleID(void) { return mPrevSampleID; }
-
-private:
- static void callback(int event, void* user, void *info);
- void process(int event, void *info, unsigned long toggle);
- bool doStop_l();
-
- SoundPool* mSoundPool;
- sp<AudioTrack> mAudioTrack;
- SoundEvent mNextEvent;
- Mutex mLock;
- int mState;
- int mNumChannels;
- int mPos;
- int mAudioBufferSize;
- unsigned long mToggle;
- bool mAutoPaused;
- int mPrevSampleID;
- bool mMuted;
-};
-
-// application object for managing a pool of sounds
+/**
+ * Native class for Java SoundPool, manages a pool of sounds.
+ *
+ * See the Android SoundPool Java documentation for description of valid values.
+ * https://developer.android.com/reference/android/media/SoundPool
+ */
class SoundPool {
- friend class SoundPoolThread;
- friend class SoundChannel;
public:
- SoundPool(int maxChannels, const audio_attributes_t* pAttributes);
+ SoundPool(int32_t maxStreams, const audio_attributes_t* attributes);
~SoundPool();
- int load(int fd, int64_t offset, int64_t length, int priority);
- bool unload(int sampleID);
- int play(int sampleID, float leftVolume, float rightVolume, int priority,
- int loop, float rate);
- void pause(int channelID);
- void mute(bool muting);
+
+ // SoundPool Java API support
+ int32_t load(int fd, int64_t offset, int64_t length, int32_t priority);
+ bool unload(int32_t soundID);
+ int32_t play(int32_t soundID, float leftVolume, float rightVolume, int32_t priority,
+ int32_t loop, float rate);
+ void pause(int32_t streamID);
void autoPause();
- void resume(int channelID);
+ void resume(int32_t streamID);
void autoResume();
- void stop(int channelID);
- void setVolume(int channelID, float leftVolume, float rightVolume);
- void setPriority(int channelID, int priority);
- void setLoop(int channelID, int loop);
- void setRate(int channelID, float rate);
- const audio_attributes_t* attributes() { return &mAttributes; }
-
- // called from SoundPoolThread
- void sampleLoaded(int sampleID);
- sp<Sample> findSample(int sampleID);
-
- // called from AudioTrack thread
- void done_l(SoundChannel* channel);
-
- // callback function
+ void stop(int32_t streamID);
+ void setVolume(int32_t streamID, float leftVolume, float rightVolume);
+ void setPriority(int32_t streamID, int32_t priority);
+ void setLoop(int32_t streamID, int32_t loop);
+ void setRate(int32_t streamID, float rate);
void setCallback(SoundPoolCallback* callback, void* user);
- void* getUserData() { return mUserData; }
+ void* getUserData() const;
-private:
- SoundPool() {} // no default constructor
- bool startThreads();
- sp<Sample> findSample_l(int sampleID);
- SoundChannel* findChannel (int channelID);
- SoundChannel* findNextChannel (int channelID);
- SoundChannel* allocateChannel_l(int priority, int sampleID);
- void moveToFront_l(SoundChannel* channel);
- void notify(SoundPoolEvent event);
- void dump();
-
- // restart thread
- void addToRestartList(SoundChannel* channel);
- void addToStopList(SoundChannel* channel);
- static int beginThread(void* arg);
- int run();
- void quit();
+ // not exposed in the public Java API, used for internal playerSetVolume() muting.
+ void mute(bool muting);
- Mutex mLock;
- Mutex mRestartLock;
- Condition mCondition;
- SoundPoolThread* mDecodeThread;
- SoundChannel* mChannelPool;
- List<SoundChannel*> mChannels;
- List<SoundChannel*> mRestart;
- List<SoundChannel*> mStop;
- DefaultKeyedVector< int, sp<Sample> > mSamples;
- int mMaxChannels;
- audio_attributes_t mAttributes;
- int mAllocated;
- int mNextSampleID;
- int mNextChannelID;
- bool mQuit;
- bool mMuted;
+private:
- // callback
- Mutex mCallbackLock;
- SoundPoolCallback* mCallback;
- void* mUserData;
+ // Constructor initialized variables
+ // Can access without lock as they are internally locked,
+ // though care needs to be taken that the final result composed of
+ // individually consistent actions are consistent.
+ soundpool::SoundManager mSoundManager;
+ soundpool::StreamManager mStreamManager;
+
+ // mApiLock serializes SoundPool application calls (configurable by kUseApiLock).
+ // It only locks at the SoundPool layer and not below. At this level,
+ // mApiLock is only required for autoPause() and autoResume() to prevent zippering
+ // of the individual pauses and resumes, and mute() for self-interaction with itself.
+ // It is optional for all other apis.
+ mutable std::mutex mApiLock;
};
} // end namespace android
-
-#endif /*SOUNDPOOL_H_*/
diff --git a/media/jni/soundpool/SoundPoolThread.cpp b/media/jni/soundpool/SoundPoolThread.cpp
deleted file mode 100644
index ba3b482935dd..000000000000
--- a/media/jni/soundpool/SoundPoolThread.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "SoundPoolThread"
-#include "utils/Log.h"
-
-#include "SoundPoolThread.h"
-
-namespace android {
-
-void SoundPoolThread::write(SoundPoolMsg msg) {
- Mutex::Autolock lock(&mLock);
- while (mMsgQueue.size() >= maxMessages) {
- mCondition.wait(mLock);
- }
-
- // if thread is quitting, don't add to queue
- if (mRunning) {
- mMsgQueue.push(msg);
- mCondition.signal();
- }
-}
-
-const SoundPoolMsg SoundPoolThread::read() {
- Mutex::Autolock lock(&mLock);
- while (mMsgQueue.size() == 0) {
- mCondition.wait(mLock);
- }
- SoundPoolMsg msg = mMsgQueue[0];
- mMsgQueue.removeAt(0);
- mCondition.signal();
- return msg;
-}
-
-void SoundPoolThread::quit() {
- Mutex::Autolock lock(&mLock);
- if (mRunning) {
- mRunning = false;
- mMsgQueue.clear();
- mMsgQueue.push(SoundPoolMsg(SoundPoolMsg::KILL, 0));
- mCondition.signal();
- mCondition.wait(mLock);
- }
- ALOGV("return from quit");
-}
-
-SoundPoolThread::SoundPoolThread(SoundPool* soundPool) :
- mSoundPool(soundPool)
-{
- mMsgQueue.setCapacity(maxMessages);
- if (createThreadEtc(beginThread, this, "SoundPoolThread")) {
- mRunning = true;
- }
-}
-
-SoundPoolThread::~SoundPoolThread()
-{
- quit();
-}
-
-int SoundPoolThread::beginThread(void* arg) {
- ALOGV("beginThread");
- SoundPoolThread* soundPoolThread = (SoundPoolThread*)arg;
- return soundPoolThread->run();
-}
-
-int SoundPoolThread::run() {
- ALOGV("run");
- for (;;) {
- SoundPoolMsg msg = read();
- ALOGV("Got message m=%d, mData=%d", msg.mMessageType, msg.mData);
- switch (msg.mMessageType) {
- case SoundPoolMsg::KILL:
- ALOGV("goodbye");
- return NO_ERROR;
- case SoundPoolMsg::LOAD_SAMPLE:
- doLoadSample(msg.mData);
- break;
- default:
- ALOGW("run: Unrecognized message %d\n",
- msg.mMessageType);
- break;
- }
- }
-}
-
-void SoundPoolThread::loadSample(int sampleID) {
- write(SoundPoolMsg(SoundPoolMsg::LOAD_SAMPLE, sampleID));
-}
-
-void SoundPoolThread::doLoadSample(int sampleID) {
- sp <Sample> sample = mSoundPool->findSample(sampleID);
- status_t status = -1;
- if (sample != 0) {
- status = sample->doLoad();
- }
- mSoundPool->notify(SoundPoolEvent(SoundPoolEvent::SAMPLE_LOADED, sampleID, status));
-}
-
-} // end namespace android
diff --git a/media/jni/soundpool/SoundPoolThread.h b/media/jni/soundpool/SoundPoolThread.h
deleted file mode 100644
index 7b3e1dda0a23..000000000000
--- a/media/jni/soundpool/SoundPoolThread.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#ifndef SOUNDPOOLTHREAD_H_
-#define SOUNDPOOLTHREAD_H_
-
-#include <utils/threads.h>
-#include <utils/Vector.h>
-#include <media/AudioTrack.h>
-
-#include "SoundPool.h"
-
-namespace android {
-
-class SoundPoolMsg {
-public:
- enum MessageType { INVALID, KILL, LOAD_SAMPLE };
- SoundPoolMsg() : mMessageType(INVALID), mData(0) {}
- SoundPoolMsg(MessageType MessageType, int data) :
- mMessageType(MessageType), mData(data) {}
- uint16_t mMessageType;
- uint16_t mData;
-};
-
-/*
- * This class handles background requests from the SoundPool
- */
-class SoundPoolThread {
-public:
- explicit SoundPoolThread(SoundPool* SoundPool);
- ~SoundPoolThread();
- void loadSample(int sampleID);
- void quit();
- void write(SoundPoolMsg msg);
-
-private:
- static const size_t maxMessages = 128;
-
- static int beginThread(void* arg);
- int run();
- void doLoadSample(int sampleID);
- const SoundPoolMsg read();
-
- Mutex mLock;
- Condition mCondition;
- Vector<SoundPoolMsg> mMsgQueue;
- SoundPool* mSoundPool;
- bool mRunning;
-};
-
-} // end namespace android
-
-#endif /*SOUNDPOOLTHREAD_H_*/
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
new file mode 100644
index 000000000000..e7d4d9093bb8
--- /dev/null
+++ b/media/jni/soundpool/Stream.cpp
@@ -0,0 +1,448 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool::Stream"
+#include <utils/Log.h>
+
+#include "Stream.h"
+
+#include "StreamManager.h"
+
+namespace android::soundpool {
+
+Stream::~Stream()
+{
+ ALOGV("%s(%p)", __func__, this);
+}
+
+void Stream::autoPause()
+{
+ std::lock_guard lock(mLock);
+ if (mState == PLAYING) {
+ ALOGV("%s: track streamID: %d", __func__, (int)mStreamID);
+ mState = PAUSED;
+ mAutoPaused = true;
+ if (mAudioTrack != nullptr) {
+ mAudioTrack->pause();
+ }
+ }
+}
+
+void Stream::autoResume()
+{
+ std::lock_guard lock(mLock);
+ if (mAutoPaused) {
+ if (mState == PAUSED) {
+ ALOGV("%s: track streamID: %d", __func__, (int)mStreamID);
+ mState = PLAYING;
+ if (mAudioTrack != nullptr) {
+ mAudioTrack->start();
+ }
+ }
+ mAutoPaused = false; // New for R: always reset autopause (consistent with API spec).
+ }
+}
+
+void Stream::mute(bool muting)
+{
+ std::lock_guard lock(mLock);
+ mMuted = muting;
+ if (mAudioTrack != nullptr) {
+ if (mMuted) {
+ mAudioTrack->setVolume(0.0f, 0.0f);
+ } else {
+ mAudioTrack->setVolume(mLeftVolume, mRightVolume);
+ }
+ }
+}
+
+void Stream::pause(int32_t streamID)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ if (mState == PLAYING) {
+ ALOGV("%s: track streamID: %d", __func__, streamID);
+ mState = PAUSED;
+ if (mAudioTrack != nullptr) {
+ mAudioTrack->pause();
+ }
+ }
+ }
+}
+
+void Stream::resume(int32_t streamID)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ if (mState == PAUSED) {
+ ALOGV("%s: track streamID: %d", __func__, streamID);
+ mState = PLAYING;
+ if (mAudioTrack != nullptr) {
+ mAudioTrack->start();
+ }
+ mAutoPaused = false; // TODO: is this right? (ambiguous per spec), move outside?
+ }
+ }
+}
+
+void Stream::setRate(int32_t streamID, float rate)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ mRate = rate;
+ if (mAudioTrack != nullptr && mSound != nullptr) {
+ const uint32_t sampleRate = uint32_t(float(mSound->getSampleRate()) * rate + 0.5);
+ mAudioTrack->setSampleRate(sampleRate);
+ }
+ }
+}
+
+void Stream::setVolume_l(float leftVolume, float rightVolume)
+{
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ if (mAudioTrack != nullptr && !mMuted) {
+ mAudioTrack->setVolume(leftVolume, rightVolume);
+ }
+}
+
+void Stream::setVolume(int32_t streamID, float leftVolume, float rightVolume)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ setVolume_l(leftVolume, rightVolume);
+ }
+}
+
+void Stream::setPriority(int32_t streamID, int32_t priority)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ mPriority = priority;
+ }
+}
+
+void Stream::setLoop(int32_t streamID, int32_t loop)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ if (mAudioTrack != nullptr && mSound != nullptr) {
+ const uint32_t loopEnd = mSound->getSizeInBytes() / mSound->getChannelCount() /
+ (mSound->getFormat() == AUDIO_FORMAT_PCM_16_BIT
+ ? sizeof(int16_t) : sizeof(uint8_t));
+ mAudioTrack->setLoop(0, loopEnd, loop);
+ }
+ mLoop = loop;
+ }
+}
+
+void Stream::setPlay(
+ int32_t streamID, const std::shared_ptr<Sound> &sound, int32_t soundID,
+ float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate)
+{
+ std::lock_guard lock(mLock);
+ // We must be idle, or we must be repurposing a pending Stream.
+ LOG_ALWAYS_FATAL_IF(mState != IDLE && mAudioTrack != nullptr, "State %d must be IDLE", mState);
+ mSound = sound;
+ mSoundID = soundID;
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ mPriority = priority;
+ mLoop = loop;
+ mRate = rate;
+ mState = PLAYING;
+ mAutoPaused = false; // New for R (consistent with Java API spec).
+ mStreamID = streamID; // prefer this to be the last, as it is an atomic sync point
+}
+
+void Stream::setStopTimeNs(int64_t stopTimeNs)
+{
+ std::lock_guard lock(mLock);
+ mStopTimeNs = stopTimeNs;
+}
+
+bool Stream::requestStop(int32_t streamID)
+{
+ std::lock_guard lock(mLock);
+ if (streamID == mStreamID) {
+ if (mAudioTrack != nullptr) {
+ if (mState == PLAYING && !mMuted && (mLeftVolume != 0.f || mRightVolume != 0.f)) {
+ setVolume_l(0.f, 0.f);
+ mStopTimeNs = systemTime() + kStopWaitTimeNs;
+ } else {
+ mStopTimeNs = systemTime();
+ }
+ return true; // must be queued on the restart list.
+ }
+ stop_l();
+ }
+ return false;
+}
+
+void Stream::stop()
+{
+ std::lock_guard lock(mLock);
+ stop_l();
+}
+
+void Stream::stop_l()
+{
+ if (mState != IDLE) {
+ if (mAudioTrack != nullptr) {
+ mAudioTrack->stop();
+ }
+ mSound.reset();
+ mState = IDLE;
+ }
+}
+
+void Stream::clearAudioTrack()
+{
+ // This will invoke the destructor which waits for the AudioTrack thread to join,
+ // and is currently the only safe way to ensure there are no callbacks afterwards.
+ mAudioTrack.clear();
+}
+
+Stream* Stream::getPairStream() const
+{
+ return mStreamManager->getPairStream(this);
+}
+
+Stream* Stream::playPairStream() {
+ Stream* pairStream = getPairStream();
+ LOG_ALWAYS_FATAL_IF(pairStream == nullptr, "No pair stream!");
+ sp<AudioTrack> releaseTracks[2];
+ {
+ // TODO: Do we really want to force a simultaneous synchronization between
+ // the stream and its pair?
+
+ // note locking order - the paired stream is obtained before the queued stream.
+ // we can invert the locking order, but it is slightly more optimal to do it this way.
+ std::lock_guard lockp(pairStream->mLock);
+ if (pairStream->mSound == nullptr) {
+ return nullptr; // no pair sound
+ }
+ {
+ std::lock_guard lock(mLock);
+ LOG_ALWAYS_FATAL_IF(mState != IDLE, "State: %d must be IDLE", mState);
+ // TODO: do we want a specific set() here?
+ pairStream->mAudioTrack = mAudioTrack;
+ pairStream->mSoundID = mSoundID; // optimization to reuse AudioTrack.
+ pairStream->mToggle = mToggle;
+ pairStream->mAutoPaused = mAutoPaused; // save autopause state
+ pairStream->mMuted = mMuted;
+ mAudioTrack.clear(); // the pair owns the audiotrack.
+ mSound.reset();
+ mSoundID = 0;
+ }
+ // TODO: do we need a specific play_l() anymore?
+ const int pairState = pairStream->mState;
+ pairStream->play_l(pairStream->mSound, pairStream->mStreamID,
+ pairStream->mLeftVolume, pairStream->mRightVolume, pairStream->mPriority,
+ pairStream->mLoop, pairStream->mRate, releaseTracks);
+ if (pairStream->mState == IDLE) {
+ return nullptr; // AudioTrack error
+ }
+ if (pairState == PAUSED) { // reestablish pause
+ pairStream->mState = PAUSED;
+ pairStream->mAudioTrack->pause();
+ }
+ }
+ // release tracks outside of Stream lock
+ return pairStream;
+}
+
+void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
+ float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
+ sp<AudioTrack> releaseTracks[2])
+{
+ // These tracks are released without the lock.
+ sp<AudioTrack> &oldTrack = releaseTracks[0];
+ sp<AudioTrack> &newTrack = releaseTracks[1];
+ status_t status = NO_ERROR;
+
+ {
+ ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
+ " priority=%d, loop=%d, rate=%f)",
+ __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
+ priority, loop, rate);
+
+ // initialize track
+ const audio_stream_type_t streamType =
+ AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
+ const int32_t channelCount = sound->getChannelCount();
+ const uint32_t sampleRate = uint32_t(float(sound->getSampleRate()) * rate + 0.5);
+ size_t frameCount = 0;
+
+ if (loop) {
+ const audio_format_t format = sound->getFormat();
+ const size_t frameSize = audio_is_linear_pcm(format)
+ ? channelCount * audio_bytes_per_sample(format) : 1;
+ frameCount = sound->getSizeInBytes() / frameSize;
+ }
+
+ // check if the existing track has the same sound id.
+ if (mAudioTrack != nullptr && mSoundID == sound->getSoundID()) {
+ // the sample rate may fail to change if the audio track is a fast track.
+ if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
+ newTrack = mAudioTrack;
+ ALOGV("%s: reusing track %p for sound %d",
+ __func__, mAudioTrack.get(), sound->getSoundID());
+ }
+ }
+ if (newTrack == 0) {
+ // mToggle toggles each time a track is started on a given stream.
+ // The toggle is concatenated with the Stream address and passed to AudioTrack
+ // as callback user data. This enables the detection of callbacks received from the old
+ // audio track while the new one is being started and avoids processing them with
+ // wrong audio audio buffer size (mAudioBufferSize)
+ auto toggle = mToggle ^ 1;
+ void* userData = (void*)((uintptr_t)this | toggle);
+ audio_channel_mask_t soundChannelMask = sound->getChannelMask();
+ // When sound contains a valid channel mask, use it as is.
+ // Otherwise, use stream count to calculate channel mask.
+ audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
+ ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
+
+ // do not create a new audio track if current track is compatible with sound parameters
+
+ newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
+ channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
+ staticCallback, userData,
+ 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
+ AudioTrack::TRANSFER_DEFAULT,
+ nullptr /*offloadInfo*/, -1 /*uid*/, -1 /*pid*/,
+ mStreamManager->getAttributes());
+
+ oldTrack = mAudioTrack;
+ status = newTrack->initCheck();
+ if (status != NO_ERROR) {
+ ALOGE("%s: error creating AudioTrack", __func__);
+ // newTrack goes out of scope, so reference count drops to zero
+ goto exit;
+ }
+ // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
+ mToggle = toggle;
+ mAudioTrack = newTrack;
+ ALOGV("%s: using new track %p for sound %d",
+ __func__, newTrack.get(), sound->getSoundID());
+ }
+ if (mMuted) {
+ newTrack->setVolume(0.0f, 0.0f);
+ } else {
+ newTrack->setVolume(leftVolume, rightVolume);
+ }
+ newTrack->setLoop(0, frameCount, loop);
+ mAudioTrack->start();
+ mSound = sound;
+ mSoundID = sound->getSoundID();
+ mPriority = priority;
+ mLoop = loop;
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ mRate = rate;
+ mState = PLAYING;
+ mStopTimeNs = 0;
+ mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point
+ }
+
+exit:
+ ALOGV("%s: delete oldTrack %p", __func__, oldTrack.get());
+ if (status != NO_ERROR) {
+ // TODO: should we consider keeping the soundID if the old track is OK?
+ // Do not attempt to restart this track (should we remove the stream id?)
+ mState = IDLE;
+ mSoundID = 0;
+ mSound.reset();
+ mAudioTrack.clear(); // actual release from releaseTracks[]
+ }
+}
+
+/* static */
+void Stream::staticCallback(int event, void* user, void* info)
+{
+ const uintptr_t userAsInt = (uintptr_t)user;
+ Stream* stream = reinterpret_cast<Stream*>(userAsInt & ~1);
+ stream->callback(event, info, userAsInt & 1, 0 /* tries */);
+}
+
+void Stream::callback(int event, void* info, int toggle, int tries)
+{
+ ALOGV("%s streamID %d", __func__, (int)mStreamID);
+ int32_t activeStreamIDToRestart = 0;
+ {
+ std::unique_lock lock(mLock);
+
+ if (mAudioTrack == nullptr) {
+ // The AudioTrack is either with this stream or its pair.
+ // if this swaps a few times, the toggle is bound to be wrong, so we fail then.
+ //
+ // TODO: Modify AudioTrack callbacks to avoid the hacky toggle and retry
+ // logic here.
+ if (tries < 3) {
+ lock.unlock();
+ getPairStream()->callback(event, info, toggle, tries + 1);
+ } else {
+ ALOGW("%s streamID %d cannot find track", __func__, (int)mStreamID);
+ }
+ return;
+ }
+ if (mToggle != toggle) {
+ ALOGD("%s streamID %d wrong toggle", __func__, (int)mStreamID);
+ return;
+ }
+ switch (event) {
+ case AudioTrack::EVENT_MORE_DATA:
+ ALOGW("%s streamID %d Invalid EVENT_MORE_DATA for static track",
+ __func__, (int)mStreamID);
+ break;
+ case AudioTrack::EVENT_UNDERRUN:
+ ALOGW("%s streamID %d Invalid EVENT_UNDERRUN for static track",
+ __func__, (int)mStreamID);
+ break;
+ case AudioTrack::EVENT_BUFFER_END:
+ ALOGV("%s streamID %d EVENT_BUFFER_END", __func__, (int)mStreamID);
+ if (mState != IDLE) {
+ activeStreamIDToRestart = mStreamID;
+ mStopTimeNs = systemTime();
+ }
+ break;
+ case AudioTrack::EVENT_LOOP_END:
+ ALOGV("%s streamID %d EVENT_LOOP_END", __func__, (int)mStreamID);
+ break;
+ case AudioTrack::EVENT_NEW_IAUDIOTRACK:
+ ALOGV("%s streamID %d NEW_IAUDIOTRACK", __func__, (int)mStreamID);
+ break;
+ default:
+ ALOGW("%s streamID %d Invalid event %d", __func__, (int)mStreamID, event);
+ break;
+ }
+ } // lock ends here. This is on the callback thread, no need to be precise.
+ if (activeStreamIDToRestart > 0) {
+ // Restart only if a particular streamID is still current and active.
+ ALOGV("%s: moveToRestartQueue %d", __func__, activeStreamIDToRestart);
+ mStreamManager->moveToRestartQueue(this, activeStreamIDToRestart);
+ }
+}
+
+void Stream::dump() const
+{
+ ALOGV("mPairStream=%p, mState=%d, mStreamID=%d, mSoundID=%d, mPriority=%d, mLoop=%d",
+ getPairStream(), mState, (int)mStreamID, mSoundID, mPriority, mLoop);
+}
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/Stream.h b/media/jni/soundpool/Stream.h
new file mode 100644
index 000000000000..82d2690e2965
--- /dev/null
+++ b/media/jni/soundpool/Stream.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Sound.h"
+
+#include <audio_utils/clock.h>
+#include <media/AudioTrack.h>
+
+namespace android::soundpool {
+
+// This is the amount of time to wait after stop is called when stealing an
+// AudioTrack to allow the sound to ramp down. If this is 0, glitches
+// may occur when stealing an AudioTrack.
+inline constexpr int64_t kStopWaitTimeNs = 20 * NANOS_PER_MILLISECOND;
+
+inline constexpr size_t kCacheLineSize = 64; /* std::hardware_constructive_interference_size */
+
+class StreamManager; // forward decl
+
+/**
+ * A Stream is associated with a StreamID exposed to the app to play a Sound.
+ *
+ * The Stream uses monitor locking strategy on mLock.
+ * https://en.wikipedia.org/wiki/Monitor_(synchronization)
+ *
+ * where public methods are guarded by a lock (as needed)
+ *
+ * For Java equivalent APIs, see
+ * https://developer.android.com/reference/android/media/SoundPool
+ *
+ * Streams are paired by the StreamManager, so one stream in the pair may be "stopping"
+ * while the other stream of the pair has been prepared to run
+ * (and the streamID returned to the app) pending its pair to be stopped.
+ * The pair of a Stream may be obtained by calling getPairStream(),
+ * where this->getPairStream()->getPairStream() == this; (pair is a commutative relationship).
+ *
+ * playPairStream() and getPairPriority() access the paired stream.
+ * See also StreamManager.h for details of physical layout implications of paired streams.
+ */
+class alignas(kCacheLineSize) Stream {
+public:
+ enum state { IDLE, PAUSED, PLAYING };
+ // The PAUSED, PLAYING state directly corresponds to the AudioTrack state of an active Stream.
+ //
+ // The IDLE state indicates an inactive Stream. An IDLE Stream may have a non-nullptr
+ // AudioTrack, which may be recycled for use if the SoundID matches the next Stream playback.
+ //
+ // PAUSED -> PLAYING through resume() (see also autoResume())
+ // PLAYING -> PAUSED through pause() (see also autoPause())
+ //
+ // IDLE is the initial state of a Stream and also when a stream becomes inactive.
+ // {PAUSED, PLAYING} -> IDLE through stop() (or if the Sound finishes playing)
+ // IDLE -> PLAYING through play(). (there is no way to start a Stream in paused mode).
+
+ ~Stream();
+ void setStreamManager(StreamManager* streamManager) { // non-nullptr
+ mStreamManager = streamManager; // set in StreamManager constructor, not changed
+ }
+
+ // The following methods are monitor locked by mLock.
+ //
+ // For methods taking a streamID:
+ // if the streamID matches the Stream's mStreamID, then method proceeds
+ // else the command is ignored with no effect.
+
+ // returns true if the stream needs to be explicitly stopped.
+ bool requestStop(int32_t streamID);
+ void stop(); // explicit stop(), typically called from the worker thread.
+ void clearAudioTrack();
+ void pause(int32_t streamID);
+ void autoPause(); // see the Java SoundPool.autoPause documentation for details.
+ void resume(int32_t streamID);
+ void autoResume();
+ void mute(bool muting);
+ void dump() const;
+
+ // returns the pair stream if successful, nullptr otherwise
+ Stream* playPairStream();
+
+ // These parameters are explicitly checked in the SoundPool class
+ // so never deviate from the Java API specified values.
+ void setVolume(int32_t streamID, float leftVolume, float rightVolume);
+ void setRate(int32_t streamID, float rate);
+ void setPriority(int32_t streamID, int priority);
+ void setLoop(int32_t streamID, int loop);
+ void setPlay(int32_t streamID, const std::shared_ptr<Sound> &sound, int32_t soundID,
+ float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate);
+ void setStopTimeNs(int64_t stopTimeNs); // systemTime() clock monotonic.
+
+ // The following getters are not locked and have weak consistency.
+ // These are considered advisory only - being stale is of nuisance.
+ int32_t getPriority() const { return mPriority; }
+ int32_t getPairPriority() const { return getPairStream()->getPriority(); }
+ int64_t getStopTimeNs() const { return mStopTimeNs; }
+
+ int32_t getStreamID() const { return mStreamID; } // Can change with setPlay()
+ int32_t getSoundID() const { return mSoundID; } // Can change with play_l()
+ bool hasSound() const { return mSound.get() != nullptr; }
+
+ Stream* getPairStream() const; // this never changes. See top of header.
+
+private:
+ void play_l(const std::shared_ptr<Sound>& sound, int streamID,
+ float leftVolume, float rightVolume, int priority, int loop, float rate,
+ sp<AudioTrack> releaseTracks[2]);
+ void stop_l();
+ void setVolume_l(float leftVolume, float rightVolume);
+
+ // For use with AudioTrack callback.
+ static void staticCallback(int event, void* user, void* info);
+ void callback(int event, void* info, int toggle, int tries);
+
+ // StreamManager should be set on construction and not changed.
+ // release mLock before calling into StreamManager
+ StreamManager* mStreamManager = nullptr;
+
+ mutable std::mutex mLock;
+ std::atomic_int32_t mStreamID = 0; // Note: valid streamIDs are always positive.
+ int mState = IDLE;
+ std::shared_ptr<Sound> mSound; // Non-null if playing.
+ int32_t mSoundID = 0; // The sound ID associated with the AudioTrack.
+ float mLeftVolume = 0.f;
+ float mRightVolume = 0.f;
+ int32_t mPriority = INT32_MIN;
+ int32_t mLoop = 0;
+ float mRate = 0.f;
+ bool mAutoPaused = false;
+ bool mMuted = false;
+
+ sp<AudioTrack> mAudioTrack;
+ int mToggle = 0;
+ int64_t mStopTimeNs = 0; // if nonzero, time to wait for stop.
+};
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
new file mode 100644
index 000000000000..8928c47cf4bb
--- /dev/null
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool::StreamManager"
+#include <utils/Log.h>
+
+#include "StreamManager.h"
+
+#include <audio_utils/clock.h>
+#include <audio_utils/roundup.h>
+
+namespace android::soundpool {
+
+// kMaxStreams is number that should be less than the current AudioTrack max per UID of 40.
+// It is the maximum number of AudioTrack resources allowed in the SoundPool.
+// We suggest a value at least 4 or greater to allow CTS tests to pass.
+static constexpr int32_t kMaxStreams = 32;
+
+// kStealActiveStream_OldestFirst = false historically (Q and earlier)
+// Changing to true could break app expectations but could change behavior beneficially.
+// In R, we change this to true, as it is the correct way per SoundPool documentation.
+static constexpr bool kStealActiveStream_OldestFirst = true;
+
+// kPlayOnCallingThread = true prior to R.
+// Changing to false means calls to play() are almost instantaneous instead of taking around
+// ~10ms to launch the AudioTrack. It is perhaps 100x faster.
+static constexpr bool kPlayOnCallingThread = false;
+
+// Amount of time for a StreamManager thread to wait before closing.
+static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND;
+
+////////////
+
+StreamMap::StreamMap(int32_t streams) {
+ ALOGV("%s(%d)", __func__, streams);
+ if (streams > kMaxStreams) {
+ ALOGW("%s: requested %d streams, clamping to %d", __func__, streams, kMaxStreams);
+ streams = kMaxStreams;
+ } else if (streams < 1) {
+ ALOGW("%s: requested %d streams, clamping to 1", __func__, streams);
+ streams = 1;
+ }
+ mStreamPoolSize = streams * 2;
+ mStreamPool.reset(new Stream[mStreamPoolSize]);
+ // we use a perfect hash table with 2x size to map StreamIDs to Stream pointers.
+ mPerfectHash = std::make_unique<PerfectHash<int32_t, Stream *>>(roundup(mStreamPoolSize * 2));
+}
+
+Stream* StreamMap::findStream(int32_t streamID) const
+{
+ Stream *stream = lookupStreamFromId(streamID);
+ return stream != nullptr && stream->getStreamID() == streamID ? stream : nullptr;
+}
+
+size_t StreamMap::streamPosition(const Stream* stream) const
+{
+ ptrdiff_t index = stream - mStreamPool.get();
+ LOG_ALWAYS_FATAL_IF(index < 0 || index >= mStreamPoolSize,
+ "%s: stream position out of range: %td", __func__, index);
+ return (size_t)index;
+}
+
+Stream* StreamMap::lookupStreamFromId(int32_t streamID) const
+{
+ return streamID > 0 ? mPerfectHash->getValue(streamID).load() : nullptr;
+}
+
+int32_t StreamMap::getNextIdForStream(Stream* stream) const {
+ // even though it is const, it mutates the internal hash table.
+ const int32_t id = mPerfectHash->generateKey(
+ stream,
+ [] (Stream *stream) {
+ return stream == nullptr ? 0 : stream->getStreamID();
+ }, /* getKforV() */
+ stream->getStreamID() /* oldID */);
+ return id;
+}
+
+////////////
+
+StreamManager::StreamManager(
+ int32_t streams, size_t threads, const audio_attributes_t* attributes)
+ : StreamMap(streams)
+ , mAttributes(*attributes)
+{
+ ALOGV("%s(%d, %zu, ...)", __func__, streams, threads);
+ forEach([this](Stream *stream) {
+ stream->setStreamManager(this);
+ if ((streamPosition(stream) & 1) == 0) { // put the first stream of pair as available.
+ mAvailableStreams.insert(stream);
+ }
+ });
+
+ mThreadPool = std::make_unique<ThreadPool>(
+ std::min(threads, (size_t)std::thread::hardware_concurrency()),
+ "SoundPool_");
+}
+
+StreamManager::~StreamManager()
+{
+ ALOGV("%s", __func__);
+ {
+ std::unique_lock lock(mStreamManagerLock);
+ mQuit = true;
+ mStreamManagerCondition.notify_all();
+ }
+ mThreadPool->quit();
+
+ // call stop on the stream pool
+ forEach([](Stream *stream) { stream->stop(); });
+
+ // This invokes the destructor on the AudioTracks -
+ // we do it here to ensure that AudioTrack callbacks will not occur
+ // afterwards.
+ forEach([](Stream *stream) { stream->clearAudioTrack(); });
+}
+
+
+int32_t StreamManager::queueForPlay(const std::shared_ptr<Sound> &sound,
+ int32_t soundID, float leftVolume, float rightVolume,
+ int32_t priority, int32_t loop, float rate)
+{
+ ALOGV("%s(sound=%p, soundID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f)",
+ __func__, sound.get(), soundID, leftVolume, rightVolume, priority, loop, rate);
+ bool launchThread = false;
+ int32_t streamID = 0;
+
+ { // for lock
+ std::unique_lock lock(mStreamManagerLock);
+ Stream *newStream = nullptr;
+ bool fromAvailableQueue = false;
+ ALOGV("%s: mStreamManagerLock lock acquired", __func__);
+
+ sanityCheckQueue_l();
+ // find an available stream, prefer one that has matching sound id.
+ if (mAvailableStreams.size() > 0) {
+ newStream = *mAvailableStreams.begin();
+ for (auto stream : mAvailableStreams) {
+ if (stream->getSoundID() == soundID) {
+ newStream = stream;
+ break;
+ }
+ }
+ if (newStream != nullptr) {
+ newStream->setStopTimeNs(systemTime());
+ }
+ fromAvailableQueue = true;
+ }
+
+ // also look in the streams restarting (if the paired stream doesn't have a pending play)
+ if (newStream == nullptr || newStream->getSoundID() != soundID) {
+ for (auto [unused , stream] : mRestartStreams) {
+ if (!stream->getPairStream()->hasSound()) {
+ if (stream->getSoundID() == soundID) {
+ newStream = stream;
+ break;
+ } else if (newStream == nullptr) {
+ newStream = stream;
+ }
+ }
+ }
+ }
+
+ // no available streams, look for one to steal from the active list
+ if (newStream == nullptr) {
+ for (auto stream : mActiveStreams) {
+ if (stream->getPriority() <= priority) {
+ if (newStream == nullptr
+ || newStream->getPriority() > stream->getPriority()) {
+ newStream = stream;
+ }
+ }
+ }
+ if (newStream != nullptr) { // we need to mute as it is still playing.
+ (void)newStream->requestStop(newStream->getStreamID());
+ }
+ }
+
+ // none found, look for a stream that is restarting, evict one.
+ if (newStream == nullptr) {
+ for (auto [unused, stream] : mRestartStreams) {
+ if (stream->getPairPriority() <= priority) {
+ newStream = stream;
+ break;
+ }
+ }
+ }
+
+ // DO NOT LOOK into mProcessingStreams as those are held by the StreamManager threads.
+
+ if (newStream == nullptr) {
+ ALOGD("%s: unable to find stream, returning 0", __func__);
+ return 0; // unable to find available stream
+ }
+
+ Stream *pairStream = newStream->getPairStream();
+ streamID = getNextIdForStream(pairStream);
+ pairStream->setPlay(
+ streamID, sound, soundID, leftVolume, rightVolume, priority, loop, rate);
+ if (fromAvailableQueue && kPlayOnCallingThread) {
+ removeFromQueues_l(newStream);
+ mProcessingStreams.emplace(newStream);
+ lock.unlock();
+ if (Stream* nextStream = newStream->playPairStream()) {
+ lock.lock();
+ ALOGV("%s: starting streamID:%d", __func__, nextStream->getStreamID());
+ addToActiveQueue_l(nextStream);
+ } else {
+ lock.lock();
+ mAvailableStreams.insert(newStream);
+ streamID = 0;
+ }
+ mProcessingStreams.erase(newStream);
+ } else {
+ launchThread = moveToRestartQueue_l(newStream) && needMoreThreads_l();
+ }
+ sanityCheckQueue_l();
+ ALOGV("%s: mStreamManagerLock released", __func__);
+ } // lock
+
+ if (launchThread) {
+ const int32_t id __unused = mThreadPool->launch([this](int32_t id) { run(id); });
+ ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
+ }
+ ALOGV("%s: returning %d", __func__, streamID);
+ return streamID;
+}
+
+void StreamManager::moveToRestartQueue(
+ Stream* stream, int32_t activeStreamIDToMatch)
+{
+ ALOGV("%s(stream(ID)=%d, activeStreamIDToMatch=%d)",
+ __func__, stream->getStreamID(), activeStreamIDToMatch);
+ bool restart;
+ {
+ std::lock_guard lock(mStreamManagerLock);
+ sanityCheckQueue_l();
+ if (mProcessingStreams.count(stream) > 0 ||
+ mProcessingStreams.count(stream->getPairStream()) > 0) {
+ ALOGD("%s: attempting to restart processing stream(%d)",
+ __func__, stream->getStreamID());
+ restart = false;
+ } else {
+ moveToRestartQueue_l(stream, activeStreamIDToMatch);
+ restart = needMoreThreads_l();
+ }
+ sanityCheckQueue_l();
+ }
+ if (restart) {
+ const int32_t id __unused = mThreadPool->launch([this](int32_t id) { run(id); });
+ ALOGV_IF(id != 0, "%s: launched thread %d", __func__, id);
+ }
+}
+
+bool StreamManager::moveToRestartQueue_l(
+ Stream* stream, int32_t activeStreamIDToMatch)
+{
+ ALOGV("%s(stream(ID)=%d, activeStreamIDToMatch=%d)",
+ __func__, stream->getStreamID(), activeStreamIDToMatch);
+ if (activeStreamIDToMatch > 0 && stream->getStreamID() != activeStreamIDToMatch) {
+ return false;
+ }
+ const ssize_t found = removeFromQueues_l(stream, activeStreamIDToMatch);
+ if (found < 0) return false;
+
+ LOG_ALWAYS_FATAL_IF(found > 1, "stream on %zd > 1 stream lists", found);
+
+ addToRestartQueue_l(stream);
+ mStreamManagerCondition.notify_one();
+ return true;
+}
+
+ssize_t StreamManager::removeFromQueues_l(
+ Stream* stream, int32_t activeStreamIDToMatch) {
+ size_t found = 0;
+ for (auto it = mActiveStreams.begin(); it != mActiveStreams.end(); ++it) {
+ if (*it == stream) {
+ mActiveStreams.erase(it); // we erase the iterator and break (otherwise it not safe).
+ ++found;
+ break;
+ }
+ }
+ // activeStreamIDToMatch is nonzero indicates we proceed only if found.
+ if (found == 0 && activeStreamIDToMatch > 0) {
+ return -1; // special code: not present on active streams, ignore restart request
+ }
+
+ for (auto it = mRestartStreams.begin(); it != mRestartStreams.end(); ++it) {
+ if (it->second == stream) {
+ mRestartStreams.erase(it);
+ ++found;
+ break;
+ }
+ }
+ found += mAvailableStreams.erase(stream);
+
+ // streams on mProcessingStreams are undergoing processing by the StreamManager thread
+ // and do not participate in normal stream migration.
+ return found;
+}
+
+void StreamManager::addToRestartQueue_l(Stream *stream) {
+ mRestartStreams.emplace(stream->getStopTimeNs(), stream);
+}
+
+void StreamManager::addToActiveQueue_l(Stream *stream) {
+ if (kStealActiveStream_OldestFirst) {
+ mActiveStreams.push_back(stream); // oldest to newest
+ } else {
+ mActiveStreams.push_front(stream); // newest to oldest
+ }
+}
+
+void StreamManager::run(int32_t id)
+{
+ ALOGV("%s(%d) entering", __func__, id);
+ int64_t waitTimeNs = kWaitTimeBeforeCloseNs;
+ std::unique_lock lock(mStreamManagerLock);
+ while (!mQuit) {
+ mStreamManagerCondition.wait_for(
+ lock, std::chrono::duration<int64_t, std::nano>(waitTimeNs));
+ ALOGV("%s(%d) awake", __func__, id);
+
+ sanityCheckQueue_l();
+
+ if (mQuit || (mRestartStreams.empty() && waitTimeNs == kWaitTimeBeforeCloseNs)) {
+ break; // end the thread
+ }
+
+ waitTimeNs = kWaitTimeBeforeCloseNs;
+ while (!mQuit && !mRestartStreams.empty()) {
+ const nsecs_t nowNs = systemTime();
+ auto it = mRestartStreams.begin();
+ Stream* const stream = it->second;
+ const int64_t diffNs = stream->getStopTimeNs() - nowNs;
+ if (diffNs > 0) {
+ waitTimeNs = std::min(waitTimeNs, diffNs);
+ break;
+ }
+ mRestartStreams.erase(it);
+ mProcessingStreams.emplace(stream);
+ lock.unlock();
+ stream->stop();
+ ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
+ if (Stream* nextStream = stream->playPairStream()) {
+ ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
+ lock.lock();
+ if (nextStream->getStopTimeNs() > 0) {
+ // the next stream was stopped before we can move it to the active queue.
+ ALOGV("%s(%d) stopping started streamID:%d",
+ __func__, id, nextStream->getStreamID());
+ moveToRestartQueue_l(nextStream);
+ } else {
+ addToActiveQueue_l(nextStream);
+ }
+ } else {
+ lock.lock();
+ mAvailableStreams.insert(stream);
+ }
+ mProcessingStreams.erase(stream);
+ sanityCheckQueue_l();
+ }
+ }
+ ALOGV("%s(%d) exiting", __func__, id);
+}
+
+void StreamManager::dump() const
+{
+ forEach([](const Stream *stream) { stream->dump(); });
+}
+
+void StreamManager::sanityCheckQueue_l() const
+{
+ // We want to preserve the invariant that each stream pair is exactly on one of the queues.
+ const size_t availableStreams = mAvailableStreams.size();
+ const size_t restartStreams = mRestartStreams.size();
+ const size_t activeStreams = mActiveStreams.size();
+ const size_t processingStreams = mProcessingStreams.size();
+ const size_t managedStreams = availableStreams + restartStreams + activeStreams
+ + processingStreams;
+ const size_t totalStreams = getStreamMapSize() >> 1;
+ LOG_ALWAYS_FATAL_IF(managedStreams != totalStreams,
+ "%s: mAvailableStreams:%zu + mRestartStreams:%zu + "
+ "mActiveStreams:%zu + mProcessingStreams:%zu = %zu != total streams %zu",
+ __func__, availableStreams, restartStreams, activeStreams, processingStreams,
+ managedStreams, totalStreams);
+ ALOGV("%s: mAvailableStreams:%zu + mRestartStreams:%zu + "
+ "mActiveStreams:%zu + mProcessingStreams:%zu = %zu (total streams: %zu)",
+ __func__, availableStreams, restartStreams, activeStreams, processingStreams,
+ managedStreams, totalStreams);
+}
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h
new file mode 100644
index 000000000000..8c98ac992f75
--- /dev/null
+++ b/media/jni/soundpool/StreamManager.h
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Stream.h"
+
+#include <condition_variable>
+#include <future>
+#include <list>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <unordered_set>
+#include <vector>
+
+#include <utils/AndroidThreads.h>
+
+namespace android::soundpool {
+
+// TODO: Move helper classes to a utility file, with separate test.
+
+/**
+ * JavaThread is used like std::thread but for threads that may call the JVM.
+ *
+ * std::thread does not easily attach to the JVM. We need JVM capable threads
+ * from createThreadEtc() since android binder call optimization may attempt to
+ * call back into Java if the SoundPool runs in system server.
+ *
+ *
+ * No locking is required - the member variables are inherently thread-safe.
+ */
+class JavaThread {
+public:
+ JavaThread(std::function<void()> f, const char *name)
+ : mF{std::move(f)} {
+ createThreadEtc(staticFunction, this, name);
+ }
+
+ JavaThread(JavaThread &&) = delete; // uses "this" ptr, not moveable.
+
+ void join() const {
+ mFuture.wait();
+ }
+
+ bool isClosed() const {
+ return mIsClosed;
+ }
+
+private:
+ static int staticFunction(void *data) {
+ JavaThread *jt = static_cast<JavaThread *>(data);
+ jt->mF();
+ jt->mIsClosed = true;
+ jt->mPromise.set_value();
+ return 0;
+ }
+
+ // No locking is provided as these variables are initialized in the constructor
+ // and the members referenced are thread-safe objects.
+ // (mFuture.wait() can block multiple threads.)
+ // Note the order of member variables is reversed for destructor.
+ const std::function<void()> mF;
+ // Used in join() to block until the thread completes.
+ // See https://en.cppreference.com/w/cpp/thread/promise for the void specialization of
+ // promise.
+ std::promise<void> mPromise;
+ std::future<void> mFuture{mPromise.get_future()};
+ std::atomic_bool mIsClosed = false;
+};
+
+/**
+ * The ThreadPool manages thread lifetimes of SoundPool worker threads.
+ *
+ * TODO: the (eventual) goal of ThreadPool is to transparently and cooperatively
+ * maximize CPU utilization while avoiding starvation of other applications.
+ * Some possibilities:
+ *
+ * We should create worker threads when we have SoundPool work and the system is idle.
+ * CPU cycles are "use-it-or-lose-it" when the system is idle.
+ *
+ * We should adjust the priority of worker threads so that the second (and subsequent) worker
+ * threads have lower priority (should we try to promote priority also?).
+ *
+ * We should throttle the spawning of new worker threads, spacing over time, to avoid
+ * creating too many new threads all at once, on initialization.
+ */
+class ThreadPool {
+public:
+ ThreadPool(size_t maxThreadCount, std::string name)
+ : mMaxThreadCount(maxThreadCount)
+ , mName{std::move(name)} { }
+
+ ~ThreadPool() { quit(); }
+
+ size_t getActiveThreadCount() const { return mActiveThreadCount; }
+ size_t getMaxThreadCount() const { return mMaxThreadCount; }
+
+ void quit() {
+ std::list<std::unique_ptr<JavaThread>> threads;
+ {
+ std::lock_guard lock(mThreadLock);
+ if (mQuit) return; // already joined.
+ mQuit = true;
+ threads = std::move(mThreads);
+ mThreads.clear();
+ }
+ // mQuit set under lock, no more threads will be created.
+ for (auto &thread : threads) {
+ thread->join();
+ thread.reset();
+ }
+ LOG_ALWAYS_FATAL_IF(mActiveThreadCount != 0,
+ "Invalid Active Threads: %zu", (size_t)mActiveThreadCount);
+ }
+
+ // returns a non-zero id if successful, the id is to help logging messages.
+ int32_t launch(std::function<void(int32_t /* id */)> f) {
+ std::list<std::unique_ptr<JavaThread>> threadsToRelease; // release outside of lock.
+ std::lock_guard lock(mThreadLock);
+ if (mQuit) return 0; // ignore if we have quit
+
+ // clean up threads.
+ for (auto it = mThreads.begin(); it != mThreads.end(); ) {
+ if ((*it)->isClosed()) {
+ threadsToRelease.emplace_back(std::move(*it));
+ it = mThreads.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ const size_t threadCount = mThreads.size();
+ if (threadCount < mMaxThreadCount) {
+ // if the id wraps, we don't care about collisions. it's just for logging.
+ mNextThreadId = mNextThreadId == INT32_MAX ? 1 : ++mNextThreadId;
+ const int32_t id = mNextThreadId;
+ mThreads.emplace_back(std::make_unique<JavaThread>(
+ [this, id, mf = std::move(f)] { mf(id); --mActiveThreadCount; },
+ (mName + std::to_string(id)).c_str()));
+ ++mActiveThreadCount;
+ return id;
+ }
+ return 0;
+ }
+
+ // TODO: launch only if load average is low.
+ // This gets the load average
+ // See also std::thread::hardware_concurrency() for the concurrent capability.
+ static double getLoadAvg() {
+ double loadAvg[1];
+ if (getloadavg(loadAvg, std::size(loadAvg)) > 0) {
+ return loadAvg[0];
+ }
+ return -1.;
+ }
+
+private:
+ const size_t mMaxThreadCount;
+ const std::string mName;
+
+ std::atomic_size_t mActiveThreadCount = 0;
+
+ std::mutex mThreadLock;
+ bool mQuit = false; // GUARDED_BY(mThreadLock)
+ int32_t mNextThreadId = 0; // GUARDED_BY(mThreadLock)
+ std::list<std::unique_ptr<JavaThread>> mThreads; // GUARDED_BY(mThreadLock)
+};
+
+/**
+ * A Perfect HashTable for IDs (key) to pointers (value).
+ *
+ * There are no collisions. Why? because we generate the IDs for you to look up :-).
+ *
+ * The goal of this hash table is to map an integer ID handle > 0 to a pointer.
+ * We give these IDs in monotonic order (though we may skip if it were to cause a collision).
+ *
+ * The size of the hashtable must be large enough to accommodate the max number of keys.
+ * We suggest 2x.
+ *
+ * Readers are lockless
+ * Single writer could be lockless, but we allow multiple writers through an internal lock.
+ *
+ * For the Key type K, valid keys generated are > 0 (signed or unsigned)
+ * For the Value type V, values are pointers - nullptr means empty.
+ */
+template <typename K, typename V>
+class PerfectHash {
+public:
+ PerfectHash(size_t hashCapacity)
+ : mHashCapacity(hashCapacity)
+ , mK2V{new std::atomic<V>[hashCapacity]()} {
+ }
+
+ // Generate a key for a value V.
+ // There is a testing function getKforV() which checks what the value reports as its key.
+ //
+ // Calls back into getKforV under lock.
+ //
+ // We expect that the hashCapacity is 2x the number of stored keys in order
+ // to have one or two tries to find an empty slot
+ K generateKey(V value, std::function<K(V)> getKforV, K oldKey = 0) {
+ std::lock_guard lock(mHashLock);
+ // try to remove the old key.
+ if (oldKey > 0) { // key valid
+ const V v = getValue(oldKey);
+ if (v != nullptr) { // value still valid
+ const K atPosition = getKforV(v);
+ if (atPosition < 0 || // invalid value
+ atPosition == oldKey || // value's key still valid and matches old key
+ ((atPosition ^ oldKey) & (mHashCapacity - 1)) != 0) { // stale key entry
+ getValue(oldKey) = nullptr; // invalidate
+ }
+ } // else if value is invalid, no need to invalidate.
+ }
+ // check if we are invalidating only.
+ if (value == nullptr) return 0;
+ // now insert the new value and return the key.
+ size_t tries = 0;
+ for (; tries < mHashCapacity; ++tries) {
+ mNextKey = mNextKey == std::numeric_limits<K>::max() ? 1 : mNextKey + 1;
+ const V v = getValue(mNextKey);
+ //ALOGD("tries: %zu, key:%d value:%p", tries, (int)mNextKey, v);
+ if (v == nullptr) break; // empty
+ const K atPosition = getKforV(v);
+ //ALOGD("tries: %zu key atPosition:%d", tries, (int)atPosition);
+ if (atPosition < 0 || // invalid value
+ ((atPosition ^ mNextKey) & (mHashCapacity - 1)) != 0) { // stale key entry
+ break;
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(tries == mHashCapacity, "hash table overflow!");
+ //ALOGD("%s: found after %zu tries", __func__, tries);
+ getValue(mNextKey) = value;
+ return mNextKey;
+ }
+
+ std::atomic<V> &getValue(K key) { return mK2V[key & (mHashCapacity - 1)]; }
+ const std::atomic_int32_t &getValue(K key) const { return mK2V[key & (mHashCapacity - 1)]; }
+
+private:
+ mutable std::mutex mHashLock;
+ const size_t mHashCapacity; // size of mK2V no lock needed.
+ std::unique_ptr<std::atomic<V>[]> mK2V; // no lock needed for read access.
+ K mNextKey{}; // GUARDED_BY(mHashLock)
+};
+
+/**
+ * StreamMap contains the all the valid streams available to SoundPool.
+ *
+ * There is no Lock required for this class because the streams are
+ * allocated in the constructor, the lookup is lockless, and the Streams
+ * returned are locked internally.
+ *
+ * The lookup uses a perfect hash.
+ * It is possible to use a lockless hash table or to use a stripe-locked concurrent
+ * hashmap for essentially lock-free lookup.
+ *
+ * This follows Map-Reduce parallelism model.
+ * https://en.wikipedia.org/wiki/MapReduce
+ *
+ * Conceivably the forEach could be parallelized using std::for_each with a
+ * std::execution::par policy.
+ *
+ * https://en.cppreference.com/w/cpp/algorithm/for_each
+ */
+class StreamMap {
+public:
+ explicit StreamMap(int32_t streams);
+
+ // Returns the stream associated with streamID or nullptr if not found.
+ // This need not be locked.
+ // The stream ID will never migrate to another Stream, but it may change
+ // underneath you. The Stream operations that take a streamID will confirm
+ // that the streamID matches under the Stream lock before executing otherwise
+ // it ignores the command as stale.
+ Stream* findStream(int32_t streamID) const;
+
+ // Iterates through the stream pool applying the function f.
+ // Since this enumerates over every single stream, it is unlocked.
+ //
+ // See related: https://en.cppreference.com/w/cpp/algorithm/for_each
+ void forEach(std::function<void(const Stream *)>f) const {
+ for (size_t i = 0; i < mStreamPoolSize; ++i) {
+ f(&mStreamPool[i]);
+ }
+ }
+
+ void forEach(std::function<void(Stream *)>f) {
+ for (size_t i = 0; i < mStreamPoolSize; ++i) {
+ f(&mStreamPool[i]);
+ }
+ }
+
+ // Returns the pair stream for a given Stream.
+ // This need not be locked as it is a property of the pointer address.
+ Stream* getPairStream(const Stream* stream) const {
+ const size_t index = streamPosition(stream);
+ return &mStreamPool[index ^ 1];
+ }
+
+ // find the position of the stream in mStreamPool array.
+ size_t streamPosition(const Stream* stream) const; // no lock needed
+
+ size_t getStreamMapSize() const {
+ return mStreamPoolSize;
+ }
+
+ // find the next valid ID for a stream and store in hash table.
+ int32_t getNextIdForStream(Stream* stream) const;
+
+private:
+
+ // use the hash table to attempt to find the stream.
+ // nullptr is returned if the lookup fails.
+ Stream* lookupStreamFromId(int32_t streamID) const;
+
+ // The stream pool is initialized in the constructor, effectively const.
+ // no locking required for access.
+ //
+ // The constructor parameter "streams" results in streams pairs of streams.
+ // We have twice as many streams because we wish to return a streamID "handle"
+ // back to the app immediately, while we may be stopping the other stream in the
+ // pair to get its AudioTrack :-).
+ //
+ // Of the stream pair, only one of the streams may have an AudioTrack.
+ // The fixed association of a stream pair allows callbacks from the AudioTrack
+ // to be associated properly to either one or the other of the stream pair.
+ //
+ // TODO: The stream pair arrangement can be removed if we have better AudioTrack
+ // callback handling (being able to remove and change the callback after construction).
+ //
+ // Streams may be accessed anytime off of the stream pool
+ // as there is internal locking on each stream.
+ std::unique_ptr<Stream[]> mStreamPool; // no lock needed for access.
+ size_t mStreamPoolSize; // no lock needed for access.
+
+ // In order to find the Stream from a StreamID, we could do a linear lookup in mStreamPool.
+ // As an alternative, one could use stripe-locked or lock-free concurrent hashtables.
+ //
+ // When considering linear search vs hashmap, verify the typical use-case size.
+ // Linear search is faster than std::unordered_map (circa 2018) for less than 40 elements.
+ // [ Skarupke, M. (2018), "You Can Do Better than std::unordered_map: New and Recent
+ // Improvements to Hash Table Performance." C++Now 2018. cppnow.org, see
+ // https://www.youtube.com/watch?v=M2fKMP47slQ ]
+ //
+ // Here, we use a PerfectHash of Id to Stream *, since we can control the
+ // StreamID returned to the user. This allows O(1) read access to mStreamPool lock-free.
+ //
+ // We prefer that the next stream ID is monotonic for aesthetic reasons
+ // (if we didn't care about monotonicity, a simple method is to apply a generation count
+ // to each stream in the unused upper bits of its index in mStreamPool for the id).
+ //
+ std::unique_ptr<PerfectHash<int32_t, Stream *>> mPerfectHash;
+};
+
+/**
+ * StreamManager is used to manage the streams (accessed by StreamID from Java).
+ *
+ * Locking order (proceeds from application to component).
+ * SoundPool mApiLock (if needed) -> StreamManager mStreamManagerLock
+ * -> pair Stream mLock -> queued Stream mLock
+ */
+class StreamManager : public StreamMap {
+public:
+ // Note: the SoundPool pointer is only used for stream initialization.
+ // It is not stored in StreamManager.
+ StreamManager(int32_t streams, size_t threads, const audio_attributes_t* attributes);
+ ~StreamManager();
+
+ // Returns positive streamID on success, 0 on failure. This is locked.
+ int32_t queueForPlay(const std::shared_ptr<Sound> &sound,
+ int32_t soundID, float leftVolume, float rightVolume,
+ int32_t priority, int32_t loop, float rate);
+
+ ///////////////////////////////////////////////////////////////////////
+ // Called from soundpool::Stream
+
+ const audio_attributes_t* getAttributes() const { return &mAttributes; }
+
+ // Moves the stream to the restart queue (called upon BUFFER_END of the static track)
+ // this is locked internally.
+ // If activeStreamIDToMatch is nonzero, it will only move to the restart queue
+ // if the streamIDToMatch is found on the active queue.
+ void moveToRestartQueue(Stream* stream, int32_t activeStreamIDToMatch = 0);
+
+private:
+
+ void run(int32_t id); // worker thread, takes lock internally.
+ void dump() const; // no lock needed
+
+ // returns true if more worker threads are needed.
+ bool needMoreThreads_l() {
+ return mRestartStreams.size() > 0 &&
+ (mThreadPool->getActiveThreadCount() == 0
+ || std::distance(mRestartStreams.begin(),
+ mRestartStreams.upper_bound(systemTime()))
+ > (ptrdiff_t)mThreadPool->getActiveThreadCount());
+ }
+
+ // returns true if the stream was added.
+ bool moveToRestartQueue_l(Stream* stream, int32_t activeStreamIDToMatch = 0);
+ // returns number of queues the stream was removed from (should be 0 or 1);
+ // a special code of -1 is returned if activeStreamIDToMatch is > 0 and
+ // the stream wasn't found on the active queue.
+ ssize_t removeFromQueues_l(Stream* stream, int32_t activeStreamIDToMatch = 0);
+ void addToRestartQueue_l(Stream *stream);
+ void addToActiveQueue_l(Stream *stream);
+ void sanityCheckQueue_l() const;
+
+ const audio_attributes_t mAttributes;
+ std::unique_ptr<ThreadPool> mThreadPool; // locked internally
+
+ // mStreamManagerLock is used to lock access for transitions between the
+ // 4 stream queues by the Manager Thread or by the user initiated play().
+ // A stream pair has exactly one stream on exactly one of the queues.
+ std::mutex mStreamManagerLock;
+ std::condition_variable mStreamManagerCondition;
+
+ bool mQuit = false; // GUARDED_BY(mStreamManagerLock)
+
+ // There are constructor arg "streams" pairs of streams, only one of each
+ // pair on the 4 stream queues below. The other stream in the pair serves as
+ // placeholder to accumulate user changes, pending actual availability of the
+ // AudioTrack, as it may be in use, requiring stop-then-restart.
+ //
+ // The 4 queues are implemented in the appropriate STL container based on perceived
+ // optimality.
+
+ // 1) mRestartStreams: Streams awaiting stop.
+ // The paired stream may be active (but with no AudioTrack), and will be restarted
+ // with an active AudioTrack when the current stream is stopped.
+ std::multimap<int64_t /* stopTimeNs */, Stream*>
+ mRestartStreams; // GUARDED_BY(mStreamManagerLock)
+
+ // 2) mActiveStreams: Streams that are active.
+ // The paired stream will be inactive.
+ // This is in order of specified by kStealActiveStream_OldestFirst
+ std::list<Stream*> mActiveStreams; // GUARDED_BY(mStreamManagerLock)
+
+ // 3) mAvailableStreams: Streams that are inactive.
+ // The paired stream will also be inactive.
+ // No particular order.
+ std::unordered_set<Stream*> mAvailableStreams; // GUARDED_BY(mStreamManagerLock)
+
+ // 4) mProcessingStreams: Streams that are being processed by the ManagerThreads
+ // When on this queue, the stream and its pair are not available for stealing.
+ // Each ManagerThread will have at most one stream on the mProcessingStreams queue.
+ // The paired stream may be active or restarting.
+ // No particular order.
+ std::unordered_set<Stream*> mProcessingStreams; // GUARDED_BY(mStreamManagerLock)
+};
+
+} // namespace android::soundpool
diff --git a/media/jni/soundpool/tests/Android.bp b/media/jni/soundpool/tests/Android.bp
new file mode 100644
index 000000000000..96ec4e5d5913
--- /dev/null
+++ b/media/jni/soundpool/tests/Android.bp
@@ -0,0 +1,28 @@
+cc_binary {
+ name: "soundpool_stress",
+ host_supported: false,
+
+ include_dirs: [
+ "frameworks/base/media/jni/"
+ ],
+
+ shared_libs: [
+ "libaudioutils",
+ "libbinder",
+ "liblog",
+ "libmedia",
+ "libsoundpool",
+ "libstagefright",
+ "libutils",
+ ],
+
+ srcs: [
+ "soundpool_stress.cpp"
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
diff --git a/media/jni/soundpool/tests/build_and_run.sh b/media/jni/soundpool/tests/build_and_run.sh
new file mode 100755
index 000000000000..741f2efbfddb
--- /dev/null
+++ b/media/jni/soundpool/tests/build_and_run.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Run samples from this directory
+#
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "Android build environment not set"
+ exit -1
+fi
+
+# ensure we have mm
+. $ANDROID_BUILD_TOP/build/envsetup.sh
+
+mm
+
+echo "waiting for device"
+
+adb root && adb wait-for-device remount
+
+echo "========================================"
+echo "testing soundpool_stress"
+uidir="/product/media/audio/notifications"
+adb push $OUT/system/bin/soundpool_stress /system/bin
+
+# test SoundPool playback of all the UI sound samples (loaded twice) looping 10s 1 thread.
+#adb shell /system/bin/soundpool_stress -l -1 $uidir/*.ogg $uidir/*.ogg
+
+# performance test SoundPool playback of all the UI sound samples (x2)
+# 1 iterations, looping, 1 second playback, 4 threads.
+adb shell /system/bin/soundpool_stress -i 1 -l -1 -p 1 -t 4 $uidir/*.ogg $uidir/*.ogg
diff --git a/media/jni/soundpool/tests/soundpool_stress.cpp b/media/jni/soundpool/tests/soundpool_stress.cpp
new file mode 100644
index 000000000000..212662fb6b96
--- /dev/null
+++ b/media/jni/soundpool/tests/soundpool_stress.cpp
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "soundpool"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include <atomic>
+#include <future>
+#include <mutex>
+#include <set>
+#include <vector>
+
+#include <audio_utils/clock.h>
+#include <binder/ProcessState.h>
+#include <media/stagefright/MediaExtractorFactory.h>
+#include <soundpool/SoundPool.h> // direct include, this is not an NDK feature.
+#include <system/audio.h>
+#include <utils/Log.h>
+
+using namespace android;
+
+// Errors and diagnostic messages all go to stdout.
+
+namespace {
+
+void usage(const char *name)
+{
+ printf("Usage: %s "
+ "[-i #iterations] [-l #loop] [-p #playback_seconds] [-s #streams] [-t #threads] "
+ "[-z #snoozeSec] <input-file>+\n", name);
+ printf("Uses soundpool to load and play a file (the first 10 seconds)\n");
+ printf(" -i #iterations, default 1\n");
+ printf(" -l #loop looping mode, -1 forever\n");
+ printf(" -p #playback_seconds, default 10\n");
+ printf(" -s #streams for concurrent sound playback, default 20\n");
+ printf(" -t #threads, default 1\n");
+ printf(" -z #snoozeSec after stopping, -1 forever, default 0\n");
+ printf(" <input-file>+ files to be played\n");
+}
+
+std::atomic_int32_t gErrors{};
+std::atomic_int32_t gWarnings{};
+
+void printEvent(const SoundPoolEvent *event) {
+ printf("{ msg:%d id:%d status:%d }\n", event->mMsg, event->mArg1, event->mArg2);
+}
+
+class CallbackManager {
+public:
+ int32_t getNumberEvents(int32_t soundID) {
+ std::lock_guard lock(mLock);
+ return mEvents[soundID] > 0;
+ }
+
+ void setSoundPool(SoundPool* soundPool) {
+ std::lock_guard lock(mLock);
+ mSoundPool = soundPool;
+ }
+
+ void callback(SoundPoolEvent event, const SoundPool *soundPool) {
+ std::lock_guard lock(mLock);
+ printEvent(&event);
+ if (soundPool != mSoundPool) {
+ printf("ERROR: mismatched soundpool: %p\n", soundPool);
+ ++gErrors;
+ return;
+ }
+ if (event.mMsg != 1 /* SoundPoolEvent::SOUND_LOADED */) {
+ printf("ERROR: invalid event msg: %d\n", event.mMsg);
+ ++gErrors;
+ return;
+ }
+ if (event.mArg2 != 0) {
+ printf("ERROR: event status(%d) != 0\n", event.mArg2);
+ ++gErrors;
+ return;
+ }
+ if (event.mArg1 <= 0) {
+ printf("ERROR: event soundID(%d) < 0\n", event.mArg1);
+ ++gErrors;
+ return;
+ }
+ ++mEvents[event.mArg1];
+ }
+
+private:
+ std::mutex mLock;
+ SoundPool *mSoundPool = nullptr;
+ std::map<int32_t /* soundID */, int32_t /* count */> mEvents;
+} gCallbackManager;
+
+
+void StaticCallbackManager(SoundPoolEvent event, SoundPool* soundPool, void* user) {
+ ((CallbackManager *)user)->callback(event, soundPool);
+}
+
+void testStreams(SoundPool *soundPool, const std::vector<const char *> &filenames,
+ int loop, int playSec)
+{
+ const int64_t startTimeNs = systemTime();
+ std::vector<int32_t> soundIDs;
+ for (auto filename : filenames) {
+ struct stat st;
+ if (stat(filename, &st) < 0) {
+ printf("ERROR: cannot stat %s\n", filename);
+ return;
+ }
+ const uint64_t length = uint64_t(st.st_size);
+ const int inp = open(filename, O_RDONLY);
+ if (inp < 0) {
+ printf("ERROR: cannot open %s\n", filename);
+ return;
+ }
+ printf("loading (%s) size (%llu)\n", filename, (unsigned long long)length);
+ const int32_t soundID = soundPool->load(
+ inp, 0 /*offset*/, length, 0 /*priority - unused*/);
+ if (soundID == 0) {
+ printf("ERROR: cannot load %s\n", filename);
+ return;
+ }
+ close(inp);
+ soundIDs.emplace_back(soundID);
+ printf("loaded %s soundID(%d)\n", filename, soundID);
+ }
+ const int64_t requestLoadTimeNs = systemTime();
+ printf("\nrequestLoadTimeMs: %d\n",
+ (int)((requestLoadTimeNs - startTimeNs) / NANOS_PER_MILLISECOND));
+
+ // create stream & get Id (playing)
+ const float maxVol = 1.f;
+ const float silentVol = 0.f;
+ const int priority = 0; // lowest
+ const float rate = 1.f; // normal
+
+ // Loading is done by a SoundPool Worker thread.
+ // TODO: Use SoundPool::setCallback() for wait
+
+ for (int32_t soundID : soundIDs) {
+ while (true) {
+ const int32_t streamID =
+ soundPool->play(soundID, silentVol, silentVol, priority, 0 /*loop*/, rate);
+ if (streamID != 0) {
+ const int32_t events = gCallbackManager.getNumberEvents(soundID);
+ if (events != 1) {
+ printf("WARNING: successful play for streamID:%d soundID:%d"
+ " but callback events(%d) != 1\n", streamID, soundID, events);
+ ++gWarnings;
+ }
+ soundPool->stop(streamID);
+ break;
+ }
+ usleep(1000);
+ }
+ printf("[%d]", soundID);
+ fflush(stdout);
+ }
+
+ const int64_t loadTimeNs = systemTime();
+ printf("\nloadTimeMs: %d\n", (int)((loadTimeNs - startTimeNs) / NANOS_PER_MILLISECOND));
+
+ // check and play (overlap with above).
+ std::vector<int32_t> streamIDs;
+ for (int32_t soundID : soundIDs) {
+ printf("\nplaying soundID=%d", soundID);
+ const int32_t streamID = soundPool->play(soundID, maxVol, maxVol, priority, loop, rate);
+ if (streamID == 0) {
+ printf(" failed! ERROR");
+ ++gErrors;
+ } else {
+ printf(" streamID=%d", streamID);
+ streamIDs.emplace_back(streamID);
+ }
+ }
+ const int64_t playTimeNs = systemTime();
+ printf("\nplayTimeMs: %d\n", (int)((playTimeNs - loadTimeNs) / NANOS_PER_MILLISECOND));
+
+ for (int i = 0; i < playSec; ++i) {
+ sleep(1);
+ printf(".");
+ fflush(stdout);
+ }
+
+ for (int32_t streamID : streamIDs) {
+ soundPool->stop(streamID);
+ }
+
+ for (int32_t soundID : soundIDs) {
+ soundPool->unload(soundID);
+ }
+ printf("\nDone!\n");
+}
+
+} // namespace
+
+int main(int argc, char *argv[])
+{
+ const char * const me = argv[0];
+
+ int iterations = 1;
+ int loop = 0; // disable looping
+ int maxStreams = 40; // change to have more concurrent playback streams
+ int playSec = 10;
+ int snoozeSec = 0;
+ int threadCount = 1;
+ for (int ch; (ch = getopt(argc, argv, "i:l:p:s:t:z:")) != -1; ) {
+ switch (ch) {
+ case 'i':
+ iterations = atoi(optarg);
+ break;
+ case 'l':
+ loop = atoi(optarg);
+ break;
+ case 'p':
+ playSec = atoi(optarg);
+ break;
+ case 's':
+ maxStreams = atoi(optarg);
+ break;
+ case 't':
+ threadCount = atoi(optarg);
+ break;
+ case 'z':
+ snoozeSec = atoi(optarg);
+ break;
+ default:
+ usage(me);
+ return EXIT_FAILURE;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc <= 0) {
+ usage(me);
+ return EXIT_FAILURE;
+ }
+
+ std::vector<const char *> filenames(argv, argv + argc);
+
+ android::ProcessState::self()->startThreadPool();
+
+ // O and later requires data sniffer registration for proper file type detection
+ MediaExtractorFactory::LoadExtractors();
+
+ // create soundpool
+ audio_attributes_t aa = {
+ .content_type = AUDIO_CONTENT_TYPE_MUSIC,
+ .usage = AUDIO_USAGE_MEDIA,
+ };
+ auto soundPool = std::make_unique<SoundPool>(maxStreams, &aa);
+
+ gCallbackManager.setSoundPool(soundPool.get());
+ soundPool->setCallback(StaticCallbackManager, &gCallbackManager);
+
+ const int64_t startTimeNs = systemTime();
+
+ for (int it = 0; it < iterations; ++it) {
+ // One instance:
+ // testStreams(soundPool.get(), filenames, loop, playSec);
+
+ // Test multiple instances
+ std::vector<std::future<void>> threads(threadCount);
+ printf("testing %zu threads\n", threads.size());
+ for (auto &thread : threads) {
+ thread = std::async(std::launch::async,
+ [&]{ testStreams(soundPool.get(), filenames, loop, playSec);});
+ }
+ // automatically joins.
+ }
+
+ const int64_t endTimeNs = systemTime();
+
+ // snooze before cleaning up to examine soundpool dumpsys state after stop
+ for (int i = 0; snoozeSec < 0 || i < snoozeSec; ++i) {
+ printf("z");
+ fflush(stdout);
+ sleep(1);
+ };
+
+ gCallbackManager.setSoundPool(nullptr);
+ soundPool.reset();
+
+ printf("total time in ms: %lld\n", (endTimeNs - startTimeNs) / NANOS_PER_MILLISECOND);
+ if (gWarnings != 0) {
+ printf("%d warnings!\n", gWarnings.load());
+ }
+ if (gErrors != 0) {
+ printf("%d errors!\n", gErrors.load());
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index a0d20506613e..2da45b6a78be 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -17,6 +17,7 @@ cc_library_shared {
srcs: [
"amidi.cpp",
+ "MidiDeviceInfo.cpp",
":IMidiDeviceServer.aidl",
],
@@ -31,12 +32,14 @@ cc_library_shared {
"-fvisibility=hidden",
],
+ header_libs: [
+ "media_ndk_headers",
+ ],
+
shared_libs: [
"liblog",
"libbinder",
"libutils",
- "libmedia",
- "libmediandk",
"libandroid_runtime",
],
diff --git a/media/native/midi/MidiDeviceInfo.cpp b/media/native/midi/MidiDeviceInfo.cpp
new file mode 100644
index 000000000000..ac68d26c935b
--- /dev/null
+++ b/media/native/midi/MidiDeviceInfo.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MidiDeviceInfo"
+
+#include <MidiDeviceInfo.h>
+
+#include <binder/Parcel.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/String16.h>
+
+namespace android {
+namespace media {
+namespace midi {
+
+// The constant values need to be kept in sync with MidiDeviceInfo.java.
+// static
+const char* const MidiDeviceInfo::PROPERTY_NAME = "name";
+const char* const MidiDeviceInfo::PROPERTY_MANUFACTURER = "manufacturer";
+const char* const MidiDeviceInfo::PROPERTY_PRODUCT = "product";
+const char* const MidiDeviceInfo::PROPERTY_VERSION = "version";
+const char* const MidiDeviceInfo::PROPERTY_SERIAL_NUMBER = "serial_number";
+const char* const MidiDeviceInfo::PROPERTY_ALSA_CARD = "alsa_card";
+const char* const MidiDeviceInfo::PROPERTY_ALSA_DEVICE = "alsa_device";
+
+String16 MidiDeviceInfo::getProperty(const char* propertyName) {
+ String16 value;
+ if (mProperties.getString(String16(propertyName), &value)) {
+ return value;
+ } else {
+ return String16();
+ }
+}
+
+#define RETURN_IF_FAILED(calledOnce) \
+ { \
+ status_t returnStatus = calledOnce; \
+ if (returnStatus) { \
+ ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
+ return returnStatus; \
+ } \
+ }
+
+status_t MidiDeviceInfo::writeToParcel(Parcel* parcel) const {
+ // Needs to be kept in sync with code in MidiDeviceInfo.java
+ RETURN_IF_FAILED(parcel->writeInt32(mType));
+ RETURN_IF_FAILED(parcel->writeInt32(mId));
+ RETURN_IF_FAILED(parcel->writeInt32((int32_t)mInputPortNames.size()));
+ RETURN_IF_FAILED(parcel->writeInt32((int32_t)mOutputPortNames.size()));
+ RETURN_IF_FAILED(writeStringVector(parcel, mInputPortNames));
+ RETURN_IF_FAILED(writeStringVector(parcel, mOutputPortNames));
+ RETURN_IF_FAILED(parcel->writeInt32(mIsPrivate ? 1 : 0));
+ RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
+ // This corresponds to "extra" properties written by Java code
+ RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
+ return OK;
+}
+
+status_t MidiDeviceInfo::readFromParcel(const Parcel* parcel) {
+ // Needs to be kept in sync with code in MidiDeviceInfo.java
+ RETURN_IF_FAILED(parcel->readInt32(&mType));
+ RETURN_IF_FAILED(parcel->readInt32(&mId));
+ int32_t inputPortCount;
+ RETURN_IF_FAILED(parcel->readInt32(&inputPortCount));
+ int32_t outputPortCount;
+ RETURN_IF_FAILED(parcel->readInt32(&outputPortCount));
+ RETURN_IF_FAILED(readStringVector(parcel, &mInputPortNames, inputPortCount));
+ RETURN_IF_FAILED(readStringVector(parcel, &mOutputPortNames, outputPortCount));
+ int32_t isPrivate;
+ RETURN_IF_FAILED(parcel->readInt32(&isPrivate));
+ mIsPrivate = isPrivate == 1;
+ RETURN_IF_FAILED(mProperties.readFromParcel(parcel));
+ // Ignore "extra" properties as they may contain Java Parcelables
+ return OK;
+}
+
+status_t MidiDeviceInfo::readStringVector(
+ const Parcel* parcel, Vector<String16> *vectorPtr, size_t defaultLength) {
+ std::unique_ptr<std::vector<std::unique_ptr<String16>>> v;
+ status_t result = parcel->readString16Vector(&v);
+ if (result != OK) return result;
+ vectorPtr->clear();
+ if (v.get() != nullptr) {
+ for (const auto& iter : *v) {
+ if (iter.get() != nullptr) {
+ vectorPtr->push_back(*iter);
+ } else {
+ vectorPtr->push_back(String16());
+ }
+ }
+ } else {
+ vectorPtr->resize(defaultLength);
+ }
+ return OK;
+}
+
+status_t MidiDeviceInfo::writeStringVector(Parcel* parcel, const Vector<String16>& vector) const {
+ std::vector<String16> v;
+ for (size_t i = 0; i < vector.size(); ++i) {
+ v.push_back(vector[i]);
+ }
+ return parcel->writeString16Vector(v);
+}
+
+// Vector does not define operator==
+static inline bool areVectorsEqual(const Vector<String16>& lhs, const Vector<String16>& rhs) {
+ if (lhs.size() != rhs.size()) return false;
+ for (size_t i = 0; i < lhs.size(); ++i) {
+ if (lhs[i] != rhs[i]) return false;
+ }
+ return true;
+}
+
+bool operator==(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs) {
+ return (lhs.mType == rhs.mType && lhs.mId == rhs.mId &&
+ areVectorsEqual(lhs.mInputPortNames, rhs.mInputPortNames) &&
+ areVectorsEqual(lhs.mOutputPortNames, rhs.mOutputPortNames) &&
+ lhs.mProperties == rhs.mProperties &&
+ lhs.mIsPrivate == rhs.mIsPrivate);
+}
+
+} // namespace midi
+} // namespace media
+} // namespace android
diff --git a/media/native/midi/MidiDeviceInfo.h b/media/native/midi/MidiDeviceInfo.h
new file mode 100644
index 000000000000..5b4a241323d7
--- /dev/null
+++ b/media/native/midi/MidiDeviceInfo.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_MIDI_DEVICE_INFO_H
+#define ANDROID_MEDIA_MIDI_DEVICE_INFO_H
+
+#include <binder/Parcelable.h>
+#include <binder/PersistableBundle.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+namespace media {
+namespace midi {
+
+class MidiDeviceInfo : public Parcelable {
+public:
+ MidiDeviceInfo() = default;
+ virtual ~MidiDeviceInfo() = default;
+ MidiDeviceInfo(const MidiDeviceInfo& midiDeviceInfo) = default;
+
+ status_t writeToParcel(Parcel* parcel) const override;
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ int getType() const { return mType; }
+ int getUid() const { return mId; }
+ bool isPrivate() const { return mIsPrivate; }
+ const Vector<String16>& getInputPortNames() const { return mInputPortNames; }
+ const Vector<String16>& getOutputPortNames() const { return mOutputPortNames; }
+ String16 getProperty(const char* propertyName);
+
+ // The constants need to be kept in sync with MidiDeviceInfo.java
+ enum {
+ TYPE_USB = 1,
+ TYPE_VIRTUAL = 2,
+ TYPE_BLUETOOTH = 3,
+ };
+ static const char* const PROPERTY_NAME;
+ static const char* const PROPERTY_MANUFACTURER;
+ static const char* const PROPERTY_PRODUCT;
+ static const char* const PROPERTY_VERSION;
+ static const char* const PROPERTY_SERIAL_NUMBER;
+ static const char* const PROPERTY_ALSA_CARD;
+ static const char* const PROPERTY_ALSA_DEVICE;
+
+ friend bool operator==(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs);
+ friend bool operator!=(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs) {
+ return !(lhs == rhs);
+ }
+
+private:
+ status_t readStringVector(
+ const Parcel* parcel, Vector<String16> *vectorPtr, size_t defaultLength);
+ status_t writeStringVector(Parcel* parcel, const Vector<String16>& vector) const;
+
+ int32_t mType;
+ int32_t mId;
+ Vector<String16> mInputPortNames;
+ Vector<String16> mOutputPortNames;
+ os::PersistableBundle mProperties;
+ bool mIsPrivate;
+};
+
+} // namespace midi
+} // namespace media
+} // namespace android
+
+#endif // ANDROID_MEDIA_MIDI_DEVICE_INFO_H
diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp
index 46f28152cd6d..35c4d42a0aa8 100644
--- a/media/native/midi/amidi.cpp
+++ b/media/native/midi/amidi.cpp
@@ -26,7 +26,7 @@
#include <core_jni_helpers.h>
#include "android/media/midi/BpMidiDeviceServer.h"
-#include "media/MidiDeviceInfo.h"
+#include "MidiDeviceInfo.h"
#include "include/amidi/AMidi.h"
#include "amidi_internal.h"
diff --git a/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl b/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl
index c022388e0aa8..347e4e8ebe4b 100644
--- a/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl
+++ b/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl
@@ -33,9 +33,10 @@ interface IPlatformCompatNative
* you do not need to call this API directly. The change will be reported for you.
*
* @param changeId The ID of the compatibility change taking effect.
+ * @param userId The ID of the user that the operation is done for.
* @param packageName The package name of the app in question.
*/
- void reportChangeByPackageName(long changeId, @utf8InCpp String packageName);
+ void reportChangeByPackageName(long changeId, @utf8InCpp String packageName, int userId);
/**
* Reports that a compatibility change is affecting an app process now.
@@ -64,9 +65,10 @@ interface IPlatformCompatNative
*
* @param changeId The ID of the compatibility change in question.
* @param packageName The package name of the app in question.
+ * @param userId The ID of the user that the operation is done for.
* @return {@code true} if the change is enabled for the current app.
*/
- boolean isChangeEnabledByPackageName(long changeId, @utf8InCpp String packageName);
+ boolean isChangeEnabledByPackageName(long changeId, @utf8InCpp String packageName, int userId);
/**
* Query if a given compatibility change is enabled for an app process. This method should
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
index 1cf48c5ffd0b..0f964fd56285 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -31,14 +31,15 @@
android:paddingStart="@*android:dimen/car_padding_5"
android:paddingEnd="@*android:dimen/car_padding_5">
- <com.android.systemui.navigationbar.car.CarNavigationButton
+ <com.android.systemui.navigationbar.car.CarFacetButton
android:id="@+id/home"
android:layout_width="@*android:dimen/car_touch_target_size"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"
- android:src="@drawable/car_ic_overview"
+ systemui:icon="@drawable/car_ic_overview"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
- />
+ systemui:selectedIcon="@drawable/car_ic_overview_selected"
+ systemui:useMoreIcon="false"/>
</LinearLayout>
</com.android.systemui.navigationbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 0f44e0815a86..93e553f59dcf 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -102,6 +102,9 @@ abstract class CarSystemUIModule {
CarSystemUIRootComponent systemUIRootComponent);
@Binds
+ public abstract StatusBar bindStatusBar(CarStatusBar statusBar);
+
+ @Binds
@IntoMap
@ClassKey(StatusBar.class)
public abstract SystemUI providesStatusBar(CarStatusBar statusBar);
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index 6fba1d516c73..63bc66afddf8 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -25,7 +25,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.Display;
import android.view.Gravity;
-import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -37,8 +36,6 @@ import com.android.systemui.dagger.qualifiers.MainHandler;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.car.hvac.HvacController;
-import com.android.systemui.statusbar.car.hvac.TemperatureView;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -52,15 +49,13 @@ import dagger.Lazy;
/** Navigation bars customized for the automotive use case. */
public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks {
- private final NavigationBarViewFactory mNavigationBarViewFactory;
+ private final CarNavigationBarController mCarNavigationBarController;
private final WindowManager mWindowManager;
private final DeviceProvisionedController mDeviceProvisionedController;
private final Lazy<FacetButtonTaskStackListener> mFacetButtonTaskStackListener;
private final Handler mMainHandler;
private final Lazy<KeyguardStateController> mKeyguardStateController;
- private final Lazy<CarFacetButtonController> mFacetButtonController;
private final Lazy<NavigationBarController> mNavigationBarController;
- private final Lazy<HvacController> mHvacController;
private IStatusBarService mBarService;
private CommandQueue mCommandQueue;
@@ -82,33 +77,23 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
// it's open.
private boolean mDeviceIsSetUpForUser = true;
- // Configuration values for if nav bars should be shown.
- private boolean mShowBottom;
- private boolean mShowLeft;
- private boolean mShowRight;
-
-
@Inject
public CarNavigationBar(Context context,
- NavigationBarViewFactory navigationBarViewFactory,
+ CarNavigationBarController carNavigationBarController,
WindowManager windowManager,
DeviceProvisionedController deviceProvisionedController,
Lazy<FacetButtonTaskStackListener> facetButtonTaskStackListener,
@MainHandler Handler mainHandler,
Lazy<KeyguardStateController> keyguardStateController,
- Lazy<CarFacetButtonController> facetButtonController,
- Lazy<NavigationBarController> navigationBarController,
- Lazy<HvacController> hvacController) {
+ Lazy<NavigationBarController> navigationBarController) {
super(context);
- mNavigationBarViewFactory = navigationBarViewFactory;
+ mCarNavigationBarController = carNavigationBarController;
mWindowManager = windowManager;
mDeviceProvisionedController = deviceProvisionedController;
mFacetButtonTaskStackListener = facetButtonTaskStackListener;
mMainHandler = mainHandler;
mKeyguardStateController = keyguardStateController;
- mFacetButtonController = facetButtonController;
mNavigationBarController = navigationBarController;
- mHvacController = hvacController;
}
@Override
@@ -118,11 +103,6 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
mBottomNavBarVisible = false;
- // Read configuration.
- mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
- mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
- mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
-
// Get bar service.
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -157,7 +137,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
mActivityManagerWrapper.registerTaskStackListener(mFacetButtonTaskStackListener.get());
- mHvacController.get().connectToCarService();
+ mCarNavigationBarController.connectToHvac();
}
private void restartNavBarsIfNecessary() {
@@ -175,8 +155,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
private void restartNavBars() {
// remove and reattach all hvac components such that we don't keep a reference to unused
// ui elements
- mHvacController.get().removeAllComponents();
- mFacetButtonController.get().removeAll();
+ mCarNavigationBarController.removeAllFromHvac();
if (mNavigationBarWindow != null) {
mNavigationBarWindow.removeAllViews();
@@ -199,10 +178,6 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
if (mKeyguardStateController.get().isShowing()) {
updateNavBarForKeyguardContent();
}
-
- // CarFacetButtonController was reset therefore we need to re-add the status bar elements
- // to the controller.
- // TODO(hseog): Add facet buttons in status bar to controller.
}
private void createNavigationBar(RegisterStatusBarResult result) {
@@ -224,54 +199,30 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
}
private void buildNavBarWindows() {
- if (mShowBottom) {
- mNavigationBarWindow = mNavigationBarViewFactory.getBottomWindow();
- }
-
- if (mShowLeft) {
- mLeftNavigationBarWindow = mNavigationBarViewFactory.getLeftWindow();
- }
-
- if (mShowRight) {
- mRightNavigationBarWindow = mNavigationBarViewFactory.getRightWindow();
- }
+ mNavigationBarWindow = mCarNavigationBarController.getBottomWindow();
+ mLeftNavigationBarWindow = mCarNavigationBarController.getLeftWindow();
+ mRightNavigationBarWindow = mCarNavigationBarController.getRightWindow();
}
private void buildNavBarContent() {
- if (mShowBottom) {
- mNavigationBarView = mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser);
+ mNavigationBarView = mCarNavigationBarController.getBottomBar(mDeviceIsSetUpForUser);
+ if (mNavigationBarView != null) {
mNavigationBarWindow.addView(mNavigationBarView);
- addTemperatureViewToController(mNavigationBarView);
}
- if (mShowLeft) {
- mLeftNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser);
+ mLeftNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser);
+ if (mLeftNavigationBarView != null) {
mLeftNavigationBarWindow.addView(mLeftNavigationBarView);
- addTemperatureViewToController(mLeftNavigationBarView);
}
- if (mShowRight) {
- mRightNavigationBarView = mNavigationBarViewFactory.getRightBar(mDeviceIsSetUpForUser);
+ mRightNavigationBarView = mCarNavigationBarController.getRightBar(mDeviceIsSetUpForUser);
+ if (mRightNavigationBarView != null) {
mRightNavigationBarWindow.addView(mRightNavigationBarView);
- // Add ability to toggle notification center.
- addTemperatureViewToController(mRightNavigationBarView);
- // Add ability to close notification center on touch.
- }
- }
-
- private void addTemperatureViewToController(View v) {
- if (v instanceof TemperatureView) {
- mHvacController.get().addHvacTextView((TemperatureView) v);
- } else if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- addTemperatureViewToController(viewGroup.getChildAt(i));
- }
}
}
private void attachNavBarWindows() {
- if (mShowBottom && !mBottomNavBarVisible) {
+ if (mNavigationBarWindow != null && !mBottomNavBarVisible) {
mBottomNavBarVisible = true;
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -287,7 +238,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
mWindowManager.addView(mNavigationBarWindow, lp);
}
- if (mShowLeft) {
+ if (mLeftNavigationBarWindow != null) {
int width = mContext.getResources().getDimensionPixelSize(
R.dimen.car_left_navigation_bar_width);
WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
@@ -304,7 +255,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
leftlp.gravity = Gravity.LEFT;
mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
}
- if (mShowRight) {
+ if (mRightNavigationBarWindow != null) {
int width = mContext.getResources().getDimensionPixelSize(
R.dimen.car_right_navigation_bar_width);
WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
@@ -340,23 +291,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks
}
boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
- showBottomNavBarWindow(isKeyboardVisible);
- }
-
- private void showBottomNavBarWindow(boolean isKeyboardVisible) {
- if (!mShowBottom) {
- return;
- }
-
- // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do
- // nothing. Same with if keyboard is not visible and bottom nav bar is visible.
- if (isKeyboardVisible ^ mBottomNavBarVisible) {
- return;
- }
-
- mNavigationBarViewFactory.getBottomWindow().setVisibility(
- isKeyboardVisible ? View.GONE : View.VISIBLE);
- mBottomNavBarVisible = !isKeyboardVisible;
+ mCarNavigationBarController.setBottomWindowVisibility(!isKeyboardVisible);
}
private void updateNavBarForKeyguardContent() {
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
new file mode 100644
index 000000000000..f59f886d487b
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.navigationbar.car;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.car.hvac.HvacController;
+import com.android.systemui.statusbar.car.hvac.TemperatureView;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
+/** A single class which controls the navigation bar views. */
+@Singleton
+public class CarNavigationBarController {
+
+ private final Context mContext;
+ private final NavigationBarViewFactory mNavigationBarViewFactory;
+ private final Lazy<HvacController> mHvacControllerLazy;
+
+ private boolean mShowBottom;
+ private boolean mShowLeft;
+ private boolean mShowRight;
+
+ private View.OnTouchListener mTopBarTouchListener;
+ private View.OnTouchListener mBottomBarTouchListener;
+ private View.OnTouchListener mLeftBarTouchListener;
+ private View.OnTouchListener mRightBarTouchListener;
+ private NotificationsShadeController mNotificationsShadeController;
+
+ private CarNavigationBarView mTopView;
+ private CarNavigationBarView mBottomView;
+ private CarNavigationBarView mLeftView;
+ private CarNavigationBarView mRightView;
+
+ @Inject
+ public CarNavigationBarController(Context context,
+ NavigationBarViewFactory navigationBarViewFactory,
+ Lazy<HvacController> hvacControllerLazy) {
+ mContext = context;
+ mNavigationBarViewFactory = navigationBarViewFactory;
+ mHvacControllerLazy = hvacControllerLazy;
+
+ // Read configuration.
+ mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
+ mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
+ mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
+ }
+
+ /** Connect to hvac service. */
+ public void connectToHvac() {
+ mHvacControllerLazy.get().connectToCarService();
+ }
+
+ /** Clean up hvac. */
+ public void removeAllFromHvac() {
+ mHvacControllerLazy.get().removeAllComponents();
+ }
+
+ /** Gets the bottom window if configured to do so. */
+ @Nullable
+ public ViewGroup getBottomWindow() {
+ return mShowBottom ? mNavigationBarViewFactory.getBottomWindow() : null;
+ }
+
+ /** Gets the left window if configured to do so. */
+ @Nullable
+ public ViewGroup getLeftWindow() {
+ return mShowLeft ? mNavigationBarViewFactory.getLeftWindow() : null;
+ }
+
+ /** Gets the right window if configured to do so. */
+ @Nullable
+ public ViewGroup getRightWindow() {
+ return mShowRight ? mNavigationBarViewFactory.getRightWindow() : null;
+ }
+
+ /** Toggles the bottom nav bar visibility. */
+ public boolean setBottomWindowVisibility(boolean isVisible) {
+ return setWindowVisibility(getBottomWindow(), isVisible);
+ }
+
+ /** Toggles the left nav bar visibility. */
+ public boolean setLeftWindowVisibility(boolean isVisible) {
+ return setWindowVisibility(getLeftWindow(), isVisible);
+ }
+
+ /** Toggles the right nav bar visibility. */
+ public boolean setRightWindowVisibility(boolean isVisible) {
+ return setWindowVisibility(getRightWindow(), isVisible);
+ }
+
+ private boolean setWindowVisibility(ViewGroup window, boolean isVisible) {
+ if (window == null) {
+ return false;
+ }
+
+ int newVisibility = isVisible ? View.VISIBLE : View.GONE;
+ if (window.getVisibility() == newVisibility) {
+ return false;
+ }
+
+ window.setVisibility(newVisibility);
+ return true;
+ }
+
+ /** Gets the top navigation bar with the appropriate listeners set. */
+ @NonNull
+ public CarNavigationBarView getTopBar(boolean isSetUp) {
+ mTopView = mNavigationBarViewFactory.getTopBar(isSetUp);
+ mTopView.setStatusBarWindowTouchListener(mTopBarTouchListener);
+ mTopView.setNotificationsPanelController(mNotificationsShadeController);
+ addTemperatureViewToController(mTopView);
+ return mTopView;
+ }
+
+ /** Gets the bottom navigation bar with the appropriate listeners set. */
+ @Nullable
+ public CarNavigationBarView getBottomBar(boolean isSetUp) {
+ if (!mShowBottom) {
+ return null;
+ }
+
+ mBottomView = mNavigationBarViewFactory.getBottomBar(isSetUp);
+ mBottomView.setStatusBarWindowTouchListener(mBottomBarTouchListener);
+ mBottomView.setNotificationsPanelController(mNotificationsShadeController);
+ addTemperatureViewToController(mBottomView);
+ return mBottomView;
+ }
+
+ /** Gets the left navigation bar with the appropriate listeners set. */
+ @Nullable
+ public CarNavigationBarView getLeftBar(boolean isSetUp) {
+ if (!mShowLeft) {
+ return null;
+ }
+
+ mLeftView = mNavigationBarViewFactory.getLeftBar(isSetUp);
+ mLeftView.setStatusBarWindowTouchListener(mLeftBarTouchListener);
+ mLeftView.setNotificationsPanelController(mNotificationsShadeController);
+ addTemperatureViewToController(mLeftView);
+ return mLeftView;
+ }
+
+ /** Gets the right navigation bar with the appropriate listeners set. */
+ @Nullable
+ public CarNavigationBarView getRightBar(boolean isSetUp) {
+ if (!mShowRight) {
+ return null;
+ }
+
+ mRightView = mNavigationBarViewFactory.getRightBar(isSetUp);
+ mRightView.setStatusBarWindowTouchListener(mRightBarTouchListener);
+ mRightView.setNotificationsPanelController(mNotificationsShadeController);
+ addTemperatureViewToController(mRightView);
+ return mRightView;
+ }
+
+ /** Sets a touch listener for the top navigation bar. */
+ public void registerTopBarTouchListener(View.OnTouchListener listener) {
+ mTopBarTouchListener = listener;
+ if (mTopView != null) {
+ mTopView.setStatusBarWindowTouchListener(mTopBarTouchListener);
+ }
+ }
+
+ /** Sets a touch listener for the bottom navigation bar. */
+ public void registerBottomBarTouchListener(View.OnTouchListener listener) {
+ mBottomBarTouchListener = listener;
+ if (mBottomView != null) {
+ mBottomView.setStatusBarWindowTouchListener(mBottomBarTouchListener);
+ }
+ }
+
+ /** Sets a touch listener for the left navigation bar. */
+ public void registerLeftBarTouchListener(View.OnTouchListener listener) {
+ mLeftBarTouchListener = listener;
+ if (mLeftView != null) {
+ mLeftView.setStatusBarWindowTouchListener(mLeftBarTouchListener);
+ }
+ }
+
+ /** Sets a touch listener for the right navigation bar. */
+ public void registerRightBarTouchListener(View.OnTouchListener listener) {
+ mRightBarTouchListener = listener;
+ if (mRightView != null) {
+ mRightView.setStatusBarWindowTouchListener(mRightBarTouchListener);
+ }
+ }
+
+ /** Sets a notification controller which toggles the notification panel. */
+ public void registerNotificationController(
+ NotificationsShadeController notificationsShadeController) {
+ mNotificationsShadeController = notificationsShadeController;
+ if (mTopView != null) {
+ mTopView.setNotificationsPanelController(mNotificationsShadeController);
+ }
+ if (mBottomView != null) {
+ mBottomView.setNotificationsPanelController(mNotificationsShadeController);
+ }
+ if (mLeftView != null) {
+ mLeftView.setNotificationsPanelController(mNotificationsShadeController);
+ }
+ if (mRightView != null) {
+ mRightView.setNotificationsPanelController(mNotificationsShadeController);
+ }
+ }
+
+ /** Interface for controlling the notifications shade. */
+ public interface NotificationsShadeController {
+ /** Toggles the visibility of the notifications shade. */
+ void togglePanel();
+ }
+
+ private void addTemperatureViewToController(View v) {
+ if (v instanceof TemperatureView) {
+ mHvacControllerLazy.get().addHvacTextView((TemperatureView) v);
+ } else if (v instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) v;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ addTemperatureViewToController(viewGroup.getChildAt(i));
+ }
+ }
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
index afb69547cb47..24f8b74eed61 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java
@@ -24,7 +24,6 @@ import android.widget.LinearLayout;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.car.CarStatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
/**
@@ -36,7 +35,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
public class CarNavigationBarView extends LinearLayout {
private View mNavButtons;
private CarNavigationButton mNotificationsButton;
- private CarStatusBar mCarStatusBar;
+ private CarNavigationBarController.NotificationsShadeController mNotificationsShadeController;
private Context mContext;
private View mLockScreenButtons;
// used to wire in open/close gestures for notifications
@@ -82,8 +81,9 @@ public class CarNavigationBarView extends LinearLayout {
return super.onInterceptTouchEvent(ev);
}
- public void setStatusBar(CarStatusBar carStatusBar) {
- mCarStatusBar = carStatusBar;
+ public void setNotificationsPanelController(
+ CarNavigationBarController.NotificationsShadeController controller) {
+ mNotificationsShadeController = controller;
}
/**
@@ -104,7 +104,9 @@ public class CarNavigationBarView extends LinearLayout {
}
protected void onNotificationsClick(View v) {
- mCarStatusBar.togglePanel();
+ if (mNotificationsShadeController != null) {
+ mNotificationsShadeController.togglePanel();
+ }
}
/**
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
index 707d80fc0d3b..40823abaaead 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
@@ -85,6 +85,7 @@ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImag
super.onFinishInflate();
setScaleType(ImageView.ScaleType.CENTER);
setAlpha(mUnselectedAlpha);
+ setImageResource(mIconResourceId);
try {
if (mIntent != null) {
final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index cd81a5cb5c4f..aebb62d7b069 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -32,6 +32,7 @@ import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.PowerManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
@@ -75,8 +76,8 @@ import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.car.CarFacetButtonController;
+import com.android.systemui.navigationbar.car.CarNavigationBarController;
import com.android.systemui.navigationbar.car.CarNavigationBarView;
-import com.android.systemui.navigationbar.car.NavigationBarViewFactory;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.car.CarQSFragment;
@@ -92,8 +93,6 @@ import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.car.hvac.HvacController;
-import com.android.systemui.statusbar.car.hvac.TemperatureView;
import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NewNotifPipeline;
@@ -106,8 +105,11 @@ import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.DozeScrimController;
+import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -170,14 +172,10 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private CarNavigationBarView mRightNavigationBarView;
private final Object mQueueLock = new Object();
- private boolean mShowLeft;
- private boolean mShowRight;
- private boolean mShowBottom;
- private final NavigationBarViewFactory mNavigationBarViewFactory;
+ private final CarNavigationBarController mCarNavigationBarController;
private CarFacetButtonController mCarFacetButtonController;
private DeviceProvisionedController mDeviceProvisionedController;
private boolean mDeviceIsSetUpForUser = true;
- private HvacController mHvacController;
private DrivingStateHelper mDrivingStateHelper;
private PowerManagerHelper mPowerManagerHelper;
private FlingAnimationUtils mFlingAnimationUtils;
@@ -299,9 +297,13 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
DozeParameters dozeParameters,
ScrimController scrimController,
Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ DozeServiceHost dozeServiceHost,
+ PowerManager powerManager,
+ DozeScrimController dozeScrimController,
/* Car Settings injected components. */
- NavigationBarViewFactory navigationBarViewFactory) {
+ CarNavigationBarController carNavigationBarController) {
super(
context,
featureFlags,
@@ -361,9 +363,13 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
notifLog,
dozeParameters,
scrimController,
- lockscreenWallpaperLazy);
+ lockscreenWallpaperLazy,
+ biometricUnlockControllerLazy,
+ dozeServiceHost,
+ powerManager,
+ dozeScrimController);
mScrimController = scrimController;
- mNavigationBarViewFactory = navigationBarViewFactory;
+ mCarNavigationBarController = carNavigationBarController;
}
@Override
@@ -378,10 +384,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
mScreenLifecycle.addObserver(mScreenObserver);
- // Need to initialize HVAC controller before calling super.start - before system bars are
- // created.
- mHvacController = new HvacController(mContext);
-
super.start();
mNotificationPanel.setScrollingEnabled(true);
@@ -395,8 +397,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
createBatteryController();
mCarBatteryController.startListening();
- mHvacController.connectToCarService();
-
mDeviceProvisionedController.addCallback(
new DeviceProvisionedController.DeviceProvisionedListener() {
@Override
@@ -433,9 +433,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
* before and after the device is provisioned. . Also for change of density and font size.
*/
private void restartNavBars() {
- // remove and reattach all hvac components such that we don't keep a reference to unused
- // ui elements
- mHvacController.removeAllComponents();
mCarFacetButtonController.removeAll();
if (mNavigationBarWindow != null) {
@@ -454,17 +451,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mCarFacetButtonController.addAllFacetButtons(mStatusBarWindow);
}
- private void addTemperatureViewToController(View v) {
- if (v instanceof TemperatureView) {
- mHvacController.addHvacTextView((TemperatureView) v);
- } else if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- addTemperatureViewToController(viewGroup.getChildAt(i));
- }
- }
- }
-
/**
* Allows for showing or hiding just the navigation bars. This is indented to be used when
* the full screen user selector is shown.
@@ -885,10 +871,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
@Override
protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
- mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
- mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
- mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
-
buildNavBarWindows();
buildNavBarContent();
}
@@ -896,50 +878,36 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private void buildNavBarContent() {
buildTopBar();
- if (mShowBottom) {
- mNavigationBarView = mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser);
- mNavigationBarView.setStatusBar(this);
- mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener);
- }
+ mNavigationBarView = mCarNavigationBarController.getBottomBar(mDeviceIsSetUpForUser);
+ mCarNavigationBarController.registerBottomBarTouchListener(
+ mNavBarNotificationTouchListener);
- if (mShowLeft) {
- mLeftNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser);
- mLeftNavigationBarView.setStatusBar(this);
- mLeftNavigationBarView.setStatusBarWindowTouchListener(
- mNavBarNotificationTouchListener);
- }
+ mLeftNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser);
+ mCarNavigationBarController.registerLeftBarTouchListener(
+ mNavBarNotificationTouchListener);
- if (mShowRight) {
- mRightNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser);
- mRightNavigationBarView.setStatusBar(this);
- mRightNavigationBarView.setStatusBarWindowTouchListener(
- mNavBarNotificationTouchListener);
- }
+ mRightNavigationBarView = mCarNavigationBarController.getLeftBar(mDeviceIsSetUpForUser);
+ mCarNavigationBarController.registerRightBarTouchListener(
+ mNavBarNotificationTouchListener);
+
+ mCarNavigationBarController.registerNotificationController(() -> togglePanel());
}
private void buildNavBarWindows() {
mTopNavigationBarContainer = mStatusBarWindow
.findViewById(R.id.car_top_navigation_bar_container);
- if (mShowBottom) {
- mNavigationBarWindow = mNavigationBarViewFactory.getBottomWindow();
- }
- if (mShowLeft) {
- mLeftNavigationBarWindow = mNavigationBarViewFactory.getLeftWindow();
- }
- if (mShowRight) {
- mRightNavigationBarWindow = mNavigationBarViewFactory.getRightWindow();
- }
+ mNavigationBarWindow = mCarNavigationBarController.getBottomWindow();
+ mLeftNavigationBarWindow = mCarNavigationBarController.getLeftWindow();
+ mRightNavigationBarWindow = mCarNavigationBarController.getRightWindow();
}
private void buildTopBar() {
mTopNavigationBarContainer.removeAllViews();
- mTopNavigationBarView = mNavigationBarViewFactory.getTopBar(mDeviceIsSetUpForUser);
+ mTopNavigationBarView = mCarNavigationBarController.getTopBar(mDeviceIsSetUpForUser);
+ mCarNavigationBarController.registerTopBarTouchListener(
+ mTopNavBarNotificationTouchListener);
mTopNavigationBarContainer.addView(mTopNavigationBarView);
-
- mTopNavigationBarView.setStatusBar(this);
- addTemperatureViewToController(mTopNavigationBarView);
- mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener);
}
@Override
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 30144524fd42..3b482599b2a0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.car;
import static android.content.DialogInterface.BUTTON_NEGATIVE;
import static android.content.DialogInterface.BUTTON_POSITIVE;
+import static android.os.UserManager.DISALLOW_ADD_USER;
import android.app.ActivityManager;
import android.app.AlertDialog;
@@ -145,7 +146,8 @@ public class UserGridRecyclerView extends RecyclerView {
userRecords.add(createStartGuestUserRecord());
// Add add user record if the foreground user can add users
- if (mCarUserManagerHelper.canForegroundUserAddUsers()) {
+ UserHandle fgUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
+ if (!mUserManager.hasUserRestriction(DISALLOW_ADD_USER, fgUserHandle)) {
userRecords.add(createAddUserRecord());
}
@@ -285,7 +287,7 @@ public class UserGridRecyclerView extends RecyclerView {
}
private void handleAddUserClicked() {
- if (mCarUserManagerHelper.isUserLimitReached()) {
+ if (!mUserManager.canAddMoreUsers()) {
mAddUserView.setEnabled(true);
showMaxUserLimitReachedDialog();
} else {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index cf286bdbde96..738c4257d2c5 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -99,11 +99,13 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
// init input stream before calling startInstallation(), which takes 90 seconds.
initInputStream();
- Thread thread = new Thread(() -> {
- mInstallationSession =
- mDynSystem.startInstallation(mSystemSize, mUserdataSize);
- });
-
+ Thread thread =
+ new Thread(
+ () -> {
+ mDynSystem.startInstallation("userdata", mUserdataSize, false);
+ mInstallationSession =
+ mDynSystem.startInstallation("system", mSystemSize, true);
+ });
thread.start();
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 1f923aff0cea..a855741c4a27 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -127,6 +127,9 @@
<!-- Summary for Connected wifi network without internet -->
<string name="wifi_connected_no_internet">Connected, no internet</string>
+ <!-- Summary for connected network without internet due to private dns validation failed [CHAR LIMIT=NONE] -->
+ <string name="private_dns_broken">Private DNS server cannot be accessed</string>
+
<!-- Summary for connected wifi network with partial internet connectivity [CHAR LIMIT=50] -->
<string name="wifi_limited_connection">Limited connection</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 81d1ea5137e0..16fd51fca0a8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -50,6 +50,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -1568,7 +1569,13 @@ public class AccessPoint implements Comparable<AccessPoint> {
NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
return context.getString(R.string.wifi_limited_connection);
} else if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
- return context.getString(R.string.wifi_connected_no_internet);
+ final String mode = Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.PRIVATE_DNS_MODE);
+ if (nc.isPrivateDnsBroken()) {
+ return context.getString(R.string.private_dns_broken);
+ } else {
+ return context.getString(R.string.wifi_connected_no_internet);
+ }
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 5352936d7224..b11585a73946 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -31,6 +31,7 @@ import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiSsid;
import android.os.Handler;
import android.os.Looper;
+import android.provider.Settings;
import com.android.settingslib.R;
@@ -163,7 +164,13 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback {
statusLabel = mContext.getString(R.string.wifi_limited_connection);
return;
} else if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
- statusLabel = mContext.getString(R.string.wifi_status_no_internet);
+ final String mode = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.PRIVATE_DNS_MODE);
+ if (networkCapabilities.isPrivateDnsBroken()) {
+ statusLabel = mContext.getString(R.string.private_dns_broken);
+ } else {
+ statusLabel = mContext.getString(R.string.wifi_status_no_internet);
+ }
return;
}
}
diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS
new file mode 100644
index 000000000000..2054129506bc
--- /dev/null
+++ b/packages/SettingsProvider/OWNERS
@@ -0,0 +1,3 @@
+hackbod@google.com
+svetoslavganov@google.com
+moltmann@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e24d387c5649..d28c1aa4cfc4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -548,6 +548,9 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
GlobalSettingsProto.Development.FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS);
+ dumpSetting(s, p,
+ Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
+ GlobalSettingsProto.Development.ENABLE_SIZECOMPAT_FREEFORM);
p.end(developmentToken);
final long deviceToken = p.start(GlobalSettingsProto.DEVICE);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 179ba8ad086a..9255c8728392 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -222,6 +222,7 @@ public class SettingsBackupTest {
Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
Settings.Global.DEVELOPMENT_FORCE_RTL,
+ Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
Settings.Global.DEVICE_DEMO_MODE,
Settings.Global.DEVICE_IDLE_CONSTANTS,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java
new file mode 100644
index 000000000000..802a8dab92d9
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NotificationPersonExtractorPlugin.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/** Custom logic that can extract a PeopleHub "person" from a notification. */
+@ProvidesInterface(
+ action = NotificationPersonExtractorPlugin.ACTION,
+ version = NotificationPersonExtractorPlugin.VERSION)
+@DependsOn(target = NotificationPersonExtractorPlugin.PersonData.class)
+public interface NotificationPersonExtractorPlugin extends Plugin {
+
+ String ACTION = "com.android.systemui.action.PEOPLE_HUB_PERSON_EXTRACTOR";
+ int VERSION = 0;
+
+ /**
+ * Attempts to extract a person from a notification. Returns {@code null} if one is not found.
+ */
+ @Nullable PersonData extractPerson(StatusBarNotification sbn);
+
+ /**
+ * Attempts to extract a person id from a notification. Returns {@code null} if one is not
+ * found.
+ *
+ * This method can be overridden in order to provide a faster implementation.
+ */
+ @Nullable
+ default String extractPersonKey(StatusBarNotification sbn) {
+ return extractPerson(sbn).key;
+ }
+
+ /** A person to be surfaced in PeopleHub. */
+ @ProvidesInterface(version = PersonData.VERSION)
+ final class PersonData {
+
+ public static final int VERSION = 0;
+
+ public final String key;
+ public final CharSequence name;
+ public final Drawable avatar;
+ public final PendingIntent clickIntent;
+
+ public PersonData(String key, CharSequence name, Drawable avatar,
+ PendingIntent clickIntent) {
+ this.key = key;
+ this.name = name;
+ this.avatar = avatar;
+ this.clickIntent = clickIntent;
+ }
+ }
+}
diff --git a/packages/SystemUI/res/layout/home_controls.xml b/packages/SystemUI/res/layout/home_controls.xml
index bb971c206e5c..b9a6a482cf1a 100644
--- a/packages/SystemUI/res/layout/home_controls.xml
+++ b/packages/SystemUI/res/layout/home_controls.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/home_controls_layout"
android:layout_width="match_parent"
@@ -8,6 +8,5 @@
android:visibility="gone"
android:padding="8dp"
android:layout_margin="5dp"
- android:background="?android:attr/colorBackgroundFloating"
- android:orientation="vertical">
-</LinearLayout>
+ android:background="?android:attr/colorBackgroundFloating">
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/people_strip.xml b/packages/SystemUI/res/layout/people_strip.xml
index b314db8d72fd..f0ac08bdad37 100644
--- a/packages/SystemUI/res/layout/people_strip.xml
+++ b/packages/SystemUI/res/layout/people_strip.xml
@@ -33,10 +33,9 @@
<LinearLayout
android:id="@+id/people_list"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
+ android:layout_height="match_parent"
+ android:paddingTop="12dp"
+ android:paddingBottom="12dp"
android:gravity="center"
android:orientation="horizontal">
@@ -49,7 +48,7 @@
<LinearLayout
android:layout_width="70dp"
android:layout_height="match_parent"
- android:gravity="center"
+ android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="invisible">
@@ -87,7 +86,7 @@
<LinearLayout
android:layout_width="70dp"
android:layout_height="match_parent"
- android:gravity="center"
+ android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="invisible">
@@ -125,7 +124,7 @@
<LinearLayout
android:layout_width="70dp"
android:layout_height="match_parent"
- android:gravity="center"
+ android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="invisible">
@@ -163,7 +162,7 @@
<LinearLayout
android:layout_width="70dp"
android:layout_height="match_parent"
- android:gravity="center"
+ android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="invisible">
@@ -201,7 +200,7 @@
<LinearLayout
android:layout_width="70dp"
android:layout_height="match_parent"
- android:gravity="center"
+ android:gravity="center_horizontal"
android:orientation="vertical"
android:visibility="invisible">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 00e8b535ccac..70f8e15eb83d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -78,6 +78,7 @@ public class ActivityManagerWrapper {
// Should match the values in PhoneWindowManager
public static final String CLOSE_SYSTEM_WINDOWS_REASON_RECENTS = "recentapps";
+ public static final String CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY = "homekey";
private final PackageManager mPackageManager;
private final BackgroundExecutor mBackgroundExecutor;
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index df0d7872bc61..795a8ce3604f 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -21,10 +21,13 @@ import android.util.ArraySet;
import android.util.SparseArray;
import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import javax.inject.Inject;
import javax.inject.Singleton;
+
/**
* Tracks state of foreground services and notifications related to foreground services per user.
*/
@@ -33,9 +36,11 @@ public class ForegroundServiceController {
private final SparseArray<ForegroundServicesUserState> mUserServices = new SparseArray<>();
private final Object mMutex = new Object();
+ private final NotificationEntryManager mEntryManager;
@Inject
- public ForegroundServiceController() {
+ public ForegroundServiceController(NotificationEntryManager entryManager) {
+ mEntryManager = entryManager;
}
/**
@@ -90,11 +95,18 @@ public class ForegroundServiceController {
}
/**
- * Records active app ops. App Ops are stored in FSC in addition to NotificationData in
- * case they change before we have a notification to tag.
+ * Records active app ops and updates the app op for the pending or visible notifications
+ * with the given parameters.
+ * App Ops are stored in FSC in addition to NotificationEntry in case they change before we
+ * have a notification to tag.
+ * @param appOpCode code for appOp to add/remove
+ * @param uid of user the notification is sent to
+ * @param packageName package that created the notification
+ * @param active whether the appOpCode is active or not
*/
- public void onAppOpChanged(int code, int uid, String packageName, boolean active) {
+ public void onAppOpChanged(int appOpCode, int uid, String packageName, boolean active) {
int userId = UserHandle.getUserId(uid);
+ // Record active app ops
synchronized (mMutex) {
ForegroundServicesUserState userServices = mUserServices.get(userId);
if (userServices == null) {
@@ -102,9 +114,30 @@ public class ForegroundServiceController {
mUserServices.put(userId, userServices);
}
if (active) {
- userServices.addOp(packageName, code);
+ userServices.addOp(packageName, appOpCode);
} else {
- userServices.removeOp(packageName, code);
+ userServices.removeOp(packageName, appOpCode);
+ }
+ }
+
+ // Update appOp if there's an associated pending or visible notification:
+ final String foregroundKey = getStandardLayoutKey(userId, packageName);
+ if (foregroundKey != null) {
+ final NotificationEntry entry = mEntryManager.getPendingOrCurrentNotif(foregroundKey);
+ if (entry != null
+ && uid == entry.getSbn().getUid()
+ && packageName.equals(entry.getSbn().getPackageName())) {
+ boolean changed;
+ synchronized (entry.mActiveAppOps) {
+ if (active) {
+ changed = entry.mActiveAppOps.add(appOpCode);
+ } else {
+ changed = entry.mActiveAppOps.remove(appOpCode);
+ }
+ }
+ if (changed) {
+ mEntryManager.updateNotifications("appOpChanged pkg=" + packageName);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index 4a3b6df6ac90..b983966317f7 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -21,6 +21,7 @@ import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
import android.util.Log;
import com.android.internal.statusbar.NotificationVisibility;
@@ -40,6 +41,7 @@ public class ForegroundServiceNotificationListener {
private final Context mContext;
private final ForegroundServiceController mForegroundServiceController;
+ private final NotificationEntryManager mEntryManager;
@Inject
public ForegroundServiceNotificationListener(Context context,
@@ -47,15 +49,16 @@ public class ForegroundServiceNotificationListener {
NotificationEntryManager notificationEntryManager) {
mContext = context;
mForegroundServiceController = foregroundServiceController;
- notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+ mEntryManager = notificationEntryManager;
+ mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
- addNotification(entry.getSbn(), entry.getImportance());
+ addNotification(entry, entry.getImportance());
}
@Override
- public void onPostEntryUpdated(NotificationEntry entry) {
- updateNotification(entry.getSbn(), entry.getImportance());
+ public void onPreEntryUpdated(NotificationEntry entry) {
+ updateNotification(entry, entry.getImportance());
}
@Override
@@ -67,15 +70,14 @@ public class ForegroundServiceNotificationListener {
}
});
- notificationEntryManager.addNotificationLifetimeExtender(
- new ForegroundServiceLifetimeExtender());
+ mEntryManager.addNotificationLifetimeExtender(new ForegroundServiceLifetimeExtender());
}
/**
- * @param sbn notification that was just posted
+ * @param entry notification that was just posted
*/
- private void addNotification(StatusBarNotification sbn, int importance) {
- updateNotification(sbn, importance);
+ private void addNotification(NotificationEntry entry, int importance) {
+ updateNotification(entry, importance);
}
/**
@@ -113,9 +115,10 @@ public class ForegroundServiceNotificationListener {
}
/**
- * @param sbn notification that was just changed in some way
+ * @param entry notification that was just changed in some way
*/
- private void updateNotification(StatusBarNotification sbn, int newImportance) {
+ private void updateNotification(NotificationEntry entry, int newImportance) {
+ final StatusBarNotification sbn = entry.getSbn();
mForegroundServiceController.updateUserState(
sbn.getUserId(),
userState -> {
@@ -143,8 +146,22 @@ public class ForegroundServiceNotificationListener {
}
}
}
+ tagForeground(entry);
return true;
},
true /* create if not found */);
}
+
+ private void tagForeground(NotificationEntry entry) {
+ final StatusBarNotification sbn = entry.getSbn();
+ ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
+ sbn.getUserId(),
+ sbn.getPackageName());
+ if (activeOps != null) {
+ synchronized (entry.mActiveAppOps) {
+ entry.mActiveAppOps.clear();
+ entry.mActiveAppOps.addAll(activeOps);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index ddbabee283c2..30a60abfcd86 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -16,6 +16,8 @@
package com.android.systemui;
+import static android.os.PowerManager.WAKE_REASON_UNKNOWN;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -28,21 +30,32 @@ import android.os.SystemClock;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
/**
* Class that only runs on debuggable builds that listens to broadcasts that simulate actions in the
* system that are used for testing the latency.
*/
+@Singleton
public class LatencyTester extends SystemUI {
- private static final String ACTION_FINGERPRINT_WAKE =
+ private static final String
+ ACTION_FINGERPRINT_WAKE =
"com.android.systemui.latency.ACTION_FINGERPRINT_WAKE";
- private static final String ACTION_TURN_ON_SCREEN =
+ private static final String
+ ACTION_TURN_ON_SCREEN =
"com.android.systemui.latency.ACTION_TURN_ON_SCREEN";
+ private final BiometricUnlockController mBiometricUnlockController;
+ private final PowerManager mPowerManager;
- public LatencyTester(Context context) {
+ @Inject
+ public LatencyTester(Context context, BiometricUnlockController biometricUnlockController,
+ PowerManager powerManager) {
super(context);
+ mBiometricUnlockController = biometricUnlockController;
+ mPowerManager = powerManager;
}
@Override
@@ -68,19 +81,17 @@ public class LatencyTester extends SystemUI {
}
private void fakeTurnOnScreen() {
- PowerManager powerManager = mContext.getSystemService(PowerManager.class);
if (LatencyTracker.isEnabled(mContext)) {
LatencyTracker.getInstance(mContext).onActionStart(
LatencyTracker.ACTION_TURN_ON_SCREEN);
}
- powerManager.wakeUp(SystemClock.uptimeMillis(), "android.policy:LATENCY_TESTS");
+ mPowerManager.wakeUp(
+ SystemClock.uptimeMillis(), WAKE_REASON_UNKNOWN, "android.policy:LATENCY_TESTS");
}
private void fakeWakeAndUnlock() {
- BiometricUnlockController biometricUnlockController = getComponent(StatusBar.class)
- .getBiometricUnlockController();
- biometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
- biometricUnlockController.onBiometricAuthenticated(
+ mBiometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
+ mBiometricUnlockController.onBiometricAuthenticated(
KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index ad209861d273..8a99e451934e 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -81,10 +81,16 @@ import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
/**
* An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
* for antialiasing and emulation purposes.
*/
+@Singleton
public class ScreenDecorations extends SystemUI implements Tunable {
private static final boolean DEBUG = false;
private static final String TAG = "ScreenDecorations";
@@ -94,6 +100,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
private static final boolean VERBOSE = false;
+ private final Lazy<StatusBar> mStatusBarLazy;
private DisplayManager mDisplayManager;
private DisplayManager.DisplayListener mDisplayListener;
@@ -132,8 +139,10 @@ public class ScreenDecorations extends SystemUI implements Tunable {
return result;
}
- public ScreenDecorations(Context context) {
+ @Inject
+ public ScreenDecorations(Context context, Lazy<StatusBar> statusBarLazy) {
super(context);
+ mStatusBarLazy = statusBarLazy;
}
@Override
@@ -434,13 +443,13 @@ public class ScreenDecorations extends SystemUI implements Tunable {
private void setupStatusBarPadding(int padding) {
// Add some padding to all the content near the edge of the screen.
- StatusBar sb = getComponent(StatusBar.class);
- View statusBar = (sb != null ? sb.getStatusBarWindow() : null);
- if (statusBar != null) {
- TunablePadding.addTunablePadding(statusBar.findViewById(R.id.keyguard_header), PADDING,
- padding, FLAG_END);
+ StatusBar statusBar = mStatusBarLazy.get();
+ View statusBarWindow = statusBar.getStatusBarWindow();
+ if (statusBarWindow != null) {
+ TunablePadding.addTunablePadding(statusBarWindow.findViewById(R.id.keyguard_header),
+ PADDING, padding, FLAG_END);
- FragmentHostManager fragmentHostManager = FragmentHostManager.get(statusBar);
+ FragmentHostManager fragmentHostManager = FragmentHostManager.get(statusBarWindow);
fragmentHostManager.addTagListener(CollapsedStatusBarFragment.TAG,
new TunablePaddingTagListener(padding, R.id.status_bar));
fragmentHostManager.addTagListener(QS.TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index b75873100025..446ed2572bab 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -93,8 +93,11 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
Log.w(TAG, "Evicting client due to: " + topPackage);
mCurrentDialog.dismissWithoutCallback(true /* animate */);
mCurrentDialog = null;
- mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
- mReceiver = null;
+ if (mReceiver != null) {
+ mReceiver.onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ mReceiver = null;
+ }
}
}
} catch (RemoteException e) {
@@ -105,6 +108,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
@Override
public void onTryAgainPressed() {
+ if (mReceiver == null) {
+ Log.e(TAG, "onTryAgainPressed: Receiver is null");
+ return;
+ }
try {
mReceiver.onTryAgainPressed();
} catch (RemoteException e) {
@@ -114,6 +121,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
@Override
public void onDeviceCredentialPressed() {
+ if (mReceiver == null) {
+ Log.e(TAG, "onDeviceCredentialPressed: Receiver is null");
+ return;
+ }
try {
mReceiver.onDeviceCredentialPressed();
} catch (RemoteException e) {
@@ -161,7 +172,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
private void sendResultAndCleanUp(@DismissedReason int reason) {
if (mReceiver == null) {
- Log.e(TAG, "Receiver is null");
+ Log.e(TAG, "sendResultAndCleanUp: Receiver is null");
return;
}
try {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
index 6674c12ab613..9032c6fc8639 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
@@ -20,6 +20,7 @@ import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.appops.AppOpsControllerImpl;
import com.android.systemui.classifier.FalsingManagerProxy;
+import com.android.systemui.doze.DozeHost;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.FalsingManager;
@@ -33,6 +34,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
+import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -241,5 +243,10 @@ public abstract class DependencyBinder {
/**
*/
@Binds
- public abstract FalsingManager provideFalsingmanager(FalsingManagerProxy falsingManagerImpl);
+ public abstract FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl);
+
+ /**
+ */
+ @Binds
+ public abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 434e2d3f4791..fffba8cc1639 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -34,6 +34,7 @@ import android.view.IWindowManager;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import com.android.internal.util.LatencyTracker;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.dagger.qualifiers.BgHandler;
import com.android.systemui.dagger.qualifiers.MainResources;
@@ -58,12 +59,13 @@ public class SystemServicesModule {
@Singleton
@Provides
- static IActivityManager providesIActivityManager() {
+ static IActivityManager provideIActivityManager() {
return ActivityManager.getService();
}
@Provides
- static IWallpaperManager provideWallPaperManager() {
+ @Nullable
+ static IWallpaperManager provideIWallPaperManager() {
return IWallpaperManager.Stub.asInterface(
ServiceManager.getService(Context.WALLPAPER_SERVICE));
}
@@ -74,6 +76,12 @@ public class SystemServicesModule {
return WindowManagerGlobal.getWindowManagerService();
}
+ @Singleton
+ @Provides
+ static LatencyTracker provideLatencyTracker(Context context) {
+ return LatencyTracker.getInstance(context);
+ }
+
@SuppressLint("MissingPermission")
@Singleton
@Provides
@@ -109,13 +117,13 @@ public class SystemServicesModule {
}
@Provides
- static WallpaperManager providesWallpaperManager(Context context) {
+ static WallpaperManager provideWallpaperManager(Context context) {
return (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
}
@Singleton
@Provides
- static WindowManager providesWindowManager(Context context) {
+ static WindowManager provideWindowManager(Context context) {
return context.getSystemService(WindowManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 49cd414f9d9e..738f53920b98 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -16,6 +16,8 @@
package com.android.systemui.dagger;
+import com.android.systemui.LatencyTester;
+import com.android.systemui.ScreenDecorations;
import com.android.systemui.SystemUI;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.pip.PipUI;
@@ -48,6 +50,12 @@ public abstract class SystemUIBinder {
@ClassKey(KeyguardViewMediator.class)
public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui);
+ /** Inject into LatencyTests. */
+ @Binds
+ @IntoMap
+ @ClassKey(LatencyTester.class)
+ public abstract SystemUI bindLatencyTester(LatencyTester sysui);
+
/** Inject into PipUI. */
@Binds
@IntoMap
@@ -66,6 +74,12 @@ public abstract class SystemUIBinder {
@ClassKey(Recents.class)
public abstract SystemUI bindRecents(Recents sysui);
+ /** Inject into ScreenDecorations. */
+ @Binds
+ @IntoMap
+ @ClassKey(ScreenDecorations.class)
+ public abstract SystemUI bindScreenDecorations(ScreenDecorations sysui);
+
/** Inject into VolumeUI. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 30f13979e66e..4e60f194e552 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
+import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -36,7 +37,9 @@ import dagger.Provides;
* A dagger module for injecting components of System UI that are not overridden by the System UI
* implementation.
*/
-@Module(includes = {AssistModule.class, ComponentBinder.class, PeopleHubModule.class})
+@Module(includes = {AssistModule.class,
+ ComponentBinder.class,
+ PeopleHubModule.class})
public abstract class SystemUIModule {
@Singleton
@@ -44,11 +47,13 @@ public abstract class SystemUIModule {
@Nullable
static KeyguardLiftController provideKeyguardLiftController(Context context,
StatusBarStateController statusBarStateController,
- AsyncSensorManager asyncSensorManager) {
+ AsyncSensorManager asyncSensorManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
return null;
}
- return new KeyguardLiftController(statusBarStateController, asyncSensorManager);
+ return new KeyguardLiftController(statusBarStateController, asyncSensorManager,
+ keyguardUpdateMonitor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index a36ff0de3df8..33f68cfc1492 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -57,6 +57,7 @@ public class DozeFactory {
private final ProximitySensor mProximitySensor;
private final DelayedWakeLock.Builder mDelayedWakeLockBuilder;
private final Handler mHandler;
+ private final BiometricUnlockController mBiometricUnlockController;
@Inject
public DozeFactory(FalsingManager falsingManager, DozeLog dozeLog,
@@ -65,7 +66,8 @@ public class DozeFactory {
WakefulnessLifecycle wakefulnessLifecycle, KeyguardUpdateMonitor keyguardUpdateMonitor,
DockManager dockManager, @Nullable IWallpaperManager wallpaperManager,
ProximitySensor proximitySensor,
- DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler) {
+ DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
+ BiometricUnlockController biometricUnlockController) {
mFalsingManager = falsingManager;
mDozeLog = dozeLog;
mDozeParameters = dozeParameters;
@@ -79,6 +81,7 @@ public class DozeFactory {
mProximitySensor = proximitySensor;
mDelayedWakeLockBuilder = delayedWakeLockBuilder;
mHandler = handler;
+ mBiometricUnlockController = biometricUnlockController;
}
/** Creates a DozeMachine with its parts for {@code dozeService}. */
@@ -107,9 +110,7 @@ public class DozeFactory {
createDozeScreenBrightness(dozeService, wrappedService, mAsyncSensorManager, host,
mDozeParameters, mHandler),
new DozeWallpaperState(
- mWallpaperManager,
- getBiometricUnlockController(dozeService),
- mDozeParameters),
+ mWallpaperManager, mBiometricUnlockController, mDozeParameters),
new DozeDockHandler(dozeService, machine, host, config, mHandler, mDockManager),
new DozeAuthRemover(dozeService)
});
@@ -149,10 +150,4 @@ public class DozeFactory {
final SystemUIApplication app = (SystemUIApplication) appCandidate;
return app.getComponent(DozeHost.class);
}
-
- public static BiometricUnlockController getBiometricUnlockController(DozeService service) {
- Application appCandidate = service.getApplication();
- final SystemUIApplication app = (SystemUIApplication) appCandidate;
- return app.getComponent(BiometricUnlockController.class);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 1a6bd60816b3..d1047e216ec4 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -68,6 +68,12 @@ public interface DozeHost {
*/
void prepareForGentleSleep(Runnable onDisplayOffCallback);
+ /**
+ * Cancel pending {@code onDisplayOffCallback} callback.
+ * @see #prepareForGentleSleep(Runnable)
+ */
+ void cancelGentleSleep();
+
void onIgnoreTouchWhilePulsing(boolean ignore);
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 95c42fcd175c..e1b4f3122861 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -71,7 +71,7 @@ public class DozeScreenState implements DozeMachine.Part {
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
int screenState = newState.screenState(mParameters);
- mDozeHost.prepareForGentleSleep(null);
+ mDozeHost.cancelGentleSleep();
if (newState == DozeMachine.State.FINISH) {
// Make sure not to apply the screen state after DozeService was destroyed.
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 5723afd4ae95..c452f64ff6f0 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -90,7 +90,7 @@ public class PipMenuActivity extends Activity {
public static final int MESSAGE_UPDATE_ACTIONS = 4;
public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
public static final int MESSAGE_ANIMATION_ENDED = 6;
- public static final int MESSAGE_TOUCH_EVENT = 7;
+ public static final int MESSAGE_POINTER_EVENT = 7;
private static final int INITIAL_DISMISS_DELAY = 3500;
private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
@@ -165,9 +165,9 @@ public class PipMenuActivity extends Activity {
break;
}
- case MESSAGE_TOUCH_EVENT: {
+ case MESSAGE_POINTER_EVENT: {
final MotionEvent ev = (MotionEvent) msg.obj;
- dispatchTouchEvent(ev);
+ dispatchPointerEvent(ev);
break;
}
}
@@ -219,6 +219,9 @@ public class PipMenuActivity extends Activity {
updateFromIntent(getIntent());
setTitle(R.string.pip_menu_title);
setDisablePreviewScreenshots(true);
+
+ // Hide without an animation.
+ getWindow().setExitTransition(null);
}
@Override
@@ -269,6 +272,17 @@ public class PipMenuActivity extends Activity {
}
}
+ /**
+ * Dispatch a pointer event from {@link PipTouchHandler}.
+ */
+ private void dispatchPointerEvent(MotionEvent event) {
+ if (event.isTouchEvent()) {
+ dispatchTouchEvent(event);
+ } else {
+ dispatchGenericMotionEvent(event);
+ }
+ }
+
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!mAllowTouches) {
@@ -288,8 +302,6 @@ public class PipMenuActivity extends Activity {
public void finish() {
notifyActivityCallback(null);
super.finish();
- // Hide without an animation (the menu should already be invisible at this point)
- overridePendingTransition(0, 0);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 62c59e5842ff..b8e0b81e5e8e 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -508,12 +508,12 @@ public class PipMenuActivityController {
}
/**
- * Handles touch event sent from pip input consumer.
+ * Handles a pointer event sent from pip input consumer.
*/
- void handleTouchEvent(MotionEvent ev) {
+ void handlePointerEvent(MotionEvent ev) {
if (mToActivityMessenger != null) {
Message m = Message.obtain();
- m.what = PipMenuActivity.MESSAGE_TOUCH_EVENT;
+ m.what = PipMenuActivity.MESSAGE_POINTER_EVENT;
m.obj = ev;
try {
mToActivityMessenger.send(m);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 1f36d97ce308..f59b372762bd 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -132,6 +132,7 @@ public class PipTouchHandler {
private boolean mSendingHoverAccessibilityEvents;
private boolean mMovementWithinMinimize;
private boolean mMovementWithinDismiss;
+ private PipAccessibilityInteractionConnection mConnection;
// Touch state
private final PipTouchState mTouchState;
@@ -213,9 +214,10 @@ public class PipTouchHandler {
// Register the listener for input consumer touch events
inputConsumerController.setInputListener(this::handleTouchEvent);
inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
- onRegistrationChanged(inputConsumerController.isRegistered());
mPipBoundsHandler = pipBoundsHandler;
+ mConnection = new PipAccessibilityInteractionConnection(mMotionHelper,
+ this::onAccessibilityShowMenu, mHandler);
}
public void setTouchEnabled(boolean enabled) {
@@ -339,9 +341,7 @@ public class PipTouchHandler {
private void onRegistrationChanged(boolean isRegistered) {
mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
- ? new PipAccessibilityInteractionConnection(mMotionHelper,
- this::onAccessibilityShowMenu, mHandler) : null);
-
+ ? mConnection : null);
if (!isRegistered && mTouchState.isUserInteracting()) {
// If the input consumer is unregistered while the user is interacting, then we may not
// get the final TOUCH_UP event, so clean up the dismiss target as well
@@ -409,27 +409,15 @@ public class PipTouchHandler {
}
case MotionEvent.ACTION_HOVER_ENTER:
case MotionEvent.ACTION_HOVER_MOVE: {
- if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) {
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
- event.setImportantForAccessibility(true);
- event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
- event.setWindowId(
- AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
- mAccessibilityManager.sendAccessibilityEvent(event);
+ if (!shouldDeliverToMenu && !mSendingHoverAccessibilityEvents) {
+ sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
mSendingHoverAccessibilityEvents = true;
}
break;
}
case MotionEvent.ACTION_HOVER_EXIT: {
- if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) {
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
- event.setImportantForAccessibility(true);
- event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
- event.setWindowId(
- AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
- mAccessibilityManager.sendAccessibilityEvent(event);
+ if (!shouldDeliverToMenu && mSendingHoverAccessibilityEvents) {
+ sendAccessibilityHoverEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
mSendingHoverAccessibilityEvents = false;
}
break;
@@ -445,12 +433,25 @@ public class PipTouchHandler {
mMenuController.pokeMenu();
}
- mMenuController.handleTouchEvent(cloneEvent);
+ mMenuController.handlePointerEvent(cloneEvent);
}
return true;
}
+ private void sendAccessibilityHoverEvent(int type) {
+ if (!mAccessibilityManager.isEnabled()) {
+ return;
+ }
+
+ AccessibilityEvent event = AccessibilityEvent.obtain(type);
+ event.setImportantForAccessibility(true);
+ event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
+ event.setWindowId(
+ AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+
/**
* Updates the appearance of the menu and scrim on top of the PiP while dismissing.
*/
@@ -523,6 +524,10 @@ public class PipTouchHandler {
* Sets the menu visibility.
*/
private void setMenuState(int menuState, boolean resize) {
+ if (mMenuState == menuState && !resize) {
+ return;
+ }
+
if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) {
// Save the current snap fraction and if we do not drag or move the PiP, then
// we store back to this snap fraction. Otherwise, we'll reset the snap
@@ -571,6 +576,9 @@ public class PipTouchHandler {
}
mMenuState = menuState;
updateMovementBounds(menuState);
+ // If pip menu has dismissed, we should register the A11y ActionReplacingConnection for pip
+ // as well, or it can't handle a11y focus and pip menu can't perform any action.
+ onRegistrationChanged(menuState == MENU_STATE_NONE);
if (menuState != MENU_STATE_CLOSE) {
MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL);
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 98f0b2a35f9f..f60d9db7ac9c 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -55,7 +55,11 @@ import java.util.Arrays;
import java.util.concurrent.Future;
import javax.inject.Inject;
+import javax.inject.Singleton;
+import dagger.Lazy;
+
+@Singleton
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
@@ -101,11 +105,14 @@ public class PowerUI extends SystemUI {
private IThermalEventListener mSkinThermalEventListener;
private IThermalEventListener mUsbThermalEventListener;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final Lazy<StatusBar> mStatusBarLazy;
@Inject
- public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher) {
+ public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
+ Lazy<StatusBar> statusBarLazy) {
super(context);
mBroadcastDispatcher = broadcastDispatcher;
+ mStatusBarLazy = statusBarLazy;
}
public void start() {
@@ -663,8 +670,7 @@ public class PowerUI extends SystemUI {
int status = temp.getStatus();
if (status >= Temperature.THROTTLING_EMERGENCY) {
- StatusBar statusBar = getComponent(StatusBar.class);
- if (statusBar != null && !statusBar.isDeviceInVrMode()) {
+ if (!mStatusBarLazy.get().isDeviceInVrMode()) {
mWarnings.showHighTemperatureWarning();
Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
+ ", current skin status = " + status
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
index af418f6308a0..694964075032 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java
@@ -33,6 +33,7 @@ import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
+import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -50,7 +51,6 @@ import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.MediaTransferManager;
/**
* Single media player for carousel in QSPanel
@@ -101,6 +101,12 @@ public class QSMediaPlayer {
mToken = token;
mController = new MediaController(mContext, token);
MediaMetadata mMediaMetadata = mController.getMetadata();
+
+ if (mMediaMetadata == null) {
+ Log.e(TAG, "Media metadata was null");
+ return;
+ }
+
Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
// Album art
@@ -151,18 +157,17 @@ public class QSMediaPlayer {
// Album name
TextView albumName = headerView.findViewById(com.android.internal.R.id.header_text);
String albumString = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
- if (!albumString.isEmpty()) {
+ if (TextUtils.isEmpty(albumString)) {
+ albumName.setVisibility(View.GONE);
+ separator.setVisibility(View.GONE);
+ } else {
albumName.setText(albumString);
albumName.setTextColor(iconColor);
albumName.setVisibility(View.VISIBLE);
separator.setVisibility(View.VISIBLE);
- } else {
- albumName.setVisibility(View.GONE);
- separator.setVisibility(View.GONE);
}
// Transfer chip
- MediaTransferManager mediaTransferManager = new MediaTransferManager(mContext);
View transferBackgroundView = headerView.findViewById(
com.android.internal.R.id.media_seamless);
LinearLayout viewLayout = (LinearLayout) transferBackgroundView;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 20600596c3a1..b48814bfd6bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -166,6 +166,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
mMediaCarousel = new LinearLayout(mContext);
mMediaCarousel.setOrientation(LinearLayout.HORIZONTAL);
mediaScrollView.addView(mMediaCarousel, lpCarousel);
+ mediaScrollView.setVisibility(View.GONE);
} else {
mMediaCarousel = null;
}
@@ -239,6 +240,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
} else {
mMediaCarousel.addView(player.getView(), lp); // add at end
}
+ mMediaPlayers.add(player);
} else if (player.isPlaying()) {
// move it to the front
mMediaCarousel.removeView(player.getView());
@@ -248,7 +250,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
Log.d(TAG, "setting player session");
player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
notif.getNotification());
- mMediaPlayers.add(player);
+
+ if (mMediaPlayers.size() > 0) {
+ ((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE);
+ }
}
protected View getMediaPanel() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
index ae66cd576765..3ec71ac30da1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java
@@ -84,6 +84,11 @@ public class QuickQSMediaPlayer {
mController = new MediaController(mContext, token);
MediaMetadata mMediaMetadata = mController.getMetadata();
+ if (mMediaMetadata == null) {
+ Log.e(TAG, "Media metadata was null");
+ return;
+ }
+
// Album art
addAlbumArtBackground(mMediaMetadata, bgColor);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 1c8e451e52f7..9a33c8c825cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -145,7 +145,7 @@ public class QSFactoryImpl implements QSFactory {
return mBluetoothTileProvider.get();
case "controls":
if (Settings.System.getInt(mHost.getContext().getContentResolver(),
- "qs_controls_tile_enabled", 0) == 1) {
+ "npv_plugin_flag", 0) == 3) {
return mControlsTileProvider.get();
} else return null;
case "cell":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java
index 0a59618b59a1..39ae66e7607a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ControlsTile.java
@@ -22,11 +22,11 @@ import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.HomeControlsPlugin;
+import com.android.systemui.plugins.NPVPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -44,7 +44,7 @@ public class ControlsTile extends QSTileImpl<BooleanState> {
private ControlsDetailAdapter mDetailAdapter;
private final ActivityStarter mActivityStarter;
private PluginManager mPluginManager;
- private HomeControlsPlugin mPlugin;
+ private NPVPlugin mPlugin;
private Intent mHomeAppIntent;
@Inject
@@ -81,7 +81,7 @@ public class ControlsTile extends QSTileImpl<BooleanState> {
public void setDetailListening(boolean listening) {
if (mPlugin == null) return;
- mPlugin.setVisible(listening);
+ mPlugin.setListening(listening);
}
@Override
@@ -142,7 +142,7 @@ public class ControlsTile extends QSTileImpl<BooleanState> {
private class ControlsDetailAdapter implements DetailAdapter {
private View mDetailView;
- protected LinearLayout mHomeControlsLayout;
+ protected FrameLayout mHomeControlsLayout;
public CharSequence getTitle() {
return "Controls";
@@ -157,24 +157,30 @@ public class ControlsTile extends QSTileImpl<BooleanState> {
}
public View createDetailView(Context context, View convertView, final ViewGroup parent) {
- mHomeControlsLayout = (LinearLayout) LayoutInflater.from(context).inflate(
- R.layout.home_controls, parent, false);
+ if (convertView != null) return convertView;
+
+ mHomeControlsLayout = (FrameLayout) LayoutInflater.from(context).inflate(
+ R.layout.home_controls, parent, false);
mHomeControlsLayout.setVisibility(View.VISIBLE);
+ parent.addView(mHomeControlsLayout);
+
mPluginManager.addPluginListener(
- new PluginListener<HomeControlsPlugin>() {
+ new PluginListener<NPVPlugin>() {
@Override
- public void onPluginConnected(HomeControlsPlugin plugin,
+ public void onPluginConnected(NPVPlugin plugin,
Context pluginContext) {
mPlugin = plugin;
- mPlugin.sendParentGroup(mHomeControlsLayout);
- mPlugin.setVisible(true);
+ mPlugin.attachToRoot(mHomeControlsLayout);
+ mPlugin.setListening(true);
}
@Override
- public void onPluginDisconnected(HomeControlsPlugin plugin) {
+ public void onPluginDisconnected(NPVPlugin plugin) {
+ mPlugin.setListening(false);
+ mHomeControlsLayout.removeAllViews();
}
- }, HomeControlsPlugin.class, false);
+ }, NPVPlugin.class, false);
return mHomeControlsLayout;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index f284f737b26d..53fbe5b57d7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -155,7 +156,8 @@ public class InstantAppNotifier extends SystemUI
focusedStack.configuration.windowConfiguration
.getWindowingMode();
if (windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ || windowingMode == WINDOWING_MODE_FREEFORM) {
checkAndPostForStack(focusedStack, notifs, noMan, pm);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index dfc64508cadf..df78fa3fd4a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -20,12 +20,12 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
+import androidx.annotation.NonNull;
+
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
-import androidx.annotation.NonNull;
-
/**
* Listener interface for changes sent by NotificationEntryManager.
*/
@@ -37,13 +37,6 @@ public interface NotificationEntryListener {
default void onPendingEntryAdded(NotificationEntry entry) {
}
- // TODO: Combine this with onPreEntryUpdated into "onBeforeEntryFiltered" or similar
- /**
- * Called when a new entry is created but before it has been filtered or displayed to the user.
- */
- default void onBeforeNotificationAdded(NotificationEntry entry) {
- }
-
/**
* Called when a new entry is created.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 6e464f480218..bde097a89f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -138,6 +138,14 @@ public class NotificationEntryManager implements
mNotificationEntryListeners.add(listener);
}
+ /**
+ * Removes a {@link NotificationEntryListener} previously registered via
+ * {@link #addNotificationEntryListener(NotificationEntryListener)}.
+ */
+ public void removeNotificationEntryListener(NotificationEntryListener listener) {
+ mNotificationEntryListeners.remove(listener);
+ }
+
/** Sets the {@link NotificationRemoveInterceptor}. */
public void setNotificationRemoveInterceptor(NotificationRemoveInterceptor interceptor) {
mRemoveInterceptor = interceptor;
@@ -254,9 +262,6 @@ public class NotificationEntryManager implements
listener.onEntryInflated(entry, inflatedFlags);
}
mNotificationData.add(entry);
- for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onBeforeNotificationAdded(entry);
- }
updateNotifications("onAsyncInflationFinished");
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onNotificationAdded(entry);
@@ -555,6 +560,18 @@ public class NotificationEntryManager implements
return mPendingNotifications.values();
}
+ /**
+ * Gets the pending or visible notification entry with the given key. Returns null if
+ * notification doesn't exist.
+ */
+ public NotificationEntry getPendingOrCurrentNotif(String key) {
+ if (mPendingNotifications.containsKey(key)) {
+ return mPendingNotifications.get(key);
+ } else {
+ return mNotificationData.get(key);
+ }
+ }
+
private void extendLifetime(NotificationEntry entry, NotificationLifetimeExtender extender) {
NotificationLifetimeExtender activeExtender = mRetainedNotifications.get(entry);
if (activeExtender != null && activeExtender != extender) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
index 533dfb6ee4aa..2eefe29cfbba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
@@ -18,10 +18,6 @@ package com.android.systemui.statusbar.notification;
import static com.android.internal.util.Preconditions.checkNotNull;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.util.ArraySet;
-
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -70,11 +66,6 @@ public class NotificationListController {
boolean removedByUser) {
mListContainer.cleanUpViewStateForEntry(entry);
}
-
- @Override
- public void onBeforeNotificationAdded(NotificationEntry entry) {
- tagForeground(entry.getSbn());
- }
};
private final DeviceProvisionedListener mDeviceProvisionedListener =
@@ -84,29 +75,4 @@ public class NotificationListController {
mEntryManager.updateNotifications("device provisioned changed");
}
};
-
- // TODO: This method is horrifically inefficient
- private void tagForeground(StatusBarNotification notification) {
- ArraySet<Integer> activeOps =
- mForegroundServiceController.getAppOps(
- notification.getUserId(), notification.getPackageName());
- if (activeOps != null) {
- int len = activeOps.size();
- for (int i = 0; i < len; i++) {
- updateNotificationsForAppOp(activeOps.valueAt(i), notification.getUid(),
- notification.getPackageName(), true);
- }
- }
- }
-
- /** When an app op changes, propagate that change to notifications. */
- public void updateNotificationsForAppOp(int appOp, int uid, String pkg, boolean showIcon) {
- String foregroundKey =
- mForegroundServiceController.getStandardLayoutKey(UserHandle.getUserId(uid), pkg);
- if (foregroundKey != null) {
- mEntryManager
- .getNotificationData().updateAppOp(appOp, uid, pkg, foregroundKey, showIcon);
- mEntryManager.updateNotifications("app opp changed pkg=" + pkg);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
index 7e398bb70433..9981c93431e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -37,6 +37,7 @@ import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.logging.NotifEvent;
import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -76,12 +77,16 @@ public class NotificationData {
private final Ranking mTmpRanking = new Ranking();
private final boolean mUsePeopleFiltering;
private final NotifLog mNotifLog;
+ private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@Inject
- public NotificationData(NotificationSectionsFeatureManager sectionsFeatureManager,
- NotifLog notifLog) {
+ public NotificationData(
+ NotificationSectionsFeatureManager sectionsFeatureManager,
+ NotifLog notifLog,
+ PeopleNotificationIdentifier peopleNotificationIdentifier) {
mUsePeopleFiltering = sectionsFeatureManager.isFilteringEnabled();
mNotifLog = notifLog;
+ mPeopleNotificationIdentifier = peopleNotificationIdentifier;
}
public void setHeadsUpManager(HeadsUpManager headsUpManager) {
@@ -220,24 +225,6 @@ public class NotificationData {
updateRankingAndSort(ranking, reason);
}
- public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
- synchronized (mEntries) {
- final int len = mEntries.size();
- for (int i = 0; i < len; i++) {
- NotificationEntry entry = mEntries.valueAt(i);
- if (uid == entry.getSbn().getUid()
- && pkg.equals(entry.getSbn().getPackageName())
- && key.equals(entry.getKey())) {
- if (showIcon) {
- entry.mActiveAppOps.add(appOp);
- } else {
- entry.mActiveAppOps.remove(appOp);
- }
- }
- }
- }
- }
-
/**
* Returns true if this notification should be displayed in the high-priority notifications
* section
@@ -460,8 +447,7 @@ public class NotificationData {
}
private boolean isPeopleNotification(NotificationEntry e) {
- return e.getSbn().getNotification().getNotificationStyle()
- == Notification.MessagingStyle.class;
+ return mPeopleNotificationIdentifier.isPeopleNotification(e.getSbn());
}
public void dump(PrintWriter pw, String indent) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 71fc5490d656..a4c8fc45938f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -130,7 +130,7 @@ public final class NotificationEntry {
private Throwable mDebugThrowable;
public CharSequence remoteInputTextWhenReset;
public long lastRemoteInputSent = NOT_LAUNCHED_YET;
- public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
+ public final ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
public CharSequence headsUpStatusBarText;
public CharSequence headsUpStatusBarTextPublic;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
index 8c067b79482c..45709893a7b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt
@@ -24,14 +24,24 @@ abstract class PeopleHubModule {
@Binds
abstract fun peopleHubSectionFooterViewController(
- viewAdapter: PeopleHubSectionFooterViewAdapterImpl
+ impl: PeopleHubSectionFooterViewAdapterImpl
): PeopleHubSectionFooterViewAdapter
@Binds
- abstract fun peopleHubDataSource(s: PeopleHubDataSourceImpl): DataSource<PeopleHubModel>
+ abstract fun peopleHubDataSource(impl: PeopleHubDataSourceImpl): DataSource<PeopleHubModel>
@Binds
abstract fun peopleHubViewModelFactoryDataSource(
- dataSource: PeopleHubViewModelFactoryDataSourceImpl
+ impl: PeopleHubViewModelFactoryDataSourceImpl
): DataSource<PeopleHubViewModelFactory>
+
+ @Binds
+ abstract fun peopleNotificationIdentifier(
+ impl: PeopleNotificationIdentifierImpl
+ ): PeopleNotificationIdentifier
+
+ @Binds
+ abstract fun notificationPersonExtractor(
+ pluginImpl: NotificationPersonExtractorPluginBoundary
+ ): NotificationPersonExtractor
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
index c8a3c1af914d..fe257d9fd252 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt
@@ -17,11 +17,14 @@
package com.android.systemui.statusbar.notification.people
import android.app.Notification
+import android.content.Context
import android.graphics.Canvas
import android.graphics.ColorFilter
import android.graphics.PixelFormat
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.service.notification.StatusBarNotification
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
@@ -30,59 +33,112 @@ import com.android.internal.statusbar.NotificationVisibility
import com.android.internal.widget.MessagingGroup
import com.android.launcher3.icons.BaseIconFactory
import com.android.systemui.R
+import com.android.systemui.plugins.NotificationPersonExtractorPlugin
import com.android.systemui.statusbar.notification.NotificationEntryListener
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.policy.ExtensionController
import java.util.ArrayDeque
import javax.inject.Inject
import javax.inject.Singleton
private const val MAX_STORED_INACTIVE_PEOPLE = 10
+interface NotificationPersonExtractor {
+ fun extractPerson(sbn: StatusBarNotification): PersonModel?
+ fun extractPersonKey(sbn: StatusBarNotification): String?
+}
+
+@Singleton
+class NotificationPersonExtractorPluginBoundary @Inject constructor(
+ extensionController: ExtensionController,
+ private val context: Context
+) : NotificationPersonExtractor {
+
+ private var plugin: NotificationPersonExtractorPlugin? = null
+
+ init {
+ plugin = extensionController
+ .newExtension(NotificationPersonExtractorPlugin::class.java)
+ .withPlugin(NotificationPersonExtractorPlugin::class.java)
+ .withCallback { extractor ->
+ plugin = extractor
+ }
+ .build()
+ .get()
+ }
+
+ override fun extractPerson(sbn: StatusBarNotification) =
+ plugin?.extractPerson(sbn)?.let { data ->
+ val badged = addBadgeToDrawable(data.avatar, context, sbn.packageName, sbn.user)
+ PersonModel(data.key, data.name, badged, data.clickIntent)
+ }
+
+ override fun extractPersonKey(sbn: StatusBarNotification) = plugin?.extractPersonKey(sbn)
+}
+
@Singleton
class PeopleHubDataSourceImpl @Inject constructor(
- notificationEntryManager: NotificationEntryManager,
- private val peopleHubManager: PeopleHubManager
+ private val notificationEntryManager: NotificationEntryManager,
+ private val peopleHubManager: PeopleHubManager,
+ private val extractor: NotificationPersonExtractor
) : DataSource<PeopleHubModel> {
- private var dataListener: DataListener<PeopleHubModel>? = null
+ private val dataListeners = mutableListOf<DataListener<PeopleHubModel>>()
- init {
- notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
- override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) =
- addVisibleEntry(entry)
+ private val notificationEntryListener = object : NotificationEntryListener {
+ override fun onEntryInflated(entry: NotificationEntry, inflatedFlags: Int) =
+ addVisibleEntry(entry)
- override fun onEntryReinflated(entry: NotificationEntry) = addVisibleEntry(entry)
+ override fun onEntryReinflated(entry: NotificationEntry) = addVisibleEntry(entry)
- override fun onPostEntryUpdated(entry: NotificationEntry) = addVisibleEntry(entry)
+ override fun onPostEntryUpdated(entry: NotificationEntry) = addVisibleEntry(entry)
- override fun onEntryRemoved(
- entry: NotificationEntry,
- visibility: NotificationVisibility?,
- removedByUser: Boolean
- ) = removeVisibleEntry(entry)
- })
+ override fun onEntryRemoved(
+ entry: NotificationEntry,
+ visibility: NotificationVisibility?,
+ removedByUser: Boolean
+ ) = removeVisibleEntry(entry)
}
private fun removeVisibleEntry(entry: NotificationEntry) {
- if (entry.extractPersonKey()?.let(peopleHubManager::removeActivePerson) == true) {
+ val key = extractor.extractPersonKey(entry.sbn) ?: entry.extractPersonKey()
+ if (key?.let(peopleHubManager::removeActivePerson) == true) {
updateUi()
}
}
private fun addVisibleEntry(entry: NotificationEntry) {
- if (entry.extractPerson()?.let(peopleHubManager::addActivePerson) == true) {
+ val personModel = extractor.extractPerson(entry.sbn) ?: entry.extractPerson()
+ if (personModel?.let(peopleHubManager::addActivePerson) == true) {
updateUi()
}
}
- override fun setListener(listener: DataListener<PeopleHubModel>) {
- this.dataListener = listener
- updateUi()
+ override fun registerListener(listener: DataListener<PeopleHubModel>): Subscription {
+ val registerWithNotificationEntryManager = dataListeners.isEmpty()
+ dataListeners.add(listener)
+ if (registerWithNotificationEntryManager) {
+ notificationEntryManager.addNotificationEntryListener(notificationEntryListener)
+ } else {
+ listener.onDataChanged(peopleHubManager.getPeopleHubModel())
+ }
+ return object : Subscription {
+ override fun unsubscribe() {
+ dataListeners.remove(listener)
+ if (dataListeners.isEmpty()) {
+ notificationEntryManager
+ .removeNotificationEntryListener(notificationEntryListener)
+ }
+ }
+ }
}
private fun updateUi() {
- dataListener?.onDataChanged(peopleHubManager.getPeopleHubModel())
+ val model = peopleHubManager.getPeopleHubModel()
+ for (listener in dataListeners) {
+ listener.onDataChanged(model)
+ }
}
}
@@ -124,19 +180,26 @@ private fun NotificationEntry.extractPerson(): PersonModel? {
if (!isMessagingNotification()) {
return null
}
-
val clickIntent = sbn.notification.contentIntent
+ ?: return null
val extras = sbn.notification.extras
val name = extras.getString(Notification.EXTRA_CONVERSATION_TITLE)
?: extras.getString(Notification.EXTRA_TITLE)
?: return null
val drawable = extractAvatarFromRow(this) ?: return null
+ val badgedAvatar = addBadgeToDrawable(drawable, row.context, sbn.packageName, sbn.user)
+ return PersonModel(key, name, badgedAvatar, clickIntent)
+}
- val context = row.context
+private fun addBadgeToDrawable(
+ drawable: Drawable,
+ context: Context,
+ packageName: String,
+ user: UserHandle
+): Drawable {
val pm = context.packageManager
- val appInfo = pm.getApplicationInfoAsUser(sbn.packageName, 0, sbn.user)
-
- val badgedAvatar = object : Drawable() {
+ val appInfo = pm.getApplicationInfoAsUser(packageName, 0, user)
+ return object : Drawable() {
override fun draw(canvas: Canvas) {
val iconBounds = getBounds()
val factory = object : BaseIconFactory(
@@ -146,7 +209,7 @@ private fun NotificationEntry.extractPerson(): PersonModel? {
true) {}
val badge = factory.createBadgedIconBitmap(
appInfo.loadIcon(pm),
- sbn.user,
+ user,
true,
appInfo.isInstantApp,
null)
@@ -156,7 +219,7 @@ private fun NotificationEntry.extractPerson(): PersonModel? {
colorFilter = drawable.colorFilter
val badgeWidth = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
- 16f,
+ 15f,
context.resources.displayMetrics
).toInt()
setBounds(
@@ -181,8 +244,6 @@ private fun NotificationEntry.extractPerson(): PersonModel? {
@PixelFormat.Opacity
override fun getOpacity(): Int = PixelFormat.OPAQUE
}
-
- return PersonModel(key, name, badgedAvatar, clickIntent)
}
private fun extractAvatarFromRow(entry: NotificationEntry): Drawable? =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
index 8d1253b457cc..5c354089dfe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubViewController.kt
@@ -60,8 +60,9 @@ class PeopleHubSectionFooterViewAdapterImpl @Inject constructor(
private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubViewModelFactory>
) : PeopleHubSectionFooterViewAdapter {
- override fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) =
- dataSource.setListener(PeopleHubDataListenerImpl(viewBoundary))
+ override fun bindView(viewBoundary: PeopleHubSectionFooterViewBoundary) {
+ dataSource.registerListener(PeopleHubDataListenerImpl(viewBoundary))
+ }
}
private class PeopleHubDataListenerImpl(
@@ -92,8 +93,8 @@ class PeopleHubViewModelFactoryDataSourceImpl @Inject constructor(
private val dataSource: DataSource<@JvmSuppressWildcards PeopleHubModel>
) : DataSource<PeopleHubViewModelFactory> {
- override fun setListener(listener: DataListener<PeopleHubViewModelFactory>) =
- dataSource.setListener(PeopleHubModelListenerImpl(activityStarter, listener))
+ override fun registerListener(listener: DataListener<PeopleHubViewModelFactory>) =
+ dataSource.registerListener(PeopleHubModelListenerImpl(activityStarter, listener))
}
private class PeopleHubModelListenerImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
new file mode 100644
index 000000000000..bfd4070846f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.people
+
+import android.app.Notification
+import android.service.notification.StatusBarNotification
+import javax.inject.Inject
+import javax.inject.Singleton
+
+interface PeopleNotificationIdentifier {
+ fun isPeopleNotification(sbn: StatusBarNotification): Boolean
+}
+
+@Singleton
+class PeopleNotificationIdentifierImpl @Inject constructor(
+ private val personExtractor: NotificationPersonExtractor
+) : PeopleNotificationIdentifier {
+
+ override fun isPeopleNotification(sbn: StatusBarNotification) =
+ sbn.notification.notificationStyle == Notification.MessagingStyle::class.java ||
+ personExtractor.extractPersonKey(sbn) != null
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
index 33e3bb883d53..3ca3792a680f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
@@ -28,10 +28,17 @@ fun <S, T> DataListener<T>.contraMap(mapper: (S) -> T): DataListener<S> = object
/** Boundary between a View and data pipeline, as seen by the View. */
interface DataSource<out T> {
- fun setListener(listener: DataListener<T>)
+ fun registerListener(listener: DataListener<T>): Subscription
+}
+
+/** Represents a registration with a [DataSource]. */
+interface Subscription {
+ /** Removes the previously registered [DataListener] from the [DataSource] */
+ fun unsubscribe()
}
/** Transform all data coming out of this [DataSource] using the given [mapper]. */
fun <S, T> DataSource<S>.map(mapper: (S) -> T): DataSource<T> = object : DataSource<T> {
- override fun setListener(listener: DataListener<T>) = setListener(listener.contraMap(mapper))
+ override fun registerListener(listener: DataListener<T>) =
+ registerListener(listener.contraMap(mapper))
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index bd87d7774e2c..54d406615ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -131,6 +131,7 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
}
mInitialized = true;
reinflateViews(layoutInflater);
+ mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
mConfigurationController.addCallback(mConfigurationListener);
}
@@ -172,10 +173,6 @@ public class NotificationSectionsManager implements StackScrollAlgorithm.Section
if (oldPeopleHubPos != -1) {
mParent.addView(mPeopleHubView, oldPeopleHubPos);
}
-
- if (!mInitialized) {
- mPeopleHubViewAdapter.bindView(mPeopleHubViewBoundary);
- }
}
/** Listener for when the "clear all" buttton is clciked on the gentle notification header. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 548afd503061..ffcbc40e7b70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone;
import android.annotation.IntDef;
import android.content.Context;
+import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
import android.metrics.LogMaker;
import android.os.Handler;
@@ -34,6 +35,7 @@ import com.android.keyguard.KeyguardConstants;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
+import com.android.systemui.dagger.qualifiers.MainResources;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -44,15 +46,19 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
/**
* Controller which coordinates all the biometric unlocking actions with the UI.
*/
+@Singleton
public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
- private static final String TAG = "BiometricUnlockController";
+ private static final String TAG = "BiometricUnlockCtrl";
private static final boolean DEBUG_BIO_WAKELOCK = KeyguardConstants.DEBUG_BIOMETRIC_WAKELOCK;
private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
- private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
+ private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
@IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
@@ -128,7 +134,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
private final KeyguardBypassController mKeyguardBypassController;
private PowerManager.WakeLock mWakeLock;
private final KeyguardUpdateMonitor mUpdateMonitor;
- private final DozeParameters mDozeParameters;
+ private DozeParameters mDozeParameters;
private final KeyguardStateController mKeyguardStateController;
private final StatusBarWindowController mStatusBarWindowController;
private final Context mContext;
@@ -145,31 +151,16 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
private boolean mHasScreenTurnedOnSinceAuthenticating;
private boolean mFadedAwayAfterWakeAndUnlock;
- private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
-
- public BiometricUnlockController(
- Context context,
- DozeScrimController dozeScrimController,
- KeyguardViewMediator keyguardViewMediator,
- ScrimController scrimController,
- StatusBar statusBar,
- KeyguardStateController keyguardStateController, Handler handler,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardBypassController keyguardBypassController,
- DozeParameters dozeParameters) {
- this(context, dozeScrimController, keyguardViewMediator, scrimController, statusBar,
- keyguardStateController, handler, keyguardUpdateMonitor,
- context.getResources()
- .getInteger(com.android.internal.R.integer.config_wakeUpDelayDoze),
- keyguardBypassController, dozeParameters);
- }
+ private final MetricsLogger mMetricsLogger;
- @VisibleForTesting
- protected BiometricUnlockController(Context context, DozeScrimController dozeScrimController,
+ @Inject
+ public BiometricUnlockController(Context context, DozeScrimController dozeScrimController,
KeyguardViewMediator keyguardViewMediator, ScrimController scrimController,
StatusBar statusBar, KeyguardStateController keyguardStateController, Handler handler,
- KeyguardUpdateMonitor keyguardUpdateMonitor, int wakeUpDelay,
- KeyguardBypassController keyguardBypassController, DozeParameters dozeParameters) {
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ @MainResources Resources resources,
+ KeyguardBypassController keyguardBypassController, DozeParameters dozeParameters,
+ MetricsLogger metricsLogger) {
mContext = context;
mPowerManager = context.getSystemService(PowerManager.class);
mUpdateMonitor = keyguardUpdateMonitor;
@@ -185,9 +176,10 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
mStatusBar = statusBar;
mKeyguardStateController = keyguardStateController;
mHandler = handler;
- mWakeUpDelay = wakeUpDelay;
+ mWakeUpDelay = resources.getInteger(com.android.internal.R.integer.config_wakeUpDelayDoze);
mKeyguardBypassController = keyguardBypassController;
mKeyguardBypassController.setUnlockController(this);
+ mMetricsLogger = metricsLogger;
}
public void setStatusBarKeyguardViewManager(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 50d33a70fed5..bc482353753d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -24,7 +24,6 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.MainResources;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
@@ -188,12 +187,7 @@ public class DozeParameters implements TunerService.Tunable,
return;
}
mControlScreenOffAnimation = controlScreenOffAnimation;
- getPowerManager().setDozeAfterScreenOff(!controlScreenOffAnimation);
- }
-
- @VisibleForTesting
- protected PowerManager getPowerManager() {
- return mPowerManager;
+ mPowerManager.setDozeAfterScreenOff(!controlScreenOffAnimation);
}
private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index fe3c04e3cda3..1ecc4899d5e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -29,10 +29,12 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import javax.inject.Inject;
+import javax.inject.Singleton;
/**
* Controller which handles all the doze animations of the scrims.
*/
+@Singleton
public class DozeScrimController implements StateListener {
private static final String TAG = "DozeScrimController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
new file mode 100644
index 000000000000..28543555bf4d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.doze.DozeEvent;
+import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.doze.DozeReceiver;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import java.util.ArrayList;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+
+/**
+ * Implementation of DozeHost for SystemUI.
+ */
+@Singleton
+public final class DozeServiceHost implements DozeHost {
+ private static final String TAG = "DozeServiceHost";
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final DozeLog mDozeLog;
+ private final PowerManager mPowerManager;
+ private boolean mAnimateWakeup;
+ private boolean mAnimateScreenOff;
+ private boolean mIgnoreTouchWhilePulsing;
+ private Runnable mPendingScreenOffCallback;
+ @VisibleForTesting
+ boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
+ "persist.sysui.wake_performs_auth", true);
+ private boolean mDozingRequested;
+ private boolean mDozing;
+ private boolean mPulsing;
+ private WakefulnessLifecycle mWakefulnessLifecycle;
+ private final SysuiStatusBarStateController mStatusBarStateController;
+ private final DeviceProvisionedController mDeviceProvisionedController;
+ private final HeadsUpManagerPhone mHeadsUpManagerPhone;
+ private final BatteryController mBatteryController;
+ private final ScrimController mScrimController;
+ private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+ private BiometricUnlockController mBiometricUnlockController;
+ private final KeyguardViewMediator mKeyguardViewMediator;
+ private final AssistManager mAssistManager;
+ private final DozeScrimController mDozeScrimController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final VisualStabilityManager mVisualStabilityManager;
+ private final PulseExpansionHandler mPulseExpansionHandler;
+ private final StatusBarWindowController mStatusBarWindowController;
+ private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
+ private NotificationIconAreaController mNotificationIconAreaController;
+ private StatusBarWindowViewController mStatusBarWindowViewController;
+ private StatusBarWindowView mStatusBarWindow;
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private NotificationPanelView mNotificationPanel;
+ private View mAmbientIndicationContainer;
+ private StatusBar mStatusBar;
+
+ @Inject
+ public DozeServiceHost(DozeLog dozeLog, PowerManager powerManager,
+ WakefulnessLifecycle wakefulnessLifecycle,
+ SysuiStatusBarStateController statusBarStateController,
+ DeviceProvisionedController deviceProvisionedController,
+ HeadsUpManagerPhone headsUpManagerPhone, BatteryController batteryController,
+ ScrimController scrimController,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ KeyguardViewMediator keyguardViewMediator,
+ AssistManager assistManager,
+ DozeScrimController dozeScrimController, KeyguardUpdateMonitor keyguardUpdateMonitor,
+ VisualStabilityManager visualStabilityManager,
+ PulseExpansionHandler pulseExpansionHandler,
+ StatusBarWindowController statusBarWindowController,
+ NotificationWakeUpCoordinator notificationWakeUpCoordinator) {
+ super();
+ mDozeLog = dozeLog;
+ mPowerManager = powerManager;
+ mWakefulnessLifecycle = wakefulnessLifecycle;
+ mStatusBarStateController = statusBarStateController;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mHeadsUpManagerPhone = headsUpManagerPhone;
+ mBatteryController = batteryController;
+ mScrimController = scrimController;
+ mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
+ mKeyguardViewMediator = keyguardViewMediator;
+ mAssistManager = assistManager;
+ mDozeScrimController = dozeScrimController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mVisualStabilityManager = visualStabilityManager;
+ mPulseExpansionHandler = pulseExpansionHandler;
+ mStatusBarWindowController = statusBarWindowController;
+ mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
+ }
+
+ // TODO: we should try to not pass status bar in here if we can avoid it.
+
+ /**
+ * Initialize instance with objects only available later during execution.
+ */
+ public void initialize(StatusBar statusBar,
+ NotificationIconAreaController notificationIconAreaController,
+ StatusBarWindowViewController statusBarWindowViewController,
+ StatusBarWindowView statusBarWindow,
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ NotificationPanelView notificationPanel, View ambientIndicationContainer) {
+ mStatusBar = statusBar;
+ mNotificationIconAreaController = notificationIconAreaController;
+ mStatusBarWindowViewController = statusBarWindowViewController;
+ mStatusBarWindow = statusBarWindow;
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mNotificationPanel = notificationPanel;
+ mAmbientIndicationContainer = ambientIndicationContainer;
+ mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
+ }
+
+ @Override
+ public String toString() {
+ return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
+ }
+
+ void firePowerSaveChanged(boolean active) {
+ for (Callback callback : mCallbacks) {
+ callback.onPowerSaveChanged(active);
+ }
+ }
+
+ void fireNotificationPulse(NotificationEntry entry) {
+ Runnable pulseSuppressedListener = () -> {
+ entry.setPulseSuppressed(true);
+ mNotificationIconAreaController.updateAodNotificationIcons();
+ };
+ for (Callback callback : mCallbacks) {
+ callback.onNotificationAlerted(pulseSuppressedListener);
+ }
+ }
+
+ boolean getDozingRequested() {
+ return mDozingRequested;
+ }
+
+ boolean isPulsing() {
+ return mPulsing;
+ }
+
+
+ @Override
+ public void addCallback(@NonNull Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ @Override
+ public void removeCallback(@NonNull Callback callback) {
+ mCallbacks.remove(callback);
+ }
+
+ @Override
+ public void startDozing() {
+ if (!mDozingRequested) {
+ mDozingRequested = true;
+ mDozeLog.traceDozing(mDozing);
+ updateDozing();
+ mStatusBar.updateIsKeyguard();
+ }
+ }
+
+ void updateDozing() {
+ // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
+ boolean
+ dozing =
+ mDozingRequested && mStatusBarStateController.getState() == StatusBarState.KEYGUARD
+ || mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ // When in wake-and-unlock we may not have received a change to StatusBarState
+ // but we still should not be dozing, manually set to false.
+ if (mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
+ dozing = false;
+ }
+
+
+ mStatusBarStateController.setIsDozing(dozing);
+ }
+
+ @Override
+ public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
+ if (reason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS) {
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
+ "com.android.systemui:LONG_PRESS");
+ mAssistManager.startAssist(new Bundle());
+ return;
+ }
+
+ if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ mScrimController.setWakeLockScreenSensorActive(true);
+ }
+
+ if (reason == DozeEvent.PULSE_REASON_DOCKING && mStatusBarWindow != null) {
+ mStatusBarWindowViewController.suppressWakeUpGesture(true);
+ }
+
+ boolean passiveAuthInterrupt = reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
+ && mWakeLockScreenPerformsAuth;
+ // Set the state to pulsing, so ScrimController will know what to do once we ask it to
+ // execute the transition. The pulse callback will then be invoked when the scrims
+ // are black, indicating that StatusBar is ready to present the rest of the UI.
+ mPulsing = true;
+ mDozeScrimController.pulse(new PulseCallback() {
+ @Override
+ public void onPulseStarted() {
+ callback.onPulseStarted();
+ mStatusBar.updateNotificationPanelTouchState();
+ setPulsing(true);
+ }
+
+ @Override
+ public void onPulseFinished() {
+ mPulsing = false;
+ callback.onPulseFinished();
+ mStatusBar.updateNotificationPanelTouchState();
+ mScrimController.setWakeLockScreenSensorActive(false);
+ if (mStatusBarWindow != null) {
+ mStatusBarWindowViewController.suppressWakeUpGesture(false);
+ }
+ setPulsing(false);
+ }
+
+ private void setPulsing(boolean pulsing) {
+ mStatusBarStateController.setPulsing(pulsing);
+ mStatusBarKeyguardViewManager.setPulsing(pulsing);
+ mKeyguardViewMediator.setPulsing(pulsing);
+ mNotificationPanel.setPulsing(pulsing);
+ mVisualStabilityManager.setPulsing(pulsing);
+ mStatusBarWindowViewController.setPulsing(pulsing);
+ mIgnoreTouchWhilePulsing = false;
+ if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) {
+ mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */);
+ }
+ mStatusBar.updateScrimController();
+ mPulseExpansionHandler.setPulsing(pulsing);
+ mNotificationWakeUpCoordinator.setPulsing(pulsing);
+ }
+ }, reason);
+ // DozeScrimController is in pulse state, now let's ask ScrimController to start
+ // pulsing and draw the black frame, if necessary.
+ mStatusBar.updateScrimController();
+ }
+
+ @Override
+ public void stopDozing() {
+ if (mDozingRequested) {
+ mDozingRequested = false;
+ mDozeLog.traceDozing(mDozing);
+ updateDozing();
+ }
+ }
+
+ @Override
+ public void onIgnoreTouchWhilePulsing(boolean ignore) {
+ if (ignore != mIgnoreTouchWhilePulsing) {
+ mDozeLog.tracePulseTouchDisabledByProx(ignore);
+ }
+ mIgnoreTouchWhilePulsing = ignore;
+ if (mDozing && ignore) {
+ mStatusBarWindowViewController.cancelCurrentTouch();
+ }
+ }
+
+ @Override
+ public void dozeTimeTick() {
+ mNotificationPanel.dozeTimeTick();
+ if (mAmbientIndicationContainer instanceof DozeReceiver) {
+ ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
+ }
+ }
+
+ @Override
+ public boolean isPowerSaveActive() {
+ return mBatteryController.isAodPowerSave();
+ }
+
+ @Override
+ public boolean isPulsingBlocked() {
+ return mBiometricUnlockController.getMode()
+ == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
+ }
+
+ @Override
+ public boolean isProvisioned() {
+ return mDeviceProvisionedController.isDeviceProvisioned()
+ && mDeviceProvisionedController.isCurrentUserSetup();
+ }
+
+ @Override
+ public boolean isBlockingDoze() {
+ if (mBiometricUnlockController.hasPendingAuthentication()) {
+ Log.i(StatusBar.TAG, "Blocking AOD because fingerprint has authenticated");
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void extendPulse(int reason) {
+ if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
+ mScrimController.setWakeLockScreenSensorActive(true);
+ }
+ if (mDozeScrimController.isPulsing() && mHeadsUpManagerPhone.hasNotifications()) {
+ mHeadsUpManagerPhone.extendHeadsUp();
+ } else {
+ mDozeScrimController.extendPulse();
+ }
+ }
+
+ @Override
+ public void stopPulsing() {
+ if (mDozeScrimController.isPulsing()) {
+ mDozeScrimController.pulseOutNow();
+ }
+ }
+
+ @Override
+ public void setAnimateWakeup(boolean animateWakeup) {
+ if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
+ || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) {
+ // Too late to change the wakeup animation.
+ return;
+ }
+ mAnimateWakeup = animateWakeup;
+ }
+
+ @Override
+ public void setAnimateScreenOff(boolean animateScreenOff) {
+ mAnimateScreenOff = animateScreenOff;
+ }
+
+ @Override
+ public void onSlpiTap(float screenX, float screenY) {
+ if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
+ && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
+ int[] locationOnScreen = new int[2];
+ mAmbientIndicationContainer.getLocationOnScreen(locationOnScreen);
+ float viewX = screenX - locationOnScreen[0];
+ float viewY = screenY - locationOnScreen[1];
+ if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth()
+ && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) {
+
+ // Dispatch a tap
+ long now = SystemClock.elapsedRealtime();
+ MotionEvent ev = MotionEvent.obtain(
+ now, now, MotionEvent.ACTION_DOWN, screenX, screenY, 0);
+ mAmbientIndicationContainer.dispatchTouchEvent(ev);
+ ev.recycle();
+ ev = MotionEvent.obtain(
+ now, now, MotionEvent.ACTION_UP, screenX, screenY, 0);
+ mAmbientIndicationContainer.dispatchTouchEvent(ev);
+ ev.recycle();
+ }
+ }
+ }
+
+ @Override
+ public void setDozeScreenBrightness(int value) {
+ mStatusBarWindowController.setDozeScreenBrightness(value);
+ }
+
+ @Override
+ public void setAodDimmingScrim(float scrimOpacity) {
+ mScrimController.setAodFrontScrimAlpha(scrimOpacity);
+ }
+
+
+
+ @Override
+ public void prepareForGentleSleep(Runnable onDisplayOffCallback) {
+ if (mPendingScreenOffCallback != null) {
+ Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one.");
+ }
+ mPendingScreenOffCallback = onDisplayOffCallback;
+ mStatusBar.updateScrimController();
+ }
+
+ @Override
+ public void cancelGentleSleep() {
+ mPendingScreenOffCallback = null;
+ if (mScrimController.getState() == ScrimState.OFF) {
+ mStatusBar.updateScrimController();
+ }
+ }
+
+ /**
+ * When the dozing host is waiting for scrims to fade out to change the display state.
+ */
+ boolean hasPendingScreenOffCallback() {
+ return mPendingScreenOffCallback != null;
+ }
+
+ /**
+ * Executes an nullifies the pending display state callback.
+ *
+ * @see #hasPendingScreenOffCallback()
+ * @see #prepareForGentleSleep(Runnable)
+ */
+ void executePendingScreenOffCallback() {
+ if (mPendingScreenOffCallback == null) {
+ return;
+ }
+ mPendingScreenOffCallback.run();
+ mPendingScreenOffCallback = null;
+ }
+
+ boolean shouldAnimateWakeup() {
+ return mAnimateWakeup;
+ }
+
+ boolean shouldAnimateScreenOff() {
+ return mAnimateScreenOff;
+ }
+
+ public void setDozing(boolean dozing) {
+ mDozing = dozing;
+ }
+
+ boolean getIgnoreTouchWhilePulsing() {
+ return mIgnoreTouchWhilePulsing;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index dd200da56d20..7f31f3db4507 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -77,9 +77,12 @@ public class HeadsUpTouchHelper implements Gefingerpoken {
ExpandableView child = mCallback.getChildAtRawPosition(x, y);
mTouchingHeadsUpView = false;
if (child instanceof ExpandableNotificationRow) {
- mPickedChild = (ExpandableNotificationRow) child;
+ ExpandableNotificationRow pickedChild = (ExpandableNotificationRow) child;
mTouchingHeadsUpView = !mCallback.isExpanded()
- && mPickedChild.isHeadsUp() && mPickedChild.isPinned();
+ && pickedChild.isHeadsUp() && pickedChild.isPinned();
+ if (mTouchingHeadsUpView) {
+ mPickedChild = pickedChild;
+ }
} else if (child == null && !mCallback.isExpanded()) {
// We might touch above the visible heads up child, but then we still would
// like to capture it.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index d9de59efb08d..bf887044e05b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -21,17 +21,16 @@ import android.hardware.TriggerEvent
import android.hardware.TriggerEventListener
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Dependency
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.util.Assert
import com.android.systemui.util.sensors.AsyncSensorManager
class KeyguardLiftController constructor(
private val statusBarStateController: StatusBarStateController,
- private val asyncSensorManager: AsyncSensorManager
+ private val asyncSensorManager: AsyncSensorManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor
) : StatusBarStateController.StateListener, KeyguardUpdateMonitorCallback() {
- private val keyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor::class.java)
private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)
private var isListening = false
private var bouncerVisible = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 30fe68a28ef2..e00cfb16bd67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -55,7 +55,6 @@ import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -243,7 +242,7 @@ public class NotificationPanelView extends PanelView implements
private View mQsNavbarScrim;
protected NotificationsQuickSettingsContainer mNotificationContainerParent;
protected NotificationStackScrollLayout mNotificationStackScroller;
- protected LinearLayout mHomeControlsLayout;
+ protected FrameLayout mHomeControlsLayout;
private boolean mAnimateNextPositionUpdate;
private int mTrackingPointer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 6064fbedf63d..35039a0d74f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -60,11 +60,13 @@ import java.lang.annotation.RetentionPolicy;
import java.util.function.Consumer;
import javax.inject.Inject;
+import javax.inject.Singleton;
/**
* Controls both the scrim behind the notifications and in front of the notifications (when a
* security method gets shown).
*/
+@Singleton
public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener,
Dumpable {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index e059715986dc..13055ffb2f77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -39,15 +39,20 @@ public enum ScrimState {
@Override
public void prepare(ScrimState previousState) {
mFrontTint = Color.BLACK;
- mBehindTint = previousState.mBehindTint;
+ mBehindTint = Color.BLACK;
mBubbleTint = previousState.mBubbleTint;
mFrontAlpha = 1f;
- mBehindAlpha = previousState.mBehindAlpha;
+ mBehindAlpha = 1f;
mBubbleAlpha = previousState.mBubbleAlpha;
mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
}
+
+ @Override
+ public boolean isLowPowerState() {
+ return true;
+ }
},
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 2963c9469fb2..70dca2f8fc10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -50,7 +50,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -157,10 +156,8 @@ import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.doze.DozeEvent;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
-import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.fragments.ExtensionFragmentListener;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -347,7 +344,7 @@ public class StatusBar extends SystemUI implements DemoMode,
/**
* The {@link StatusBarState} of the status bar.
*/
- protected int mState;
+ protected int mState; // TODO: remove this. Just use StatusBarStateController
protected boolean mBouncerShowing;
private PhoneStatusBarPolicy mIconPolicy;
@@ -356,7 +353,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private VolumeComponent mVolumeComponent;
private BrightnessMirrorController mBrightnessMirrorController;
private boolean mBrightnessMirrorVisible;
- protected BiometricUnlockController mBiometricUnlockController;
+ private BiometricUnlockController mBiometricUnlockController;
private final LightBarController mLightBarController;
private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
protected LockscreenWallpaper mLockscreenWallpaper;
@@ -373,7 +370,7 @@ public class StatusBar extends SystemUI implements DemoMode,
protected StatusBarWindowController mStatusBarWindowController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
- DozeServiceHost mDozeServiceHost = new DozeServiceHost();
+ DozeServiceHost mDozeServiceHost;
private boolean mWakeUpComingFromTouch;
private PointF mWakeUpTouchLocation;
@@ -398,6 +395,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private final StatusBarWindowViewController.Builder mStatusBarWindowViewControllerBuilder;
private final NotifLog mNotifLog;
private final DozeParameters mDozeParameters;
+ private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
// expanded notifications
protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
@@ -492,7 +490,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private final UiOffloadThread mUiOffloadThread;
protected boolean mDozing;
- private boolean mDozingRequested;
private final NotificationMediaManager mMediaManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
@@ -611,7 +608,6 @@ public class StatusBar extends SystemUI implements DemoMode,
private ActivityLaunchAnimator mActivityLaunchAnimator;
protected StatusBarNotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
- private boolean mPulsing;
private final BubbleController mBubbleController;
private final BubbleController.BubbleExpandListener mBubbleExpandListener;
@@ -621,7 +617,6 @@ public class StatusBar extends SystemUI implements DemoMode,
public void onActiveStateChanged(int code, int uid, String packageName, boolean active) {
Dependency.get(MAIN_HANDLER).post(() -> {
mForegroundServiceController.onAppOpChanged(code, uid, packageName, active);
- mNotificationListController.updateNotificationsForAppOp(code, uid, packageName, active);
});
}
@@ -691,7 +686,11 @@ public class StatusBar extends SystemUI implements DemoMode,
NotifLog notifLog,
DozeParameters dozeParameters,
ScrimController scrimController,
- Lazy<LockscreenWallpaper> lockscreenWallpaperLazy) {
+ Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
+ DozeServiceHost dozeServiceHost,
+ PowerManager powerManager,
+ DozeScrimController dozeScrimController) {
super(context);
mFeatureFlags = featureFlags;
mLightBarController = lightBarController;
@@ -748,9 +747,13 @@ public class StatusBar extends SystemUI implements DemoMode,
mStatusBarWindowController = statusBarWindowController;
mStatusBarWindowViewControllerBuilder = statusBarWindowViewControllerBuilder;
mNotifLog = notifLog;
+ mDozeServiceHost = dozeServiceHost;
+ mPowerManager = powerManager;
mDozeParameters = dozeParameters;
mScrimController = scrimController;
mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
+ mDozeScrimController = dozeScrimController;
+ mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
mBubbleExpandListener =
(isExpanding, key) -> {
@@ -802,7 +805,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -902,6 +904,9 @@ public class StatusBar extends SystemUI implements DemoMode,
startKeyguard();
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
+ mDozeServiceHost.initialize(this, mNotificationIconAreaController,
+ mStatusBarWindowViewController, mStatusBarWindow, mStatusBarKeyguardViewManager,
+ mNotificationPanel, mAmbientIndicationContainer);
putComponent(DozeHost.class, mDozeServiceHost);
mScreenPinningRequest = new ScreenPinningRequest(mContext);
@@ -1066,7 +1071,6 @@ public class StatusBar extends SystemUI implements DemoMode,
mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
mHeadsUpManager, mNotificationIconAreaController, mScrimController);
- mDozeScrimController = new DozeScrimController(mDozeParameters, mDozeLog);
BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop);
mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
@@ -1370,11 +1374,7 @@ public class StatusBar extends SystemUI implements DemoMode,
protected void startKeyguard() {
Trace.beginSection("StatusBar#startKeyguard");
- mBiometricUnlockController = new BiometricUnlockController(mContext,
- mDozeScrimController, mKeyguardViewMediator,
- mScrimController, this, mKeyguardStateController, new Handler(),
- mKeyguardUpdateMonitor, mKeyguardBypassController, mDozeParameters);
- putComponent(BiometricUnlockController.class, mBiometricUnlockController);
+ mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
mStatusBarKeyguardViewManager = mKeyguardViewMediator.registerStatusBar(this,
getBouncerContainer(), mNotificationPanel, mBiometricUnlockController,
mStatusBarWindow.findViewById(R.id.lock_icon_container), mStackScroller,
@@ -1731,7 +1731,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (isDozing() && isHeadsUp) {
entry.setPulseSuppressed(false);
mDozeServiceHost.fireNotificationPulse(entry);
- if (mPulsing) {
+ if (mDozeServiceHost.isPulsing()) {
mDozeScrimController.cancelPendingPulseTimeout();
}
}
@@ -1763,7 +1763,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public boolean isPulsing() {
- return mPulsing;
+ return mDozeServiceHost.isPulsing();
}
public boolean hideStatusBarIconsWhenExpanded() {
@@ -2826,7 +2826,7 @@ public class StatusBar extends SystemUI implements DemoMode,
if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
&& mKeyguardStateController.canDismissLockScreen()
&& !mStatusBarStateController.leaveOpenOnKeyguardHide()
- && isPulsing()) {
+ && mDozeServiceHost.isPulsing()) {
// Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a pulse.
// TODO: Factor this transition out of BiometricUnlockController.
mBiometricUnlockController.startWakeAndUnlock(
@@ -3157,7 +3157,7 @@ public class StatusBar extends SystemUI implements DemoMode,
return mState == StatusBarState.FULLSCREEN_USER_SWITCHER;
}
- private boolean updateIsKeyguard() {
+ boolean updateIsKeyguard() {
boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -3165,8 +3165,8 @@ public class StatusBar extends SystemUI implements DemoMode,
// there's no surface we can show to the user. Note that the device goes fully interactive
// late in the transition, so we also allow the device to start dozing once the screen has
// turned off fully.
- boolean keyguardForDozing = mDozingRequested &&
- (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard));
+ boolean keyguardForDozing = mDozeServiceHost.getDozingRequested()
+ && (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard));
boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested()
|| keyguardForDozing) && !wakeAndUnlocking;
if (keyguardForDozing) {
@@ -3582,7 +3582,7 @@ public class StatusBar extends SystemUI implements DemoMode,
public void onStateChanged(int newState) {
mState = newState;
updateReportRejectedTouchVisibility();
- updateDozing();
+ mDozeServiceHost.updateDozing();
updateTheme();
mNavigationBarController.touchAutoDim(mDisplayId);
Trace.beginSection("StatusBar#updateKeyguardState");
@@ -3620,36 +3620,23 @@ public class StatusBar extends SystemUI implements DemoMode,
public void onDozingChanged(boolean isDozing) {
Trace.beginSection("StatusBar#updateDozing");
mDozing = isDozing;
+ mDozeServiceHost.setDozing(mDozing);
// Collapse the notification panel if open
- boolean dozingAnimated = mDozingRequested && mDozeParameters.shouldControlScreenOff();
+ boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
+ && mDozeParameters.shouldControlScreenOff();
mNotificationPanel.resetViews(dozingAnimated);
updateQsExpansionEnabled();
mKeyguardViewMediator.setDozing(mDozing);
mEntryManager.updateNotifications("onDozingChanged");
- updateDozingState();
+ mDozeServiceHost.updateDozing();
updateScrimController();
updateReportRejectedTouchVisibility();
Trace.endSection();
}
- private void updateDozing() {
- // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
- boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
- || mBiometricUnlockController.getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
- // When in wake-and-unlock we may not have received a change to mState
- // but we still should not be dozing, manually set to false.
- if (mBiometricUnlockController.getMode() ==
- BiometricUnlockController.MODE_WAKE_AND_UNLOCK) {
- dozing = false;
- }
-
- mStatusBarStateController.setIsDozing(dozing);
- }
-
private void updateKeyguardState() {
mKeyguardStateController.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
mStatusBarKeyguardViewManager.isOccluded());
@@ -3874,10 +3861,11 @@ public class StatusBar extends SystemUI implements DemoMode,
* collapse the panel after we expanded it, and thus we would end up with a blank
* Keyguard.
*/
- private void updateNotificationPanelTouchState() {
+ void updateNotificationPanelTouchState() {
boolean goingToSleepWithoutAnimation = isGoingToSleep()
&& !mDozeParameters.shouldControlScreenOff();
- boolean disabled = (!mDeviceInteractive && !mPulsing) || goingToSleepWithoutAnimation;
+ boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
+ || goingToSleepWithoutAnimation;
mNotificationPanel.setTouchAndAnimationDisabled(disabled);
mNotificationIconAreaController.setAnimationsEnabled(!disabled);
}
@@ -4027,7 +4015,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
public void notifyBiometricAuthModeChanged() {
- updateDozing();
+ mDozeServiceHost.updateDozing();
updateScrimController();
mStatusBarWindowViewController.onBiometricAuthModeChanged(
mBiometricUnlockController.isWakeAndUnlock(),
@@ -4063,7 +4051,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
} else if (mBrightnessMirrorVisible) {
mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
- } else if (isPulsing()) {
+ } else if (mDozeServiceHost.isPulsing()) {
mScrimController.transitionTo(ScrimState.PULSING,
mDozeScrimController.getScrimCallback());
} else if (mDozeServiceHost.hasPendingScreenOffCallback()) {
@@ -4093,287 +4081,8 @@ public class StatusBar extends SystemUI implements DemoMode,
return mStatusBarKeyguardViewManager.isShowing();
}
- @VisibleForTesting
- final class DozeServiceHost implements DozeHost {
- private final ArrayList<Callback> mCallbacks = new ArrayList<>();
- private boolean mAnimateWakeup;
- private boolean mAnimateScreenOff;
- private boolean mIgnoreTouchWhilePulsing;
- private Runnable mPendingScreenOffCallback;
- @VisibleForTesting
- boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
- "persist.sysui.wake_performs_auth", true);
-
- @Override
- public String toString() {
- return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
- }
-
- public void firePowerSaveChanged(boolean active) {
- for (Callback callback : mCallbacks) {
- callback.onPowerSaveChanged(active);
- }
- }
-
- public void fireNotificationPulse(NotificationEntry entry) {
- Runnable pulseSupressedListener = () -> {
- entry.setPulseSuppressed(true);
- mNotificationIconAreaController.updateAodNotificationIcons();
- };
- for (Callback callback : mCallbacks) {
- callback.onNotificationAlerted(pulseSupressedListener);
- }
- }
-
- @Override
- public void addCallback(@NonNull Callback callback) {
- mCallbacks.add(callback);
- }
-
- @Override
- public void removeCallback(@NonNull Callback callback) {
- mCallbacks.remove(callback);
- }
-
- @Override
- public void startDozing() {
- if (!mDozingRequested) {
- mDozingRequested = true;
- mDozeLog.traceDozing(mDozing);
- updateDozing();
- updateIsKeyguard();
- }
- }
-
- @Override
- public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
- if (reason == DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS) {
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
- "com.android.systemui:LONG_PRESS");
- startAssist(new Bundle());
- return;
- }
-
- if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
- mScrimController.setWakeLockScreenSensorActive(true);
- }
-
- if (reason == DozeEvent.PULSE_REASON_DOCKING && mStatusBarWindow != null) {
- mStatusBarWindowViewController.suppressWakeUpGesture(true);
- }
-
- boolean passiveAuthInterrupt = reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN
- && mWakeLockScreenPerformsAuth;
- // Set the state to pulsing, so ScrimController will know what to do once we ask it to
- // execute the transition. The pulse callback will then be invoked when the scrims
- // are black, indicating that StatusBar is ready to present the rest of the UI.
- mPulsing = true;
- mDozeScrimController.pulse(new PulseCallback() {
- @Override
- public void onPulseStarted() {
- callback.onPulseStarted();
- updateNotificationPanelTouchState();
- setPulsing(true);
- }
-
- @Override
- public void onPulseFinished() {
- mPulsing = false;
- callback.onPulseFinished();
- updateNotificationPanelTouchState();
- mScrimController.setWakeLockScreenSensorActive(false);
- if (mStatusBarWindow != null) {
- mStatusBarWindowViewController.suppressWakeUpGesture(false);
- }
- setPulsing(false);
- }
-
- private void setPulsing(boolean pulsing) {
- mStatusBarStateController.setPulsing(pulsing);
- mStatusBarKeyguardViewManager.setPulsing(pulsing);
- mKeyguardViewMediator.setPulsing(pulsing);
- mNotificationPanel.setPulsing(pulsing);
- mVisualStabilityManager.setPulsing(pulsing);
- mStatusBarWindowViewController.setPulsing(pulsing);
- mIgnoreTouchWhilePulsing = false;
- if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) {
- mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */);
- }
- updateScrimController();
- mPulseExpansionHandler.setPulsing(pulsing);
- mWakeUpCoordinator.setPulsing(pulsing);
- }
- }, reason);
- // DozeScrimController is in pulse state, now let's ask ScrimController to start
- // pulsing and draw the black frame, if necessary.
- updateScrimController();
- }
-
- @Override
- public void stopDozing() {
- if (mDozingRequested) {
- mDozingRequested = false;
- mDozeLog.traceDozing(mDozing);
- updateDozing();
- }
- }
-
- @Override
- public void onIgnoreTouchWhilePulsing(boolean ignore) {
- if (ignore != mIgnoreTouchWhilePulsing) {
- mDozeLog.tracePulseTouchDisabledByProx(ignore);
- }
- mIgnoreTouchWhilePulsing = ignore;
- if (isDozing() && ignore) {
- mStatusBarWindowViewController.cancelCurrentTouch();
- }
- }
-
- @Override
- public void dozeTimeTick() {
- mNotificationPanel.dozeTimeTick();
- if (mAmbientIndicationContainer instanceof DozeReceiver) {
- ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
- }
- }
-
- @Override
- public boolean isPowerSaveActive() {
- return mBatteryController.isAodPowerSave();
- }
-
- @Override
- public boolean isPulsingBlocked() {
- return mBiometricUnlockController.getMode()
- == BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
- }
-
- @Override
- public boolean isProvisioned() {
- return mDeviceProvisionedController.isDeviceProvisioned()
- && mDeviceProvisionedController.isCurrentUserSetup();
- }
-
- @Override
- public boolean isBlockingDoze() {
- if (mBiometricUnlockController.hasPendingAuthentication()) {
- Log.i(TAG, "Blocking AOD because fingerprint has authenticated");
- return true;
- }
- return false;
- }
-
- @Override
- public void extendPulse(int reason) {
- if (reason == DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN) {
- mScrimController.setWakeLockScreenSensorActive(true);
- }
- if (mDozeScrimController.isPulsing() && mHeadsUpManager.hasNotifications()) {
- mHeadsUpManager.extendHeadsUp();
- } else {
- mDozeScrimController.extendPulse();
- }
- }
-
- @Override
- public void stopPulsing() {
- if (mDozeScrimController.isPulsing()) {
- mDozeScrimController.pulseOutNow();
- }
- }
-
- @Override
- public void setAnimateWakeup(boolean animateWakeup) {
- if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
- || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) {
- // Too late to change the wakeup animation.
- return;
- }
- mAnimateWakeup = animateWakeup;
- }
-
- @Override
- public void setAnimateScreenOff(boolean animateScreenOff) {
- mAnimateScreenOff = animateScreenOff;
- }
-
- @Override
- public void onSlpiTap(float screenX, float screenY) {
- if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
- && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
- mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2);
- float viewX = screenX - mTmpInt2[0];
- float viewY = screenY - mTmpInt2[1];
- if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth()
- && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) {
- dispatchTap(mAmbientIndicationContainer, viewX, viewY);
- }
- }
- }
-
- @Override
- public void setDozeScreenBrightness(int value) {
- mStatusBarWindowController.setDozeScreenBrightness(value);
- }
-
- @Override
- public void setAodDimmingScrim(float scrimOpacity) {
- mScrimController.setAodFrontScrimAlpha(scrimOpacity);
- }
-
- @Override
- public void prepareForGentleSleep(Runnable onDisplayOffCallback) {
- if (onDisplayOffCallback != null) {
- Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one.");
- }
- mPendingScreenOffCallback = onDisplayOffCallback;
- updateScrimController();
- }
-
- /**
- * When the dozing host is waiting for scrims to fade out to change the display state.
- */
- boolean hasPendingScreenOffCallback() {
- return mPendingScreenOffCallback != null;
- }
-
- /**
- * Executes an nullifies the pending display state callback.
- *
- * @see #hasPendingScreenOffCallback()
- * @see #prepareForGentleSleep(Runnable)
- */
- void executePendingScreenOffCallback() {
- if (mPendingScreenOffCallback == null) {
- return;
- }
- mPendingScreenOffCallback.run();
- mPendingScreenOffCallback = null;
- }
-
- private void dispatchTap(View view, float x, float y) {
- long now = SystemClock.elapsedRealtime();
- dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN);
- dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP);
- }
-
- private void dispatchTouchEvent(View view, float x, float y, long now, int action) {
- MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */);
- view.dispatchTouchEvent(ev);
- ev.recycle();
- }
-
- private boolean shouldAnimateWakeup() {
- return mAnimateWakeup;
- }
-
- public boolean shouldAnimateScreenOff() {
- return mAnimateScreenOff;
- }
- }
-
public boolean shouldIgnoreTouch() {
- return isDozing() && mDozeServiceHost.mIgnoreTouchWhilePulsing;
+ return isDozing() && mDozeServiceHost.getIgnoreTouchWhilePulsing();
}
// Begin Extra BaseStatusBar methods.
@@ -4400,7 +4109,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private boolean mVisibleToUser;
protected DevicePolicyManager mDevicePolicyManager;
- protected PowerManager mPowerManager;
+ private final PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
protected KeyguardManager mKeyguardManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
index cade5dc46388..3ce62396fbb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
@@ -57,7 +57,7 @@ public interface ExtensionController {
ExtensionBuilder<T> withCallback(Consumer<T> callback);
ExtensionBuilder<T> withUiMode(int mode, Supplier<T> def);
ExtensionBuilder<T> withFeature(String feature, Supplier<T> def);
- Extension build();
+ Extension<T> build();
}
public interface PluginConverter<T, P> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index fd030d133be0..eeef726ace75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -135,7 +135,7 @@ public class ExtensionControllerImpl implements ExtensionController {
}
@Override
- public ExtensionController.Extension build() {
+ public ExtensionController.Extension<T> build() {
// Sort items in ascending order
Collections.sort(mExtension.mProducers, Comparator.comparingInt(Item::sortOrder));
mExtension.notifyChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 8f201c793258..a3a9322395c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.policy;
import android.app.ActivityManager;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.UserManager;
@@ -29,11 +30,13 @@ import com.android.systemui.dagger.qualifiers.MainHandler;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
+ * Controller used to retrieve information related to a hotspot.
*/
@Singleton
public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback {
@@ -48,11 +51,12 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
private final Context mContext;
private int mHotspotState;
- private int mNumConnectedDevices;
+ private volatile int mNumConnectedDevices;
private boolean mWaitingForTerminalState;
private boolean mListening;
/**
+ * Controller used to retrieve information related to a hotspot.
*/
@Inject
public HotspotControllerImpl(Context context, @MainHandler Handler mainHandler) {
@@ -96,7 +100,6 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
/**
* Adds {@code callback} to the controller. The controller will update the callback on state
* changes. It will immediately trigger the callback added to notify current state.
- * @param callback
*/
@Override
public void addCallback(Callback callback) {
@@ -110,8 +113,8 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
mWifiManager.registerSoftApCallback(this, mMainHandler);
} else {
// mWifiManager#registerSoftApCallback triggers a call to
- // onNumClientsChanged on the Main Handler. In order to always update the
- // callback on added, we make this call when adding callbacks after the
+ // onConnectedClientsChanged on the Main Handler. In order to always update
+ // the callback on added, we make this call when adding callbacks after the
// first.
mMainHandler.post(() ->
callback.onHotspotChanged(isHotspotEnabled(),
@@ -232,8 +235,8 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof
}
@Override
- public void onNumClientsChanged(int numConnectedDevices) {
- mNumConnectedDevices = numConnectedDevices;
+ public void onConnectedClientsChanged(List<WifiClient> clients) {
+ mNumConnectedDevices = clients.size();
fireHotspotChangedCallback();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 7b4a7d20293c..502a9bd34b12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -679,8 +679,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
}
};
- InputConnection ic = InputConnectionCompat.createWrapper(
- inputConnection, outAttrs, callback);
+ InputConnection ic = inputConnection == null ? null :
+ InputConnectionCompat.createWrapper(inputConnection, outAttrs, callback);
Context userContext = null;
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
index d6d0a3603c25..9b685f0ad0f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/AudioRecordingDisclosureBar.java
@@ -30,6 +30,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.ImageView;
@@ -45,7 +46,10 @@ class AudioRecordingDisclosureBar {
private static final String TAG = "AudioRecordingDisclosureBar";
private static final boolean DEBUG = false;
- private static final String LAYOUT_PARAMS_TITLE = "AudioRecordingDisclosureBar";
+ // This title is used to test the microphone disclosure indicator in
+ // CtsSystemUiHostTestCases:TvMicrophoneCaptureIndicatorTest
+ private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator";
+
private static final int ANIM_DURATION_MS = 150;
private final Context mContext;
@@ -70,6 +74,7 @@ class AudioRecordingDisclosureBar {
}
private void createView() {
+ //TODO(b/142228704): this is to be re-implemented once proper design is completed
mView = View.inflate(mContext,
R.layout.tv_status_bar_audio_recording, null);
mAppsInfoContainer = mView.findViewById(R.id.container);
@@ -88,7 +93,7 @@ class AudioRecordingDisclosureBar {
Context.WINDOW_SERVICE);
windowManager.addView(mView, layoutParams);
- // Set invisible first util it gains its actual size and we are able to hide it by moving
+ // Set invisible first until it gains its actual size and we are able to hide it by moving
// off the screen
mView.setVisibility(View.INVISIBLE);
mView.getViewTreeObserver().addOnGlobalLayoutListener(
@@ -98,16 +103,18 @@ class AudioRecordingDisclosureBar {
// Now that we get the height, we can move the bar off ("below") the screen
final int height = mView.getHeight();
mView.setTranslationY(height);
- // ... and make it visible
- mView.setVisibility(View.VISIBLE);
// Remove the observer
mView.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
+ // Now, that the view has been measured, and the translation was set to
+ // move it off the screen, we change the visibility to GONE
+ mView.setVisibility(View.GONE);
}
});
}
private void showAudioRecordingDisclosureBar() {
+ mView.setVisibility(View.VISIBLE);
mView.animate()
.translationY(0f)
.setDuration(ANIM_DURATION_MS)
@@ -138,9 +145,10 @@ class AudioRecordingDisclosureBar {
}
private void hideAudioRecordingDisclosureBar() {
- mView.animate()
- .translationY(mView.getHeight())
+ final ViewPropertyAnimator animator = mView.animate();
+ animator.translationY(mView.getHeight())
.setDuration(ANIM_DURATION_MS)
+ .withEndAction(() -> mView.setVisibility(View.GONE))
.start();
}
@@ -156,7 +164,7 @@ class AudioRecordingDisclosureBar {
public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
if (DEBUG) {
Log.d(TAG,
- "OP_RECORD_AUDIO active change, active" + active + ", app=" + packageName);
+ "OP_RECORD_AUDIO active change, active=" + active + ", app=" + packageName);
}
if (mExemptApps.contains(packageName)) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 5c4ef18b61a6..768bd138fd64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -21,11 +21,15 @@ import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.os.Bundle;
@@ -42,6 +46,8 @@ import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import junit.framework.Assert;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,27 +56,104 @@ import org.mockito.ArgumentCaptor;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ForegroundServiceControllerTest extends SysuiTestCase {
- @UserIdInt private static final int USERID_ONE = 10; // UserManagerService.MIN_USER_ID;
- @UserIdInt private static final int USERID_TWO = USERID_ONE + 1;
-
private ForegroundServiceController mFsc;
private ForegroundServiceNotificationListener mListener;
private NotificationEntryListener mEntryListener;
+ private NotificationEntryManager mEntryManager;
@Before
public void setUp() throws Exception {
- mFsc = new ForegroundServiceController();
- NotificationEntryManager notificationEntryManager = mock(NotificationEntryManager.class);
+ mEntryManager = mock(NotificationEntryManager.class);
+ mFsc = new ForegroundServiceController(mEntryManager);
mListener = new ForegroundServiceNotificationListener(
- mContext, mFsc, notificationEntryManager);
+ mContext, mFsc, mEntryManager);
ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
ArgumentCaptor.forClass(NotificationEntryListener.class);
- verify(notificationEntryManager).addNotificationEntryListener(
+ verify(mEntryManager).addNotificationEntryListener(
entryListenerCaptor.capture());
mEntryListener = entryListenerCaptor.getValue();
}
@Test
+ public void testAppOps_appOpChangedBeforeNotificationExists() {
+ // GIVEN app op exists, but notification doesn't exist in NEM yet
+ NotificationEntry entry = createFgEntry();
+ mFsc.onAppOpChanged(
+ AppOpsManager.OP_CAMERA,
+ entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(),
+ true);
+ assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+ // WHEN the notification is added
+ mEntryListener.onPendingEntryAdded(entry);
+
+ // THEN the app op is added to the entry
+ Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+ }
+
+ @Test
+ public void testAppOps_appOpAddedToForegroundNotif() {
+ // GIVEN a notification associated with a foreground service
+ NotificationEntry entry = addFgEntry();
+ when(mEntryManager.getPendingOrCurrentNotif(entry.getKey())).thenReturn(entry);
+
+ // WHEN we are notified of a new app op for this notification
+ mFsc.onAppOpChanged(
+ AppOpsManager.OP_CAMERA,
+ entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(),
+ true);
+
+ // THEN the app op is added to the entry
+ Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+ // THEN notification views are updated since the notification is visible
+ verify(mEntryManager, times(1)).updateNotifications(anyString());
+ }
+
+ @Test
+ public void testAppOpsAlreadyAdded() {
+ // GIVEN a foreground service associated notification that already has the correct app op
+ NotificationEntry entry = addFgEntry();
+ entry.mActiveAppOps.add(AppOpsManager.OP_CAMERA);
+ when(mEntryManager.getPendingOrCurrentNotif(entry.getKey())).thenReturn(entry);
+
+ // WHEN we are notified of the same app op for this notification
+ mFsc.onAppOpChanged(
+ AppOpsManager.OP_CAMERA,
+ entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(),
+ true);
+
+ // THEN the app op still exists in the notification entry
+ Assert.assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+ // THEN notification views aren't updated since nothing changed
+ verify(mEntryManager, never()).updateNotifications(anyString());
+ }
+
+ @Test
+ public void testAppOps_appOpNotAddedToUnrelatedNotif() {
+ // GIVEN no notification entries correspond to the newly updated appOp
+ NotificationEntry entry = addFgEntry();
+ when(mEntryManager.getPendingOrCurrentNotif(entry.getKey())).thenReturn(null);
+
+ // WHEN a new app op is detected
+ mFsc.onAppOpChanged(
+ AppOpsManager.OP_CAMERA,
+ entry.getSbn().getUid(),
+ entry.getSbn().getPackageName(),
+ true);
+
+ // THEN we won't see appOps on the entry
+ Assert.assertFalse(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
+
+ // THEN notification views aren't updated since nothing changed
+ verify(mEntryManager, never()).updateNotifications(anyString());
+ }
+
+ @Test
public void testAppOpsCRUD() {
// no crash on remove that doesn't exist
mFsc.onAppOpChanged(9, 1000, "pkg1", false);
@@ -339,12 +422,12 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
assertTrue(mFsc.isSystemAlertWarningNeeded(USERID_ONE, PKG1));
}
- private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag,
+ private StatusBarNotification makeMockSBN(int userId, String pkg, int id, String tag,
int flags) {
final Notification n = mock(Notification.class);
n.extras = new Bundle();
n.flags = flags;
- return makeMockSBN(userid, pkg, id, tag, n);
+ return makeMockSBN(userId, pkg, id, tag, n);
}
private StatusBarNotification makeMockSBN(int userid, String pkg, int id, String tag,
@@ -360,10 +443,10 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
return sbn;
}
- private StatusBarNotification makeMockFgSBN(int userid, String pkg, int id,
+ private StatusBarNotification makeMockFgSBN(int uid, String pkg, int id,
boolean usesStdLayout) {
StatusBarNotification sbn =
- makeMockSBN(userid, pkg, id, "foo", Notification.FLAG_FOREGROUND_SERVICE);
+ makeMockSBN(uid, pkg, id, "foo", Notification.FLAG_FOREGROUND_SERVICE);
if (usesStdLayout) {
sbn.getNotification().contentView = null;
sbn.getNotification().headsUpContentView = null;
@@ -374,8 +457,8 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
return sbn;
}
- private StatusBarNotification makeMockFgSBN(int userid, String pkg) {
- return makeMockSBN(userid, pkg, 1000, "foo", Notification.FLAG_FOREGROUND_SERVICE);
+ private StatusBarNotification makeMockFgSBN(int uid, String pkg) {
+ return makeMockSBN(uid, pkg, 1000, "foo", Notification.FLAG_FOREGROUND_SERVICE);
}
private StatusBarNotification makeMockDisclosure(int userid, String[] pkgs) {
@@ -392,6 +475,19 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
return sbn;
}
+ private NotificationEntry addFgEntry() {
+ NotificationEntry entry = createFgEntry();
+ mEntryListener.onPendingEntryAdded(entry);
+ return entry;
+ }
+
+ private NotificationEntry createFgEntry() {
+ return new NotificationEntryBuilder()
+ .setSbn(makeMockFgSBN(0, TEST_PACKAGE_NAME, 1000, true))
+ .setImportance(NotificationManager.IMPORTANCE_DEFAULT)
+ .build();
+ }
+
private void entryRemoved(StatusBarNotification notification) {
mEntryListener.onEntryRemoved(
new NotificationEntryBuilder()
@@ -414,6 +510,10 @@ public class ForegroundServiceControllerTest extends SysuiTestCase {
.setSbn(notification)
.setImportance(importance)
.build();
- mEntryListener.onPostEntryUpdated(entry);
+ mEntryListener.onPreEntryUpdated(entry);
}
+
+ @UserIdInt private static final int USERID_ONE = 10; // UserManagerService.MIN_USER_ID;
+ @UserIdInt private static final int USERID_TWO = USERID_ONE + 1;
+ private static final String TEST_PACKAGE_NAME = "test";
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index c338d7031fea..7359fdce462d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -61,9 +61,13 @@ import com.android.systemui.tuner.TunerService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.Collections;
+import dagger.Lazy;
+
@RunWithLooper
@RunWith(AndroidTestingRunner.class)
@SmallTest
@@ -71,16 +75,19 @@ public class ScreenDecorationsTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
private ScreenDecorations mScreenDecorations;
- private StatusBar mStatusBar;
+ @Mock private StatusBar mStatusBar;
private WindowManager mWindowManager;
private FragmentService mFragmentService;
private FragmentHostManager mFragmentHostManager;
private TunerService mTunerService;
private StatusBarWindowView mView;
private TunablePaddingService mTunablePaddingService;
+ @Mock private Lazy<StatusBar> mStatusBarLazy;
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
+
mTestableLooper = TestableLooper.get(this);
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
new Handler(mTestableLooper.getLooper()));
@@ -88,11 +95,10 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mTunerService = mDependency.injectMockDependency(TunerService.class);
mFragmentService = mDependency.injectMockDependency(FragmentService.class);
- mStatusBar = mock(StatusBar.class);
mWindowManager = mock(WindowManager.class);
mView = spy(new StatusBarWindowView(mContext, null));
+ when(mStatusBarLazy.get()).thenReturn(mStatusBar);
when(mStatusBar.getStatusBarWindow()).thenReturn(mView);
- mContext.putComponent(StatusBar.class, mStatusBar);
Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
when(mWindowManager.getDefaultDisplay()).thenReturn(display);
@@ -102,7 +108,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
when(mFragmentService.getFragmentHostManager(any())).thenReturn(mFragmentHostManager);
- mScreenDecorations = new ScreenDecorations(mContext) {
+ mScreenDecorations = new ScreenDecorations(mContext, mStatusBarLazy) {
@Override
public void start() {
super.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index b089b740fc47..85d818ac8be3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -389,6 +389,20 @@ public class AuthControllerTest extends SysuiTestCase {
verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL));
}
+ @Test
+ public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ mAuthController.onTryAgainPressed();
+ }
+
+ @Test
+ public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() {
+ showDialog(Authenticator.TYPE_BIOMETRIC, BiometricPrompt.TYPE_FACE);
+ mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ mAuthController.onDeviceCredentialPressed();
+ }
+
// Helpers
private void showDialog(int authenticators, int biometricModality) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 8f4de3f114a2..47b35fdc6dc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -22,7 +22,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -60,6 +59,8 @@ import org.mockito.MockitoAnnotations;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
+import dagger.Lazy;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -86,6 +87,8 @@ public class PowerUITest extends SysuiTestCase {
private IThermalEventListener mUsbThermalEventListener;
private IThermalEventListener mSkinThermalEventListener;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock private Lazy<StatusBar> mStatusBarLazy;
+ @Mock private StatusBar mStatusBar;
@Before
public void setup() {
@@ -93,7 +96,8 @@ public class PowerUITest extends SysuiTestCase {
mMockWarnings = mDependency.injectMockDependency(WarningsUI.class);
mEnhancedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class);
- mContext.putComponent(StatusBar.class, mock(StatusBar.class));
+ when(mStatusBarLazy.get()).thenReturn(mStatusBar);
+
mContext.addMockSystemService(Context.POWER_SERVICE, mPowerManager);
createPowerUi();
@@ -682,7 +686,7 @@ public class PowerUITest extends SysuiTestCase {
}
private void createPowerUi() {
- mPowerUI = new PowerUI(mContext, mBroadcastDispatcher);
+ mPowerUI = new PowerUI(mContext, mBroadcastDispatcher, mStatusBarLazy);
mPowerUI.mComponents = mContext.getComponents();
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index ebdf851899b5..b2a5109749e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -82,6 +82,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -139,6 +140,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
@Mock private RowInflaterTask mAsyncInflationTask;
@Mock private NotificationRowBinder mMockedRowBinder;
+ private int mId;
private NotificationEntry mEntry;
private StatusBarNotification mSbn;
private TestableNotificationEntryManager mEntryManager;
@@ -148,8 +150,12 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
private final CountDownLatch mCountDownLatch;
TestableNotificationEntryManager() {
- super(new NotificationData(mock(NotificationSectionsFeatureManager.class),
- mock(NotifLog.class)), mock(NotifLog.class));
+ super(
+ new NotificationData(
+ mock(NotificationSectionsFeatureManager.class),
+ mock(NotifLog.class),
+ mock(PeopleNotificationIdentifier.class)),
+ mock(NotifLog.class));
mCountDownLatch = new CountDownLatch(1);
}
@@ -238,18 +244,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
when(mListContainer.getViewParentForNotification(any())).thenReturn(
new FrameLayout(mContext));
- Notification.Builder n = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
-
- mEntry = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_NAME)
- .setOpPkg(TEST_PACKAGE_NAME)
- .setUid(TEST_UID)
- .setNotification(n.build())
- .setUser(new UserHandle(ActivityManager.getCurrentUser()))
- .build();
+ mEntry = createNotification();
mSbn = mEntry.getSbn();
mEntry.expandedIcon = mock(StatusBarIconView.class);
@@ -602,6 +597,22 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
any(NotificationVisibility.class), anyBoolean());
}
+ private NotificationEntry createNotification() {
+ Notification.Builder n = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+
+ return new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setId(mId++)
+ .setNotification(n.build())
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+ }
+
private Notification.Action createAction() {
return new Notification.Action.Builder(
Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
index 9202c51d3c97..c2d2e3158f54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationListControllerTest.java
@@ -16,24 +16,16 @@
package com.android.systemui.statusbar.notification;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
-import android.app.AppOpsManager;
import android.app.Notification;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -46,6 +38,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -78,8 +71,10 @@ public class NotificationListControllerTest extends SysuiTestCase {
// TODO: Remove this once EntryManager no longer needs to be mocked
private NotificationData mNotificationData =
- new NotificationData(new NotificationSectionsFeatureManager(
- new DeviceConfigProxyFake(), mContext), mock(NotifLog.class));
+ new NotificationData(
+ new NotificationSectionsFeatureManager(new DeviceConfigProxyFake(), mContext),
+ mock(NotifLog.class),
+ mock(PeopleNotificationIdentifier.class));
private int mNextNotifId = 0;
@@ -120,107 +115,6 @@ public class NotificationListControllerTest extends SysuiTestCase {
verify(mEntryManager).updateNotifications(anyString());
}
- @Test
- public void testAppOps_appOpAddedToForegroundNotif() {
- // GIVEN a notification associated with a foreground service
- final NotificationEntry entry = buildEntry();
- mNotificationData.add(entry);
- when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
- .thenReturn(entry.getKey());
-
- // WHEN we are notified of a new app op
- mController.updateNotificationsForAppOp(
- AppOpsManager.OP_CAMERA,
- entry.getSbn().getUid(),
- entry.getSbn().getPackageName(),
- true);
-
- // THEN the app op is added to the entry
- assertTrue(entry.mActiveAppOps.contains(AppOpsManager.OP_CAMERA));
- // THEN updateNotifications(TEST) is called
- verify(mEntryManager, times(1)).updateNotifications(anyString());
- }
-
- @Test
- public void testAppOps_appOpAddedToUnrelatedNotif() {
- // GIVEN No current foreground notifs
- when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
- .thenReturn(null);
-
- // WHEN An unrelated notification gets a new app op
- mController.updateNotificationsForAppOp(AppOpsManager.OP_CAMERA, 1000, "pkg", true);
-
- // THEN We never call updateNotifications(TEST)
- verify(mEntryManager, never()).updateNotifications(anyString());
- }
-
- @Test
- public void testAppOps_addNotificationWithExistingAppOps() {
- // GIVEN a notification with three associated app ops that is associated with a foreground
- // service
- final NotificationEntry entry = buildEntry();
- mNotificationData.add(entry);
- ArraySet<Integer> expected = new ArraySet<>();
- expected.add(3);
- expected.add(235);
- expected.add(1);
- when(mForegroundServiceController.getStandardLayoutKey(
- entry.getSbn().getUserId(),
- entry.getSbn().getPackageName())).thenReturn(entry.getKey());
- when(mForegroundServiceController.getAppOps(entry.getSbn().getUserId(),
- entry.getSbn().getPackageName())).thenReturn(expected);
-
- // WHEN the notification is added
- mEntryListener.onBeforeNotificationAdded(entry);
-
- // THEN the entry is tagged with all three app ops
- assertEquals(expected.size(), entry.mActiveAppOps.size());
- for (int op : expected) {
- assertTrue("Entry missing op " + op, entry.mActiveAppOps.contains(op));
- }
- }
-
- @Test
- public void testAdd_addNotificationWithNoExistingAppOps() {
- // GIVEN a notification with NO associated app ops
- final NotificationEntry entry = buildEntry();
-
- mNotificationData.add(entry);
- when(mForegroundServiceController.getStandardLayoutKey(
- entry.getSbn().getUserId(),
- entry.getSbn().getPackageName())).thenReturn(entry.getKey());
- when(mForegroundServiceController.getAppOps(entry.getSbn().getUserId(),
- entry.getSbn().getPackageName())).thenReturn(null);
-
- // WHEN the notification is added
- mEntryListener.onBeforeNotificationAdded(entry);
-
- // THEN the entry doesn't have any app ops associated with it
- assertEquals(0, entry.mActiveAppOps.size());
- }
-
- @Test
- public void testAdd_addNonForegroundNotificationWithExistingAppOps() {
- // GIVEN a notification with app ops that isn't associated with a foreground service
- final NotificationEntry entry = buildEntry();
- mNotificationData.add(entry);
- ArraySet<Integer> ops = new ArraySet<>();
- ops.add(3);
- ops.add(235);
- ops.add(1);
- when(mForegroundServiceController.getAppOps(entry.getSbn().getUserId(),
- entry.getSbn().getPackageName())).thenReturn(ops);
- when(mForegroundServiceController.getStandardLayoutKey(
- entry.getSbn().getUserId(),
- entry.getSbn().getPackageName())).thenReturn("something else");
-
- // WHEN the notification is added
- mEntryListener.onBeforeNotificationAdded(entry);
-
- // THEN the entry doesn't have any app ops associated with it
- assertEquals(0, entry.mActiveAppOps.size());
- }
-
private NotificationEntry buildEntry() {
mNextNotifId++;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 640984b1129f..1a469d881d2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.collection;
-import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
-import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.Notification.CATEGORY_ALARM;
import static android.app.Notification.CATEGORY_CALL;
import static android.app.Notification.CATEGORY_EVENT;
@@ -56,7 +54,6 @@ import android.content.pm.PackageManager;
import android.graphics.drawable.Icon;
import android.media.session.MediaSession;
import android.os.Bundle;
-import android.os.Process;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
@@ -64,7 +61,6 @@ import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -81,6 +77,7 @@ import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -159,69 +156,6 @@ public class NotificationDataTest extends SysuiTestCase {
}
@Test
- public void testAllRelevantNotisTaggedWithAppOps() throws Exception {
- mNotificationData.add(mRow.getEntry());
- ExpandableNotificationRow row2 = new NotificationTestHelper(getContext(), mDependency)
- .createRow();
- mNotificationData.add(row2.getEntry());
- ExpandableNotificationRow diffPkg =
- new NotificationTestHelper(getContext(), mDependency).createRow("pkg", 4000,
- Process.myUserHandle());
- mNotificationData.add(diffPkg.getEntry());
-
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_CAMERA);
- expectedOps.add(OP_ACCEPT_HANDOVER);
-
- for (int op : expectedOps) {
- mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
- NotificationTestHelper.PKG, mRow.getEntry().getKey(), true);
- mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
- NotificationTestHelper.PKG, row2.getEntry().getKey(), true);
- }
- for (int op : expectedOps) {
- assertTrue(mRow.getEntry().getKey() + " doesn't have op " + op,
- mNotificationData.get(mRow.getEntry().getKey()).mActiveAppOps.contains(op));
- assertTrue(row2.getEntry().getKey() + " doesn't have op " + op,
- mNotificationData.get(row2.getEntry().getKey()).mActiveAppOps.contains(op));
- assertFalse(diffPkg.getEntry().getKey() + " has op " + op,
- mNotificationData.get(diffPkg.getEntry().getKey()).mActiveAppOps.contains(op));
- }
- }
-
- @Test
- public void testAppOpsRemoval() throws Exception {
- mNotificationData.add(mRow.getEntry());
- ExpandableNotificationRow row2 = new NotificationTestHelper(getContext(), mDependency)
- .createRow();
- mNotificationData.add(row2.getEntry());
-
- ArraySet<Integer> expectedOps = new ArraySet<>();
- expectedOps.add(OP_CAMERA);
- expectedOps.add(OP_ACCEPT_HANDOVER);
-
- for (int op : expectedOps) {
- mNotificationData.updateAppOp(op, NotificationTestHelper.UID,
- NotificationTestHelper.PKG, row2.getEntry().getKey(), true);
- }
-
- expectedOps.remove(OP_ACCEPT_HANDOVER);
- mNotificationData.updateAppOp(OP_ACCEPT_HANDOVER, NotificationTestHelper.UID,
- NotificationTestHelper.PKG, row2.getEntry().getKey(), false);
-
- assertTrue(mRow.getEntry().getKey() + " doesn't have op " + OP_CAMERA,
- mNotificationData.get(mRow.getEntry().getKey()).mActiveAppOps.contains(OP_CAMERA));
- assertTrue(row2.getEntry().getKey() + " doesn't have op " + OP_CAMERA,
- mNotificationData.get(row2.getEntry().getKey()).mActiveAppOps.contains(OP_CAMERA));
- assertFalse(mRow.getEntry().getKey() + " has op " + OP_ACCEPT_HANDOVER,
- mNotificationData.get(mRow.getEntry().getKey())
- .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
- assertFalse(row2.getEntry().getKey() + " has op " + OP_ACCEPT_HANDOVER,
- mNotificationData.get(row2.getEntry().getKey())
- .mActiveAppOps.contains(OP_ACCEPT_HANDOVER));
- }
-
- @Test
public void testGetNotificationsForCurrentUser_shouldFilterNonCurrentUserNotifications()
throws Exception {
mNotificationData.add(mRow.getEntry());
@@ -639,7 +573,10 @@ public class NotificationDataTest extends SysuiTestCase {
public static class TestableNotificationData extends NotificationData {
public TestableNotificationData(NotificationSectionsFeatureManager sectionsFeatureManager) {
- super(sectionsFeatureManager, mock(NotifLog.class));
+ super(
+ sectionsFeatureManager,
+ mock(NotifLog.class),
+ mock(PeopleNotificationIdentifier.class));
}
public static final String OVERRIDE_RANK = "r";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 72bea564d2bf..4a0b3718db6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -34,7 +34,9 @@ import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.testing.TestableResources;
+import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -78,11 +80,14 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
private KeyguardBypassController mKeyguardBypassController;
@Mock
private DozeParameters mDozeParameters;
+ @Mock
+ private MetricsLogger mMetricsLogger;
private BiometricUnlockController mBiometricUnlockController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ TestableResources res = getContext().getOrCreateTestableResources();
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
@@ -92,10 +97,11 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
mDependency.injectTestDependency(StatusBarWindowController.class,
mStatusBarWindowController);
+ res.addOverride(com.android.internal.R.integer.config_wakeUpDelayDoze, 0);
mBiometricUnlockController = new BiometricUnlockController(mContext, mDozeScrimController,
mKeyguardViewMediator, mScrimController, mStatusBar, mKeyguardStateController,
- mHandler, mUpdateMonitor, 0 /* wakeUpDelay */, mKeyguardBypassController,
- mDozeParameters);
+ mHandler, mUpdateMonitor, res.getResources(), mKeyguardBypassController,
+ mDozeParameters, mMetricsLogger);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
new file mode 100644
index 000000000000..b05172c6d7c2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.PowerManager;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.doze.DozeEvent;
+import com.android.systemui.doze.DozeHost;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+import dagger.Lazy;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DozeServiceHostTest extends SysuiTestCase {
+
+ private DozeServiceHost mDozeServiceHost;
+
+ @Mock private HeadsUpManagerPhone mHeadsUpManager;
+ @Mock private ScrimController mScrimController;
+ @Mock private DozeScrimController mDozeScrimController;
+ @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+ @Mock private VisualStabilityManager mVisualStabilityManager;
+ @Mock private KeyguardViewMediator mKeyguardViewMediator;
+ @Mock private StatusBarStateControllerImpl mStatusBarStateController;
+ @Mock private BatteryController mBatteryController;
+ @Mock private DeviceProvisionedController mDeviceProvisionedController;
+ @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock private AssistManager mAssistManager;
+ @Mock private DozeLog mDozeLog;
+ @Mock private PulseExpansionHandler mPulseExpansionHandler;
+ @Mock private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
+ @Mock private StatusBarWindowController mStatusBarWindowController;
+ @Mock private PowerManager mPowerManager;
+ @Mock private WakefulnessLifecycle mWakefullnessLifecycle;
+ @Mock private StatusBar mStatusBar;
+ @Mock private NotificationIconAreaController mNotificationIconAreaController;
+ @Mock private StatusBarWindowViewController mStatusBarWindowViewController;
+ @Mock private StatusBarWindowView mStatusBarWindow;
+ @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock private NotificationPanelView mNotificationPanel;
+ @Mock private View mAmbientIndicationContainer;
+ @Mock private BiometricUnlockController mBiometricUnlockController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
+ mDozeServiceHost = new DozeServiceHost(mDozeLog, mPowerManager, mWakefullnessLifecycle,
+ mStatusBarStateController, mDeviceProvisionedController, mHeadsUpManager,
+ mBatteryController, mScrimController, mBiometricUnlockControllerLazy,
+ mKeyguardViewMediator, mAssistManager, mDozeScrimController, mKeyguardUpdateMonitor,
+ mVisualStabilityManager, mPulseExpansionHandler, mStatusBarWindowController,
+ mNotificationWakeUpCoordinator);
+
+ mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController,
+ mStatusBarWindowViewController, mStatusBarWindow, mStatusBarKeyguardViewManager,
+ mNotificationPanel, mAmbientIndicationContainer);
+ }
+
+ @Test
+ public void testStartStopDozing() {
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
+ when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
+
+ assertFalse(mDozeServiceHost.getDozingRequested());
+
+ mDozeServiceHost.startDozing();
+ verify(mStatusBarStateController).setIsDozing(eq(true));
+ verify(mStatusBar).updateIsKeyguard();
+
+ mDozeServiceHost.stopDozing();
+ verify(mStatusBarStateController).setIsDozing(eq(false));
+ }
+
+
+ @Test
+ public void testPulseWhileDozing_updatesScrimController() {
+ mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mStatusBar.showKeyguardImpl();
+
+ // Keep track of callback to be able to stop the pulse
+// DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
+// doAnswer(invocation -> {
+// pulseCallback[0] = invocation.getArgument(0);
+// return null;
+// }).when(mDozeScrimController).pulse(any(), anyInt());
+
+ // Starting a pulse should change the scrim controller to the pulsing state
+ mDozeServiceHost.pulseWhileDozing(new DozeHost.PulseCallback() {
+ @Override
+ public void onPulseStarted() {
+ }
+
+ @Override
+ public void onPulseFinished() {
+ }
+ }, DozeEvent.PULSE_REASON_NOTIFICATION);
+
+ ArgumentCaptor<DozeHost.PulseCallback> pulseCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(DozeHost.PulseCallback.class);
+
+ verify(mDozeScrimController).pulse(
+ pulseCallbackArgumentCaptor.capture(), eq(DozeEvent.PULSE_REASON_NOTIFICATION));
+ verify(mStatusBar).updateScrimController();
+ reset(mStatusBar);
+
+ pulseCallbackArgumentCaptor.getValue().onPulseFinished();
+ assertFalse(mDozeScrimController.isPulsing());
+ verify(mStatusBar).updateScrimController();
+ }
+
+
+ @Test
+ public void testPulseWhileDozingWithDockingReason_suppressWakeUpGesture() {
+ // Keep track of callback to be able to stop the pulse
+ final DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
+ doAnswer(invocation -> {
+ pulseCallback[0] = invocation.getArgument(0);
+ return null;
+ }).when(mDozeScrimController).pulse(any(), anyInt());
+
+ // Starting a pulse while docking should suppress wakeup gesture
+ mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
+ DozeEvent.PULSE_REASON_DOCKING);
+ verify(mStatusBarWindowViewController).suppressWakeUpGesture(eq(true));
+
+ // Ending a pulse should restore wakeup gesture
+ pulseCallback[0].onPulseFinished();
+ verify(mStatusBarWindowViewController).suppressWakeUpGesture(eq(false));
+ }
+
+ @Test
+ public void testPulseWhileDozing_notifyAuthInterrupt() {
+ HashSet<Integer> reasonsWantingAuth = new HashSet<>(
+ Collections.singletonList(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN));
+ HashSet<Integer> reasonsSkippingAuth = new HashSet<>(
+ Arrays.asList(DozeEvent.PULSE_REASON_INTENT,
+ DozeEvent.PULSE_REASON_NOTIFICATION,
+ DozeEvent.PULSE_REASON_SENSOR_SIGMOTION,
+ DozeEvent.REASON_SENSOR_PICKUP,
+ DozeEvent.REASON_SENSOR_DOUBLE_TAP,
+ DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS,
+ DozeEvent.PULSE_REASON_DOCKING,
+ DozeEvent.REASON_SENSOR_WAKE_UP,
+ DozeEvent.REASON_SENSOR_TAP));
+ HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
+ Arrays.asList(DozeEvent.REASON_SENSOR_PICKUP,
+ DozeEvent.REASON_SENSOR_DOUBLE_TAP,
+ DozeEvent.REASON_SENSOR_TAP));
+
+ doAnswer(invocation -> {
+ DozeHost.PulseCallback callback = invocation.getArgument(0);
+ callback.onPulseStarted();
+ return null;
+ }).when(mDozeScrimController).pulse(any(), anyInt());
+
+ mDozeServiceHost.mWakeLockScreenPerformsAuth = true;
+ for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) {
+ reset(mKeyguardUpdateMonitor);
+ mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), i);
+ if (reasonsWantingAuth.contains(i)) {
+ verify(mKeyguardUpdateMonitor).onAuthInterruptDetected(eq(true));
+ } else if (reasonsSkippingAuth.contains(i) || reasonsThatDontPulse.contains(i)) {
+ verify(mKeyguardUpdateMonitor, never()).onAuthInterruptDetected(eq(true));
+ } else {
+ throw new AssertionError("Reason " + i + " isn't specified as wanting or skipping"
+ + " passive auth. Please consider how this pulse reason should behave.");
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index cff663562ba8..4853f2023172 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -57,6 +57,7 @@ import com.android.systemui.statusbar.notification.NotificationSectionsFeatureMa
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -236,8 +237,11 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mock(PluginManager.class),
mock(ShadeController.class),
mock(NotificationLockscreenUserManager.class),
- new NotificationEntryManager(new NotificationData(mock(
- NotificationSectionsFeatureManager.class), mock(NotifLog.class)),
+ new NotificationEntryManager(
+ new NotificationData(
+ mock(NotificationSectionsFeatureManager.class),
+ mock(NotifLog.class),
+ mock(PeopleNotificationIdentifier.class)),
mock(NotifLog.class)),
mock(KeyguardStateController.class),
statusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 1b132b962493..85c247e11f94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -64,6 +64,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -249,7 +250,7 @@ public class ScrimControllerTest extends SysuiTestCase {
finishAnimationsImmediately();
assertScrimAlpha(OPAQUE /* front */,
- SEMI_TRANSPARENT /* back */,
+ OPAQUE /* back */,
TRANSPARENT /* bubble */);
assertScrimTint(true /* front */,
@@ -858,6 +859,22 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimForBubble.getDefaultFocusHighlightEnabled());
}
+ @Test
+ public void testIsLowPowerMode() {
+ HashSet<ScrimState> lowPowerModeStates = new HashSet<>(Arrays.asList(
+ ScrimState.OFF, ScrimState.AOD, ScrimState.PULSING));
+ HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList(
+ ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER,
+ ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED,
+ ScrimState.BUBBLE_EXPANDED));
+
+ for (ScrimState state : ScrimState.values()) {
+ if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) {
+ Assert.fail("Scrim state not whitelisted nor blacklisted as low power mode");
+ }
+ }
+ }
+
private void assertScrimTint(boolean front, boolean behind, boolean bubble) {
Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
+ " with scrim: " + getScrimName(mScrimInFront) + " and tint: "
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 03d0cea65417..66c01ca58491 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -61,7 +61,9 @@ import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.DisplayMetrics;
import android.util.SparseArray;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.widget.LinearLayout;
import androidx.test.filters.SmallTest;
@@ -70,6 +72,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.InitController;
@@ -82,8 +85,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.doze.DozeEvent;
-import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
@@ -142,9 +143,6 @@ import org.mockito.MockitoAnnotations;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
import dagger.Lazy;
@@ -173,6 +171,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private ScrimController mScrimController;
@Mock private DozeScrimController mDozeScrimController;
@Mock private ArrayList<NotificationEntry> mNotificationList;
+ @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationData mNotificationData;
@Mock private NotificationInterruptionStateProvider.HeadsUpSuppressor mHeadsUpSuppressor;
@@ -227,11 +226,15 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private DozeParameters mDozeParameters;
@Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
@Mock private LockscreenWallpaper mLockscreenWallpaper;
+ @Mock private DozeServiceHost mDozeServiceHost;
+ @Mock private LinearLayout mLockIconContainer;
+ @Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter);
+ mDependency.injectMockDependency(KeyguardDismissUtil.class);
IPowerManager powerManagerService = mock(IPowerManager.class);
mPowerManager = new PowerManager(mContext, powerManagerService,
@@ -293,6 +296,7 @@ public class StatusBarTest extends SysuiTestCase {
.thenReturn(mStatusBarWindowViewController);
when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
+ when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
mStatusBar = new StatusBar(
mContext,
@@ -356,23 +360,37 @@ public class StatusBarTest extends SysuiTestCase {
mNotifLog,
mDozeParameters,
mScrimController,
- mLockscreenWallpaperLazy);
+ mLockscreenWallpaperLazy,
+ mBiometricUnlockControllerLazy,
+ mDozeServiceHost,
+ mPowerManager,
+ mDozeScrimController);
+
+ when(mStatusBarWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
+ mLockIconContainer);
+
+ when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
+ any(NotificationPanelView.class), any(BiometricUnlockController.class),
+ any(ViewGroup.class), any(ViewGroup.class), any(KeyguardBypassController.class)))
+ .thenReturn(mStatusBarKeyguardViewManager);
+
+ when(mKeyguardViewMediator.getViewMediatorCallback()).thenReturn(
+ mKeyguardVieMediatorCallback);
+
// TODO: we should be able to call mStatusBar.start() and have all the below values
// initialized automatically.
mStatusBar.mComponents = mContext.getComponents();
- mStatusBar.mStatusBarKeyguardViewManager = mStatusBarKeyguardViewManager;
mStatusBar.mStatusBarWindow = mStatusBarWindowView;
- mStatusBar.mBiometricUnlockController = mBiometricUnlockController;
mStatusBar.mNotificationPanel = mNotificationPanelView;
mStatusBar.mCommandQueue = mCommandQueue;
mStatusBar.mDozeScrimController = mDozeScrimController;
mStatusBar.mNotificationIconAreaController = mNotificationIconAreaController;
mStatusBar.mPresenter = mNotificationPresenter;
mStatusBar.mKeyguardIndicationController = mKeyguardIndicationController;
- mStatusBar.mPowerManager = mPowerManager;
mStatusBar.mBarService = mBarService;
mStatusBar.mStackScroller = mStackScroller;
mStatusBar.mStatusBarWindowViewController = mStatusBarWindowViewController;
+ mStatusBar.startKeyguard();
mStatusBar.putComponent(StatusBar.class, mStatusBar);
Dependency.get(InitController.class).executePostInitTasks();
entryManager.setUpForTest(mock(NotificationPresenter.class), mStackScroller,
@@ -737,83 +755,18 @@ public class StatusBarTest extends SysuiTestCase {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
mStatusBar.showKeyguardImpl();
- // Keep track of callback to be able to stop the pulse
- DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
- doAnswer(invocation -> {
- pulseCallback[0] = invocation.getArgument(0);
- return null;
- }).when(mDozeScrimController).pulse(any(), anyInt());
-
// Starting a pulse should change the scrim controller to the pulsing state
- mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
- DozeEvent.PULSE_REASON_NOTIFICATION);
+ when(mDozeServiceHost.isPulsing()).thenReturn(true);
+ mStatusBar.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any());
// Ending a pulse should take it back to keyguard state
- pulseCallback[0].onPulseFinished();
+ when(mDozeServiceHost.isPulsing()).thenReturn(false);
+ mStatusBar.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
}
@Test
- public void testPulseWhileDozing_notifyAuthInterrupt() {
- HashSet<Integer> reasonsWantingAuth = new HashSet<>(
- Collections.singletonList(DozeEvent.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN));
- HashSet<Integer> reasonsSkippingAuth = new HashSet<>(
- Arrays.asList(DozeEvent.PULSE_REASON_INTENT,
- DozeEvent.PULSE_REASON_NOTIFICATION,
- DozeEvent.PULSE_REASON_SENSOR_SIGMOTION,
- DozeEvent.REASON_SENSOR_PICKUP,
- DozeEvent.REASON_SENSOR_DOUBLE_TAP,
- DozeEvent.PULSE_REASON_SENSOR_LONG_PRESS,
- DozeEvent.PULSE_REASON_DOCKING,
- DozeEvent.REASON_SENSOR_WAKE_UP,
- DozeEvent.REASON_SENSOR_TAP));
- HashSet<Integer> reasonsThatDontPulse = new HashSet<>(
- Arrays.asList(DozeEvent.REASON_SENSOR_PICKUP,
- DozeEvent.REASON_SENSOR_DOUBLE_TAP,
- DozeEvent.REASON_SENSOR_TAP));
-
- doAnswer(invocation -> {
- DozeHost.PulseCallback callback = invocation.getArgument(0);
- callback.onPulseStarted();
- return null;
- }).when(mDozeScrimController).pulse(any(), anyInt());
-
- mStatusBar.mDozeServiceHost.mWakeLockScreenPerformsAuth = true;
- for (int i = 0; i < DozeEvent.TOTAL_REASONS; i++) {
- reset(mKeyguardUpdateMonitor);
- mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class), i);
- if (reasonsWantingAuth.contains(i)) {
- verify(mKeyguardUpdateMonitor).onAuthInterruptDetected(eq(true));
- } else if (reasonsSkippingAuth.contains(i) || reasonsThatDontPulse.contains(i)) {
- verify(mKeyguardUpdateMonitor, never()).onAuthInterruptDetected(eq(true));
- } else {
- throw new AssertionError("Reason " + i + " isn't specified as wanting or skipping"
- + " passive auth. Please consider how this pulse reason should behave.");
- }
- }
- }
-
- @Test
- public void testPulseWhileDozingWithDockingReason_suppressWakeUpGesture() {
- // Keep track of callback to be able to stop the pulse
- final DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
- doAnswer(invocation -> {
- pulseCallback[0] = invocation.getArgument(0);
- return null;
- }).when(mDozeScrimController).pulse(any(), anyInt());
-
- // Starting a pulse while docking should suppress wakeup gesture
- mStatusBar.mDozeServiceHost.pulseWhileDozing(mock(DozeHost.PulseCallback.class),
- DozeEvent.PULSE_REASON_DOCKING);
- verify(mStatusBarWindowViewController).suppressWakeUpGesture(eq(true));
-
- // Ending a pulse should restore wakeup gesture
- pulseCallback[0].onPulseFinished();
- verify(mStatusBarWindowViewController).suppressWakeUpGesture(eq(false));
- }
-
- @Test
public void testSetState_changesIsFullScreenUserSwitcherState() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
assertFalse(mStatusBar.isFullScreenUserSwitcherState());
@@ -839,27 +792,17 @@ public class StatusBarTest extends SysuiTestCase {
}
@Test
- public void testStartStopDozing() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
-
- mStatusBar.mDozeServiceHost.startDozing();
- verify(mStatusBarStateController).setIsDozing(eq(true));
-
- mStatusBar.mDozeServiceHost.stopDozing();
- verify(mStatusBarStateController).setIsDozing(eq(false));
- }
-
- @Test
public void testOnStartedWakingUp_isNotDozing() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
- mStatusBar.mDozeServiceHost.startDozing();
- verify(mStatusBarStateController).setIsDozing(eq(true));
+ when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
+ mStatusBar.updateIsKeyguard();
+ // TODO: mNotificationPanelView.expand(false) gets called twice. Should be once.
+ verify(mNotificationPanelView, times(2)).expand(eq(false));
clearInvocations(mNotificationPanelView);
mStatusBar.mWakefulnessObserver.onStartedWakingUp();
- verify(mStatusBarStateController).setIsDozing(eq(false));
+ verify(mDozeServiceHost).stopDozing();
verify(mNotificationPanelView).expand(eq(false));
}
@@ -867,7 +810,8 @@ public class StatusBarTest extends SysuiTestCase {
public void testOnStartedWakingUp_doesNotDismissBouncer_whenPulsing() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
- mStatusBar.mDozeServiceHost.startDozing();
+ when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
+ mStatusBar.updateIsKeyguard();
clearInvocations(mNotificationPanelView);
mStatusBar.setBouncerShowing(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index 556ed5c30496..4ccf8a4a4990 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -42,6 +42,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
+import java.util.ArrayList;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -67,7 +69,8 @@ public class HotspotControllerImplTest extends SysuiTestCase {
mContext.addMockSystemService(WifiManager.class, mWifiManager);
doAnswer((InvocationOnMock invocation) -> {
- ((WifiManager.SoftApCallback) invocation.getArgument(0)).onNumClientsChanged(1);
+ ((WifiManager.SoftApCallback) invocation.getArgument(0))
+ .onConnectedClientsChanged(new ArrayList<>());
return null;
}).when(mWifiManager).registerSoftApCallback(any(WifiManager.SoftApCallback.class),
any(Handler.class));
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
new file mode 100644
index 000000000000..ca69c187089d
--- /dev/null
+++ b/packages/Tethering/Android.bp
@@ -0,0 +1,79 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+java_defaults {
+ name: "TetheringAndroidLibraryDefaults",
+ platform_apis: true,
+ srcs: [
+ "src/**/*.java",
+ ":framework-tethering-shared-srcs",
+ ":services-tethering-shared-srcs",
+ ],
+ static_libs: [
+ "androidx.annotation_annotation",
+ "netd_aidl_interface-java",
+ "networkstack-aidl-interfaces-java",
+ "tethering-client",
+ ],
+ manifest: "AndroidManifestBase.xml",
+}
+
+// Build tethering static library, used to compile both variants of the tethering.
+android_library {
+ name: "TetheringApiCurrentLib",
+ defaults: ["TetheringAndroidLibraryDefaults"],
+}
+
+// Common defaults for compiling the actual APK.
+java_defaults {
+ name: "TetheringAppDefaults",
+ platform_apis: true,
+ privileged: true,
+ resource_dirs: [
+ "res",
+ ],
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ },
+}
+
+// Non-updatable tethering running in the system server process for devices not using the module
+// TODO: build in-process tethering APK here.
+
+// Updatable tethering packaged as an application
+android_app {
+ name: "Tethering",
+ defaults: ["TetheringAppDefaults"],
+ static_libs: ["TetheringApiCurrentLib"],
+ certificate: "networkstack",
+ manifest: "AndroidManifest.xml",
+ use_embedded_native_libs: true,
+ // The permission configuration *must* be included to ensure security of the device
+ required: ["NetworkPermissionConfig"],
+}
+
+// This group will be removed when tethering migration is done.
+filegroup {
+ name: "tethering-services-srcs",
+ srcs: [
+ "src/com/android/server/connectivity/tethering/TetheringConfiguration.java",
+ "src/android/net/dhcp/DhcpServerCallbacks.java",
+ "src/android/net/dhcp/DhcpServingParamsParcelExt.java",
+ "src/android/net/ip/IpServer.java",
+ "src/android/net/ip/RouterAdvertisementDaemon.java",
+ "src/android/net/util/InterfaceSet.java",
+ ],
+}
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
new file mode 100644
index 000000000000..eb51593e4538
--- /dev/null
+++ b/packages/Tethering/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tethering"
+ android:sharedUserId="android.uid.networkstack">
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+
+ <application
+ android:process="com.android.networkstack.process"
+ android:extractNativeLibs="false"
+ android:persistent="true">
+ </application>
+</manifest>
diff --git a/packages/Tethering/AndroidManifestBase.xml b/packages/Tethering/AndroidManifestBase.xml
new file mode 100644
index 000000000000..b9cac1994359
--- /dev/null
+++ b/packages/Tethering/AndroidManifestBase.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tethering"
+ android:versionCode="1"
+ android:versionName="R-initial">
+ <application
+ android:label="Tethering"
+ android:defaultToDeviceProtectedStorage="true"
+ android:directBootAware="true"
+ android:usesCleartextTraffic="true">
+ </application>
+</manifest>
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
new file mode 100644
index 000000000000..5b01b1e038c3
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// AIDL interfaces between the core system and the tethering mainline module.
+aidl_interface {
+ name: "tethering-aidl-interfaces",
+ local_include_dir: "src",
+ srcs: [
+ "src/android/net/ITetheringConnector.aidl",
+ ],
+ backend: {
+ ndk: {
+ enabled: false,
+ },
+ cpp: {
+ enabled: false,
+ },
+ },
+}
+
+java_library {
+ name: "tethering-client",
+ platform_apis: true,
+ static_libs: [
+ "tethering-aidl-interfaces-java",
+ ],
+}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
new file mode 100644
index 000000000000..443481e33f8d
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing perNmissions and
+ * limitations under the License.
+ */
+package android.net;
+
+/** @hide */
+oneway interface ITetheringConnector {
+}
diff --git a/packages/Tethering/proguard.flags b/packages/Tethering/proguard.flags
new file mode 100644
index 000000000000..77fc024a8755
--- /dev/null
+++ b/packages/Tethering/proguard.flags
@@ -0,0 +1 @@
+#TBD
diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml
new file mode 100644
index 000000000000..37e679dbeb63
--- /dev/null
+++ b/packages/Tethering/res/values/config.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!--
+ OEMs that wish to change the below settings must do so via a runtime resource overlay package
+ and *NOT* by changing this file. This file is part of the tethering mainline module.
+ -->
+</resources>
diff --git a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
index 7c41377985d3..7c41377985d3 100644
--- a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
+++ b/packages/Tethering/src/android/net/dhcp/DhcpServerCallbacks.java
diff --git a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
index 1fe2328f1cdb..1fe2328f1cdb 100644
--- a/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
+++ b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
diff --git a/services/net/java/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 3d79bba7bbe3..ff3d7bc6fd35 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -16,7 +16,7 @@
package android.net.ip;
-import static android.net.NetworkUtils.numericToInetAddress;
+import static android.net.InetAddresses.parseNumericAddress;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.util.NetworkConstants.FF;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
@@ -77,6 +77,7 @@ public class IpServer extends StateMachine {
public static final int STATE_TETHERED = 2;
public static final int STATE_LOCAL_ONLY = 3;
+ /** Get string name of |state|.*/
public static String getStateString(int state) {
switch (state) {
case STATE_UNAVAILABLE: return "UNAVAILABLE";
@@ -103,15 +104,16 @@ public class IpServer extends StateMachine {
// TODO: have this configurable
private static final int DHCP_LEASE_TIME_SECS = 3600;
- private final static String TAG = "IpServer";
- private final static boolean DBG = false;
- private final static boolean VDBG = false;
- private static final Class[] messageClasses = {
+ private static final String TAG = "IpServer";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
+ private static final Class[] sMessageClasses = {
IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
- MessageUtils.findMessageNames(messageClasses);
+ MessageUtils.findMessageNames(sMessageClasses);
+ /** IpServer callback. */
public static class Callback {
/**
* Notify that |who| has changed its tethering state.
@@ -131,11 +133,14 @@ public class IpServer extends StateMachine {
public void updateLinkProperties(IpServer who, LinkProperties newLp) {}
}
+ /** Capture IpServer dependencies, for injection. */
public static class Dependencies {
+ /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams);
}
+ /** Get |ifName|'s interface information.*/
public InterfaceParams getInterfaceParams(String ifName) {
return InterfaceParams.getByName(ifName);
}
@@ -244,25 +249,51 @@ public class IpServer extends StateMachine {
setInitialState(mInitialState);
}
- public String interfaceName() { return mIfaceName; }
-
- public int interfaceType() { return mInterfaceType; }
+ /** Interface name which IpServer served.*/
+ public String interfaceName() {
+ return mIfaceName;
+ }
- public int lastError() { return mLastError; }
+ /**
+ * Tethering downstream type. It would be one of ConnectivityManager#TETHERING_*.
+ */
+ public int interfaceType() {
+ return mInterfaceType;
+ }
- public int servingMode() { return mServingMode; }
+ /** Last error from this IpServer. */
+ public int lastError() {
+ return mLastError;
+ }
- public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); }
+ /** Serving mode is the current state of IpServer state machine. */
+ public int servingMode() {
+ return mServingMode;
+ }
- public void stop() { sendMessage(CMD_INTERFACE_DOWN); }
+ /** The properties of the network link which IpServer is serving. */
+ public LinkProperties linkProperties() {
+ return new LinkProperties(mLinkProperties);
+ }
- public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); }
+ /** Stop this IpServer. After this is called this IpServer should not be used any more. */
+ public void stop() {
+ sendMessage(CMD_INTERFACE_DOWN);
+ }
/**
- * Internals.
+ * Tethering is canceled. IpServer state machine will be available and wait for
+ * next tethering request.
*/
+ public void unwanted() {
+ sendMessage(CMD_TETHER_UNREQUESTED);
+ }
+
+ /** Internals. */
- private boolean startIPv4() { return configureIPv4(true); }
+ private boolean startIPv4() {
+ return configureIPv4(true);
+ }
/**
* Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer
@@ -410,7 +441,7 @@ public class IpServer extends StateMachine {
prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH;
} else {
// BT configures the interface elsewhere: only start DHCP.
- final Inet4Address srvAddr = (Inet4Address) numericToInetAddress(BLUETOOTH_IFACE_ADDR);
+ final Inet4Address srvAddr = (Inet4Address) parseNumericAddress(BLUETOOTH_IFACE_ADDR);
return configureDhcp(enabled, srvAddr, BLUETOOTH_DHCP_PREFIX_LENGTH);
}
@@ -422,7 +453,7 @@ public class IpServer extends StateMachine {
return false;
}
- InetAddress addr = numericToInetAddress(ipAsString);
+ InetAddress addr = parseNumericAddress(ipAsString);
linkAddr = new LinkAddress(addr, prefixLen);
ifcg.setLinkAddress(linkAddr);
if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
@@ -473,7 +504,7 @@ public class IpServer extends StateMachine {
private String getRandomWifiIPv4Address() {
try {
- byte[] bytes = numericToInetAddress(WIFI_HOST_IFACE_ADDR).getAddress();
+ byte[] bytes = parseNumericAddress(WIFI_HOST_IFACE_ADDR).getAddress();
bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
return InetAddress.getByAddress(bytes).getHostAddress();
} catch (Exception e) {
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 59aea21f46c8..414741309ef0 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -119,6 +119,7 @@ public class RouterAdvertisementDaemon {
private volatile MulticastTransmitter mMulticastTransmitter;
private volatile UnicastResponder mUnicastResponder;
+ /** Encapsulate the RA parameters for RouterAdvertisementDaemon.*/
public static class RaParams {
// Tethered traffic will have the hop limit properly decremented.
// Consequently, set the hoplimit greater by one than the upstream
@@ -150,10 +151,12 @@ public class RouterAdvertisementDaemon {
dnses = (HashSet) other.dnses.clone();
}
- // Returns the subset of RA parameters that become deprecated when
- // moving from announcing oldRa to announcing newRa.
- //
- // Currently only tracks differences in |prefixes| and |dnses|.
+ /**
+ * Returns the subset of RA parameters that become deprecated when
+ * moving from announcing oldRa to announcing newRa.
+ *
+ * Currently only tracks differences in |prefixes| and |dnses|.
+ */
public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
RaParams newlyDeprecated = new RaParams();
@@ -179,7 +182,9 @@ public class RouterAdvertisementDaemon {
private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
- Set<IpPrefix> getPrefixes() { return mPrefixes.keySet(); }
+ Set<IpPrefix> getPrefixes() {
+ return mPrefixes.keySet();
+ }
void putPrefixes(Set<IpPrefix> prefixes) {
for (IpPrefix ipp : prefixes) {
@@ -193,7 +198,9 @@ public class RouterAdvertisementDaemon {
}
}
- Set<Inet6Address> getDnses() { return mDnses.keySet(); }
+ Set<Inet6Address> getDnses() {
+ return mDnses.keySet();
+ }
void putDnses(Set<Inet6Address> dnses) {
for (Inet6Address dns : dnses) {
@@ -207,7 +214,9 @@ public class RouterAdvertisementDaemon {
}
}
- boolean isEmpty() { return mPrefixes.isEmpty() && mDnses.isEmpty(); }
+ boolean isEmpty() {
+ return mPrefixes.isEmpty() && mDnses.isEmpty();
+ }
private boolean decrementCounters() {
boolean removed = decrementCounter(mPrefixes);
@@ -219,7 +228,7 @@ public class RouterAdvertisementDaemon {
boolean removed = false;
for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
- it.hasNext();) {
+ it.hasNext();) {
Map.Entry<T, Integer> kv = it.next();
if (kv.getValue() == 0) {
it.remove();
@@ -240,6 +249,7 @@ public class RouterAdvertisementDaemon {
mDeprecatedInfoTracker = new DeprecatedInfoTracker();
}
+ /** Build new RA.*/
public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
synchronized (mLock) {
if (deprecatedParams != null) {
@@ -260,6 +270,7 @@ public class RouterAdvertisementDaemon {
maybeNotifyMulticastTransmitter();
}
+ /** Start router advertisement daemon. */
public boolean start() {
if (!createSocket()) {
return false;
@@ -274,6 +285,7 @@ public class RouterAdvertisementDaemon {
return true;
}
+ /** Stop router advertisement daemon. */
public void stop() {
closeSocket();
// Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before
@@ -362,8 +374,12 @@ public class RouterAdvertisementDaemon {
}
}
- private static byte asByte(int value) { return (byte) value; }
- private static short asShort(int value) { return (short) value; }
+ private static byte asByte(int value) {
+ return (byte) value;
+ }
+ private static short asShort(int value) {
+ return (short) value;
+ }
private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) {
/**
@@ -384,14 +400,14 @@ public class RouterAdvertisementDaemon {
+-+-+-+-+-+-+-+-+-+-+-+-
*/
ra.put(ICMPV6_ND_ROUTER_ADVERT)
- .put(asByte(0))
- .putShort(asShort(0))
- .put(hopLimit)
- // RFC 4191 "high" preference, iff. advertising a default route.
- .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
- .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
- .putInt(0)
- .putInt(0);
+ .put(asByte(0))
+ .putShort(asShort(0))
+ .put(hopLimit)
+ // RFC 4191 "high" preference, iff. advertising a default route.
+ .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
+ .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
+ .putInt(0)
+ .putInt(0);
}
private static void putSlla(ByteBuffer ra, byte[] slla) {
@@ -408,11 +424,12 @@ public class RouterAdvertisementDaemon {
// Only IEEE 802.3 6-byte addresses are supported.
return;
}
- final byte ND_OPTION_SLLA = 1;
- final byte SLLA_NUM_8OCTETS = 1;
- ra.put(ND_OPTION_SLLA)
- .put(SLLA_NUM_8OCTETS)
- .put(slla);
+
+ final byte nd_option_slla = 1;
+ final byte slla_num_8octets = 1;
+ ra.put(nd_option_slla)
+ .put(slla_num_8octets)
+ .put(slla);
}
private static void putExpandedFlagsOption(ByteBuffer ra) {
@@ -428,13 +445,13 @@ public class RouterAdvertisementDaemon {
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- final byte ND_OPTION_EFO = 26;
- final byte EFO_NUM_8OCTETS = 1;
+ final byte nd_option__efo = 26;
+ final byte efo_num_8octets = 1;
- ra.put(ND_OPTION_EFO)
- .put(EFO_NUM_8OCTETS)
- .putShort(asShort(0))
- .putInt(0);
+ ra.put(nd_option__efo)
+ .put(efo_num_8octets)
+ .putShort(asShort(0))
+ .putInt(0);
}
private static void putMtu(ByteBuffer ra, int mtu) {
@@ -449,12 +466,12 @@ public class RouterAdvertisementDaemon {
| MTU |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
- final byte ND_OPTION_MTU = 5;
- final byte MTU_NUM_8OCTETS = 1;
- ra.put(ND_OPTION_MTU)
- .put(MTU_NUM_8OCTETS)
- .putShort(asShort(0))
- .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
+ final byte nd_option_mtu = 5;
+ final byte mtu_num_8octs = 1;
+ ra.put(nd_option_mtu)
+ .put(mtu_num_8octs)
+ .putShort(asShort(0))
+ .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
}
private static void putPio(ByteBuffer ra, IpPrefix ipp,
@@ -486,22 +503,22 @@ public class RouterAdvertisementDaemon {
if (prefixLength != 64) {
return;
}
- final byte ND_OPTION_PIO = 3;
- final byte PIO_NUM_8OCTETS = 4;
+ final byte nd_option_pio = 3;
+ final byte pio_num_8octets = 4;
if (validTime < 0) validTime = 0;
if (preferredTime < 0) preferredTime = 0;
if (preferredTime > validTime) preferredTime = validTime;
final byte[] addr = ipp.getAddress().getAddress();
- ra.put(ND_OPTION_PIO)
- .put(PIO_NUM_8OCTETS)
- .put(asByte(prefixLength))
- .put(asByte(0xc0)) /* L & A set */
- .putInt(validTime)
- .putInt(preferredTime)
- .putInt(0)
- .put(addr);
+ ra.put(nd_option_pio)
+ .put(pio_num_8octets)
+ .put(asByte(prefixLength))
+ .put(asByte(0xc0)) /* L & A set */
+ .putInt(validTime)
+ .putInt(preferredTime)
+ .putInt(0)
+ .put(addr);
}
private static void putRio(ByteBuffer ra, IpPrefix ipp) {
@@ -524,16 +541,16 @@ public class RouterAdvertisementDaemon {
if (prefixLength > 64) {
return;
}
- final byte ND_OPTION_RIO = 24;
- final byte RIO_NUM_8OCTETS = asByte(
+ final byte nd_option_rio = 24;
+ final byte rio_num_8octets = asByte(
(prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3);
final byte[] addr = ipp.getAddress().getAddress();
- ra.put(ND_OPTION_RIO)
- .put(RIO_NUM_8OCTETS)
- .put(asByte(prefixLength))
- .put(asByte(0x18))
- .putInt(DEFAULT_LIFETIME);
+ ra.put(nd_option_rio)
+ .put(rio_num_8octets)
+ .put(asByte(prefixLength))
+ .put(asByte(0x18))
+ .putInt(DEFAULT_LIFETIME);
// Rely upon an IpPrefix's address being properly zeroed.
if (prefixLength > 0) {
@@ -566,12 +583,12 @@ public class RouterAdvertisementDaemon {
}
if (filteredDnses.isEmpty()) return;
- final byte ND_OPTION_RDNSS = 25;
- final byte RDNSS_NUM_8OCTETS = asByte(dnses.size() * 2 + 1);
- ra.put(ND_OPTION_RDNSS)
- .put(RDNSS_NUM_8OCTETS)
- .putShort(asShort(0))
- .putInt(lifetime);
+ final byte nd_option_rdnss = 25;
+ final byte rdnss_num_8octets = asByte(dnses.size() * 2 + 1);
+ ra.put(nd_option_rdnss)
+ .put(rdnss_num_8octets)
+ .putShort(asShort(0))
+ .putInt(lifetime);
for (Inet6Address dns : filteredDnses) {
// NOTE: If the full of list DNS servers doesn't fit in the packet,
@@ -585,7 +602,7 @@ public class RouterAdvertisementDaemon {
}
private boolean createSocket() {
- final int SEND_TIMEOUT_MS = 300;
+ final int send_timout_ms = 300;
final int oldTag = TrafficStats.getAndSetThreadStatsTag(
TrafficStatsConstants.TAG_SYSTEM_NEIGHBOR);
@@ -593,7 +610,7 @@ public class RouterAdvertisementDaemon {
mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
// Setting SNDTIMEO is purely for defensive purposes.
Os.setsockoptTimeval(
- mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(SEND_TIMEOUT_MS));
+ mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms));
Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name);
NetworkUtils.protectFromVpn(mSocket);
NetworkUtils.setupRaSocket(mSocket, mInterface.index);
@@ -611,7 +628,7 @@ public class RouterAdvertisementDaemon {
if (mSocket != null) {
try {
IoBridge.closeAndSignalBlockedThreads(mSocket);
- } catch (IOException ignored) {}
+ } catch (IOException ignored) { }
}
mSocket = null;
}
@@ -627,9 +644,9 @@ public class RouterAdvertisementDaemon {
}
final InetAddress destip = dest.getAddress();
- return (destip instanceof Inet6Address) &&
- destip.isLinkLocalAddress() &&
- (((Inet6Address) destip).getScopeId() == mInterface.index);
+ return (destip instanceof Inet6Address)
+ && destip.isLinkLocalAddress()
+ && (((Inet6Address) destip).getScopeId() == mInterface.index);
}
private void maybeSendRA(InetSocketAddress dest) {
@@ -654,11 +671,11 @@ public class RouterAdvertisementDaemon {
}
private final class UnicastResponder extends Thread {
- private final InetSocketAddress solicitor = new InetSocketAddress();
+ private final InetSocketAddress mSolicitor = new InetSocketAddress();
// The recycled buffer for receiving Router Solicitations from clients.
// If the RS is larger than IPV6_MIN_MTU the packets are truncated.
// This is fine since currently only byte 0 is examined anyway.
- private final byte mSolication[] = new byte[IPV6_MIN_MTU];
+ private final byte[] mSolicitation = new byte[IPV6_MIN_MTU];
@Override
public void run() {
@@ -666,9 +683,9 @@ public class RouterAdvertisementDaemon {
try {
// Blocking receive.
final int rval = Os.recvfrom(
- mSocket, mSolication, 0, mSolication.length, 0, solicitor);
+ mSocket, mSolicitation, 0, mSolicitation.length, 0, mSolicitor);
// Do the least possible amount of validation.
- if (rval < 1 || mSolication[0] != ICMPV6_ND_ROUTER_SOLICIT) {
+ if (rval < 1 || mSolicitation[0] != ICMPV6_ND_ROUTER_SOLICIT) {
continue;
}
} catch (ErrnoException | SocketException e) {
@@ -678,7 +695,7 @@ public class RouterAdvertisementDaemon {
continue;
}
- maybeSendRA(solicitor);
+ maybeSendRA(mSolicitor);
}
}
}
diff --git a/services/net/java/android/net/util/InterfaceSet.java b/packages/Tethering/src/android/net/util/InterfaceSet.java
index 9f26fa17ef5c..758978711bd4 100644
--- a/services/net/java/android/net/util/InterfaceSet.java
+++ b/packages/Tethering/src/android/net/util/InterfaceSet.java
@@ -47,6 +47,6 @@ public class InterfaceSet {
public boolean equals(Object obj) {
return obj != null
&& obj instanceof InterfaceSet
- && ifnames.equals(((InterfaceSet)obj).ifnames);
+ && ifnames.equals(((InterfaceSet) obj).ifnames);
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index a1b94ca33944..77097271c454 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -82,7 +82,7 @@ public class TetheringConfiguration {
"192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
};
- private final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
+ private static final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
public final String[] tetherableUsbRegexs;
public final String[] tetherableWifiRegexs;
@@ -133,10 +133,12 @@ public class TetheringConfiguration {
configLog.log(toString());
}
+ /** Check whether input interface belong to usb.*/
public boolean isUsb(String iface) {
return matchesDownstreamRegexs(iface, tetherableUsbRegexs);
}
+ /** Check whether input interface belong to wifi.*/
public boolean isWifi(String iface) {
return matchesDownstreamRegexs(iface, tetherableWifiRegexs);
}
@@ -146,18 +148,22 @@ public class TetheringConfiguration {
return matchesDownstreamRegexs(iface, tetherableWifiP2pRegexs);
}
+ /** Check whether using legacy mode for wifi P2P. */
public boolean isWifiP2pLegacyTetheringMode() {
return (tetherableWifiP2pRegexs == null || tetherableWifiP2pRegexs.length == 0);
}
+ /** Check whether input interface belong to bluetooth.*/
public boolean isBluetooth(String iface) {
return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
}
+ /** Check whether no ui entitlement application is available.*/
public boolean hasMobileHotspotProvisionApp() {
return !TextUtils.isEmpty(provisioningAppNoUi);
}
+ /** Does the dumping.*/
public void dump(PrintWriter pw) {
pw.print("subId: ");
pw.println(subId);
@@ -186,6 +192,7 @@ public class TetheringConfiguration {
pw.println(enableLegacyDhcpServer);
}
+ /** Returns the string representation of this object.*/
public String toString() {
final StringJoiner sj = new StringJoiner(" ");
sj.add(String.format("subId:%d", subId));
@@ -210,7 +217,7 @@ public class TetheringConfiguration {
if (values != null) {
final StringJoiner sj = new StringJoiner(", ", "[", "]");
- for (String value : values) { sj.add(value); }
+ for (String value : values) sj.add(value);
pw.print(sj.toString());
} else {
pw.print("null");
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
new file mode 100644
index 000000000000..da621076bb63
--- /dev/null
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -0,0 +1,50 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+ name: "TetheringTests",
+ certificate: "platform",
+ srcs: ["src/**/*.java"],
+ test_suites: ["device-tests"],
+ static_libs: [
+ "androidx.test.rules",
+ "frameworks-base-testutils",
+ "mockito-target-extended-minus-junit4",
+ "TetheringApiCurrentLib",
+ "testables",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+ jni_libs: [
+ // For mockito extended
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+}
+
+// This group would be removed when tethering migration is done.
+filegroup {
+ name: "tethering-tests-src",
+ srcs: [
+ "src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java",
+ "src/android/net/dhcp/DhcpServingParamsParcelExtTest.java",
+ "src/android/net/ip/IpServerTest.java",
+ "src/android/net/util/InterfaceSetTest.java",
+ ],
+}
diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml
new file mode 100644
index 000000000000..049ff6d415f3
--- /dev/null
+++ b/packages/Tethering/tests/unit/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tethering.tests.unit">
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.tethering.tests.unit"
+ android:label="Tethering service tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
index e01ac7f08c2f..e01ac7f08c2f 100644
--- a/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index b6ccebb3dcd3..4358cd6966f1 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -183,7 +183,7 @@ public class IpServerTest {
@Test
public void shouldDoNothingUntilRequested() throws Exception {
initStateMachine(TETHERING_BLUETOOTH);
- final int [] NOOP_COMMANDS = {
+ final int [] noOp_commands = {
IpServer.CMD_TETHER_UNREQUESTED,
IpServer.CMD_IP_FORWARDING_ENABLE_ERROR,
IpServer.CMD_IP_FORWARDING_DISABLE_ERROR,
@@ -192,7 +192,7 @@ public class IpServerTest {
IpServer.CMD_SET_DNS_FORWARDERS_ERROR,
IpServer.CMD_TETHER_CONNECTION_CHANGED
};
- for (int command : NOOP_COMMANDS) {
+ for (int command : noOp_commands) {
// None of these commands should trigger us to request action from
// the rest of the system.
dispatchCommand(command);
diff --git a/tests/net/java/android/net/util/InterfaceSetTest.java b/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
index ea084b607868..ea084b607868 100644
--- a/tests/net/java/android/net/util/InterfaceSetTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index e28296354d2a..9f9221f3010d 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -24,7 +24,12 @@ import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
+import static com.android.internal.R.array.config_tether_bluetooth_regexs;
+import static com.android.internal.R.array.config_tether_dhcp_range;
import static com.android.internal.R.array.config_tether_upstream_types;
+import static com.android.internal.R.array.config_tether_usb_regexs;
+import static com.android.internal.R.array.config_tether_wifi_regexs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -86,7 +91,9 @@ public class TetheringConfigurationTest {
}
@Override
- public Resources getResources() { return mResources; }
+ public Resources getResources() {
+ return mResources;
+ }
@Override
public Object getSystemService(String name) {
@@ -105,17 +112,13 @@ public class TetheringConfigurationTest {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
- .thenReturn(new String[0]);
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
- .thenReturn(new String[0]);
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
+ when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]);
+ when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]);
+ when(mResources.getStringArray(config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d" });
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
+ when(mResources.getStringArray(config_tether_bluetooth_regexs)).thenReturn(new String[0]);
when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
- when(mResources.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
+ when(mResources.getStringArray(config_mobile_hotspot_provision_app))
.thenReturn(new String[0]);
mContentResolver = new MockContentResolver();
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -297,19 +300,16 @@ public class TetheringConfigurationTest {
private void setUpResourceForSubId() {
when(mResourcesForSubId.getStringArray(
- com.android.internal.R.array.config_tether_dhcp_range)).thenReturn(new String[0]);
+ config_tether_dhcp_range)).thenReturn(new String[0]);
when(mResourcesForSubId.getStringArray(
- com.android.internal.R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
+ config_tether_usb_regexs)).thenReturn(new String[0]);
when(mResourcesForSubId.getStringArray(
- com.android.internal.R.array.config_tether_wifi_regexs))
- .thenReturn(new String[]{ "test_wlan\\d" });
+ config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
when(mResourcesForSubId.getStringArray(
- com.android.internal.R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
+ config_tether_bluetooth_regexs)).thenReturn(new String[0]);
when(mResourcesForSubId.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
when(mResourcesForSubId.getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app))
- .thenReturn(PROVISIONING_APP_NAME);
+ config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
}
}
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 5d6d1c993cd3..5712566311d7 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -253,6 +253,8 @@ message SystemMessage {
NOTE_NETWORK_LOGGED_IN = 744;
// A partial connectivity network was detected during network validation
NOTE_NETWORK_PARTIAL_CONNECTIVITY = 745;
+ // Private DNS is broken in strict mode
+ NOTE_NETWORK_PRIVATE_DNS_BROKEN = 746;
// Notify the user that their work profile has been deleted
// Package: android
diff --git a/services/art-profile b/services/art-profile
index 8b911a24b5cc..a0338d55c55f 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -21148,7 +21148,6 @@ HPLcom/android/server/wm/RemoteAnimationController$FinishedCallback;->release()V
HPLcom/android/server/wm/RemoteAnimationController;->lambda$goodToGo$1$RemoteAnimationController([Landroid/view/RemoteAnimationTarget;[Landroid/view/RemoteAnimationTarget;)V
HPLcom/android/server/wm/RunningTasks;->getTasks(ILjava/util/List;IILjava/util/ArrayList;IZZLandroid/util/ArraySet;)V
HPLcom/android/server/wm/SplashScreenStartingData;->createStartingSurface(Lcom/android/server/wm/ActivityRecord;)Lcom/android/server/policy/WindowManagerPolicy$StartingSurface;
-HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
HPLcom/android/server/wm/SurfaceAnimator;->lambda$getFinishedCallback$0$SurfaceAnimator(Lcom/android/server/wm/AnimationAdapter;Ljava/lang/Runnable;)V
HPLcom/android/server/wm/Task;->positionChildAt(ILcom/android/server/wm/ActivityRecord;Z)V
HPLcom/android/server/wm/TaskRecord;->handlesOrientationChangeFromDescendant()Z
@@ -21191,7 +21190,6 @@ HPLcom/android/server/wm/DisplayRotation;->updateRotationUnchecked(Z)Z
HPLcom/android/server/wm/RemoteAnimationController;->createRemoteAnimationRecord(Lcom/android/server/wm/ActivityRecord;Landroid/graphics/Point;Landroid/graphics/Rect;Landroid/graphics/Rect;)Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationRecord;
HPLcom/android/server/wm/RemoteAnimationController;->linkToDeathOfRunner()V
HPLcom/android/server/wm/RemoteAnimationController;->writeStartDebugStatement()V
-HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
HPLcom/android/server/wm/TaskRecord;->setMinDimensions(Landroid/content/pm/ActivityInfo;)V
HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget;
HPLcom/android/server/wm/WindowStateAnimator;->finishDrawingLocked(Landroid/view/SurfaceControl$Transaction;)Z
@@ -21206,13 +21204,152 @@ HPLcom/android/server/statusbar/StatusBarManagerService;->setSystemUiVisibility(
HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->dump(Ljava/io/PrintWriter;Ljava/lang/String;)V
HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationRecord;-><init>(Lcom/android/server/wm/RemoteAnimationController;Lcom/android/server/wm/ActivityRecord;Landroid/graphics/Point;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
-HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
HPLcom/android/server/wm/WindowProcessController;->setBoundClientUids(Landroid/util/ArraySet;)V
HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onActivityLaunched$2$IorapForwardingService$AppLaunchObserver([BILcom/google/android/startop/iorap/IIorap;)V
HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onActivityLaunchFinished$4$IorapForwardingService$AppLaunchObserver([BJLcom/google/android/startop/iorap/IIorap;)V
HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onIntentStarted$0$IorapForwardingService$AppLaunchObserver(Landroid/content/Intent;JLcom/google/android/startop/iorap/IIorap;)V
HPLcom/android/server/statusbar/StatusBarManagerService;->updateUiVisibilityLocked(IIIIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)V
HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
-HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z
HPLcom/google/android/startop/iorap/IIorap$Stub$Proxy;->onAppLaunchEvent(Lcom/google/android/startop/iorap/RequestId;Lcom/google/android/startop/iorap/AppLaunchEvent;)V
HPLcom/google/android/startop/iorap/RequestId;->nextValueForSequence()Lcom/google/android/startop/iorap/RequestId;
+HPLcom/android/server/AlarmManagerService;->decrementAlarmCount(II)V
+HPLcom/android/server/am/ActivityManagerService$4;->newArray(I)[Landroid/content/IntentFilter;
+HPLcom/android/server/am/ActivityManagerService$PidMap;->remove(Lcom/android/server/am/ProcessRecord;)V
+HPLcom/android/server/am/ActivityManagerService;->updateOomAdjLocked(Ljava/lang/String;)V
+HPLcom/android/server/am/ActivityManagerShellCommand$1;-><init>(Lcom/android/server/am/ActivityManagerShellCommand;)V
+HPLcom/android/server/am/ActivityManagerShellCommand;-><init>(Lcom/android/server/am/ActivityManagerService;Z)V
+HPLcom/android/server/am/ActivityManagerShellCommand;->runStartActivity(Ljava/io/PrintWriter;)I
+HPLcom/android/server/am/HostingRecord;-><init>(Ljava/lang/String;Landroid/content/ComponentName;I)V
+HPLcom/android/server/am/PendingIntentController;->registerIntentSenderCancelListener(Landroid/content/IIntentSender;Lcom/android/internal/os/IResultReceiver;)V
+HPLcom/android/server/appprediction/-$$Lambda$AppPredictionManagerService$PredictionManagerServiceStub$vSY20eQq5y5FXrxhhqOTcEmezTs;-><init>(Landroid/app/prediction/AppPredictionSessionId;)V
+HPLcom/android/server/appprediction/-$$Lambda$RemoteAppPredictionService$9DCowUTEF8fYuBlWGxOmP5hTAWA;-><init>(Landroid/app/prediction/AppPredictionSessionId;)V
+HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$requestPredictionUpdate$6(Landroid/app/prediction/AppPredictionSessionId;Landroid/service/appprediction/IPredictionService;)V
+HPLcom/android/server/autofill/-$$Lambda$AutofillManagerService$1$1-WNu3tTkxodB_LsZ7dGIlvrPN0;->visit(Ljava/lang/Object;)V
+HPLcom/android/server/autofill/ui/-$$Lambda$AutoFillUI$56AC3ykfo4h_e2LSjdkJ3XQn370;-><init>(Lcom/android/server/autofill/ui/AutoFillUI;Lcom/android/server/autofill/ui/AutoFillUI$AutoFillUiCallback;)V
+HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$Enqw46SYVKFK9F2xX4qUcIu5_3I;->run(Landroid/os/IInterface;)V
+HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$eoGnQ2MDLLnW1UBX6wxNE1VBLAk;->run(Landroid/os/IInterface;)V
+HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$VKh1DoMPNSPjPfnVGdsInmxuqzc;->run(Landroid/os/IInterface;)V
+HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$yUTbcaYlZCYTmagCkNJ3i2VCkY4;->run(Landroid/os/IInterface;)V
+HPLcom/android/server/contentsuggestions/RemoteContentSuggestionsService;->getServiceInterface(Landroid/os/IBinder;)Landroid/os/IInterface;
+HPLcom/android/server/display/AutomaticBrightnessController;->updateAutoBrightness(ZZ)V
+HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->setModifier(I)V
+HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->setReason(I)V
+HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->toString(I)Ljava/lang/String;
+HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->toString()Ljava/lang/String;
+HPLcom/android/server/media/projection/MediaProjectionManagerService$1;->onProcessDied(II)V
+HPLcom/android/server/net/NetworkPolicyManagerService;->removeUidStateUL(I)Z
+HPLcom/android/server/pm/permission/PermissionManagerService;->addOnPermissionsChangeListener(Landroid/permission/IOnPermissionsChangeListener;)V
+HPLcom/android/server/protolog/ProtoLogImpl;->getSingleInstance()Lcom/android/server/protolog/ProtoLogImpl;
+HPLcom/android/server/soundtrigger/SoundTriggerHelper$PowerSaveModeListener;-><init>(Lcom/android/server/soundtrigger/SoundTriggerHelper;)V
+HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$uF0ibEnnXe7Lxunxb98QQLJjgZM;->run()V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->setSystemUiState(IIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->systemUiStateEquals(IIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)Z
+HPLcom/android/server/usage/UsageStatsDatabase;->filterStats(Lcom/android/server/usage/IntervalStats;)V
+HPLcom/android/server/VibratorService$VibrationInfo;-><init>(JLandroid/os/VibrationEffect;Landroid/os/VibrationEffect;Landroid/media/AudioAttributes;ILjava/lang/String;Ljava/lang/String;)V
+HPLcom/android/server/VibratorService;->getAppOpMode(Lcom/android/server/VibratorService$Vibration;)I
+HPLcom/android/server/VibratorService;->getCurrentIntensityLocked(Lcom/android/server/VibratorService$Vibration;)I
+HPLcom/android/server/VibratorService;->isAllowedToVibrateLocked(Lcom/android/server/VibratorService$Vibration;)Z
+HPLcom/android/server/VibratorService;->vibrate(ILjava/lang/String;Landroid/os/VibrationEffect;Landroid/media/AudioAttributes;Ljava/lang/String;Landroid/os/IBinder;)V
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$g49I60MBbnNkxHlgA-NR7ALwWTQ;->apply(Ljava/lang/Object;)Z
+HPLcom/android/server/wm/-$$Lambda$ActivityRecord$pBc6yUdDV5IrUd9vt6oCz6QzpiE;->accept(Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$BEx3OWenCvYAaV5h_J2ZkZXhEcY;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$Zbxkj4wIhcDki6VwBh1kWmSmxqM;-><init>(Lcom/android/server/wm/DisplayPolicy;IIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLcom/android/server/wm/WindowState;I[Lcom/android/internal/view/AppearanceRegion;ZZ)V
+HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$Zbxkj4wIhcDki6VwBh1kWmSmxqM;->run()V
+HPLcom/android/server/wm/-$$Lambda$TaskChangeNotificationController$UexNbaqPy0mc3VxTw2coCctHho8;->accept(Landroid/app/ITaskStackListener;Landroid/os/Message;)V
+HPLcom/android/server/wm/ActivityMetricsLogger;->allWindowsDrawn()Z
+HPLcom/android/server/wm/ActivityRecord;->createAnimationBoundsLayer(Landroid/view/SurfaceControl$Transaction;)Landroid/view/SurfaceControl;
+HPLcom/android/server/wm/ActivityRecord;->destroyed(Ljava/lang/String;)V
+HPLcom/android/server/wm/ActivityRecord;->forAllWindowsUnchecked(Lcom/android/internal/util/ToBooleanFunction;Z)Z
+HPLcom/android/server/wm/ActivityRecord;->getTopFullscreenWindow()Lcom/android/server/wm/WindowState;
+HPLcom/android/server/wm/ActivityRecord;->loadAnimation(Landroid/view/WindowManager$LayoutParams;IZZ)Landroid/view/animation/Animation;
+HPLcom/android/server/wm/ActivityRecord;->notifyAppStopped()V
+HPLcom/android/server/wm/ActivityRecord;->onFirstWindowDrawn(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowStateAnimator;)V
+HPLcom/android/server/wm/ActivityRecord;->onRemovedFromDisplay()V
+HPLcom/android/server/wm/ActivityRecord;->removeDeadWindows()V
+HPLcom/android/server/wm/ActivityRecord;->setClientHidden(Z)V
+HPLcom/android/server/wm/ActivityRecord;->stopFreezingScreenLocked(Z)V
+HPLcom/android/server/wm/ActivityStack;->removeTimeoutsForActivity(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/ActivityStarter;->recycleTask(Lcom/android/server/wm/TaskRecord;Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;[Lcom/android/server/wm/ActivityRecord;)I
+HPLcom/android/server/wm/ActivityTaskManagerService;->getTaskSnapshot(IZZ)Landroid/app/ActivityManager$TaskSnapshot;
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/DisplayContent;->getWindowCornerRadius()F
+HPLcom/android/server/wm/DisplayPolicy;->convertNonDecorInsetsToStableInsets(Landroid/graphics/Rect;I)V
+HPLcom/android/server/wm/DisplayPolicy;->getInsetsPolicy()Lcom/android/server/wm/InsetsPolicy;
+HPLcom/android/server/wm/DisplayPolicy;->updateLightStatusBarAppearanceLw(ILcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState;)I
+HPLcom/android/server/wm/InsetsPolicy;->getInsetsForDispatch(Lcom/android/server/wm/WindowState;)Landroid/view/InsetsState;
+HPLcom/android/server/wm/InsetsPolicy;->isHidden(I)Z
+HPLcom/android/server/wm/InsetsPolicy;->updateBarControlTarget(Lcom/android/server/wm/WindowState;)V
+HPLcom/android/server/wm/Task;->getTopFullscreenActivity()Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/TaskRecord;->removeTaskActivitiesLocked(Ljava/lang/String;)V
+HPLcom/android/server/wm/Task;->removeChild(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/TaskSnapshotCache$CacheEntry;-><init>(Landroid/app/ActivityManager$TaskSnapshot;Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/TaskSnapshotController;->createTaskSnapshot(Lcom/android/server/wm/Task;F)Landroid/view/SurfaceControl$ScreenshotGraphicBuffer;
+HPLcom/android/server/wm/TaskSnapshotController;->findAppTokenForSnapshot(Lcom/android/server/wm/Task;)Lcom/android/server/wm/ActivityRecord;
+HPLcom/android/server/wm/TaskSnapshotPersister$DeleteWriteQueueItem;-><init>(Lcom/android/server/wm/TaskSnapshotPersister;II)V
+HPLcom/android/server/wm/TaskSnapshotPersister$StoreWriteQueueItem;-><init>(Lcom/android/server/wm/TaskSnapshotPersister;IILandroid/app/ActivityManager$TaskSnapshot;)V
+HPLcom/android/server/wm/TaskSnapshotPersister$StoreWriteQueueItem;->onQueuedLocked()V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->lambda$startWallpaperAnimations$0(JJLjava/util/function/Consumer;Ljava/util/ArrayList;Ljava/util/ArrayList;Lcom/android/server/wm/WallpaperWindowToken;)V
+HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget;
+HPLcom/android/server/wm/WindowAnimationSpec;->findTranslateAnimation(Landroid/view/animation/Animation;)Landroid/view/animation/TranslateAnimation;
+HPLcom/android/server/wm/WindowProcessControllerMap;->removeProcessFromUidMap(Lcom/android/server/wm/WindowProcessController;)V
+HPLcom/android/server/wm/WindowProcessController;->updateProcessInfo(ZZZ)V
+HPLcom/android/server/wm/WindowState$DeathRecipient;-><init>(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState$1;)V
+HPLcom/android/server/wm/WindowState$WindowId;-><init>(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState$1;)V
+HPLcom/google/android/startop/iorap/AppLaunchEvent$ActivityLaunched;->writeToParcelImpl(Landroid/os/Parcel;I)V
+HPLcom/google/android/startop/iorap/AppLaunchEvent$ActivityLaunchFinished;->writeToParcelImpl(Landroid/os/Parcel;I)V
+HPLcom/google/android/startop/iorap/AppLaunchEvent$IntentStarted;->writeToParcelImpl(Landroid/os/Parcel;I)V
+HPLcom/google/android/startop/iorap/AppLaunchEvent;->getTypeIndex()I
+HPLcom/android/server/pm/PackageManagerService;->access$7600(Lcom/android/server/pm/PackageManagerService;Landroid/content/Intent;Ljava/lang/String;IIZI)Landroid/content/pm/ResolveInfo;
+HPLcom/android/server/pm/PackageManagerService;->access$8100(Lcom/android/server/pm/PackageManagerService;II)Z
+HPLcom/android/server/wm/ActivityRecord$Token;->access$100(Lcom/android/server/wm/ActivityRecord$Token;Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->access$000(Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;)Lcom/android/server/wm/SurfaceAnimator$OnAnimationFinishedCallback;
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->access$400(Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;)Landroid/graphics/Point;
+HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->access$500(Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;)Landroid/graphics/Rect;
+HPLcom/android/server/wm/RemoteAnimationController;->access$100(Lcom/android/server/wm/RemoteAnimationController;)V
+HPLcom/android/server/wm/RemoteAnimationController;->access$300(Lcom/android/server/wm/RemoteAnimationController;)Landroid/view/RemoteAnimationAdapter;
+HPLcom/android/server/wm/TaskPersister$TaskWriteQueueItem;->access$200(Lcom/android/server/wm/TaskPersister$TaskWriteQueueItem;)Lcom/android/server/wm/TaskRecord;
+HPLcom/android/server/wm/TaskPersister;->access$000(I)Ljava/io/File;
+HPLcom/android/server/wm/WindowContainer;->access$100(Lcom/android/server/wm/WindowContainer;)Landroid/util/Pools$SynchronizedPool;
+HPLcom/android/server/wm/WindowManagerService;->access$1600(Lcom/android/server/wm/WindowManagerService;Landroid/os/IBinder;)V
+HPLcom/android/server/pm/ShortcutUser;->logSharingShortcutStats(Lcom/android/internal/logging/MetricsLogger;)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->lambda$topAppWindowChanged$1$StatusBarManagerService(IZZ)V
+HPLcom/android/server/wm/ActivityRecord$Token;->attach(Lcom/android/server/wm/ActivityRecord;)V
+HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/DisplayPolicy;->lambda$updateSystemUiVisibilityLw$10$DisplayPolicy(IIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLcom/android/server/wm/WindowState;I[Lcom/android/internal/view/AppearanceRegion;ZZ)V
+HPLcom/android/server/wm/InsetsSourceProvider;->hasWindow()Z
+HPLcom/android/server/wm/InsetsStateController;->onBarControlTargetChanged(Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;)V
+HPLcom/android/server/wm/InsetsStateController;->peekSourceProvider(I)Lcom/android/server/wm/InsetsSourceProvider;
+HPLcom/android/server/wm/TaskPersister$TaskWriteQueueItem;->access$200(Lcom/android/server/wm/TaskPersister$TaskWriteQueueItem;)Lcom/android/server/wm/TaskRecord;
+HPLcom/android/server/wm/WindowContainer;->access$100(Lcom/android/server/wm/WindowContainer;)Landroid/util/Pools$SynchronizedPool;
+HPLcom/android/server/-$$Lambda$AlarmManagerService$2$Eo-D98J-N9R2METkD-12gPs320c;-><init>(Lcom/android/server/AlarmManagerService$2;Landroid/app/IAlarmCompleteListener;)V
+HPLcom/android/server/AlarmManagerService;->access$100(Lcom/android/server/AlarmManagerService;)Lcom/android/server/AlarmManagerService$Injector;
+HPLcom/android/server/AlarmManagerService;->access$2102(Lcom/android/server/AlarmManagerService;J)J
+HPLcom/android/server/AlarmManagerService;->access$2202(Lcom/android/server/AlarmManagerService;J)J
+HPLcom/android/server/AlarmManagerService;->access$2300(Lcom/android/server/AlarmManagerService;)V
+HPLcom/android/server/AlarmManagerService;->access$2400(Lcom/android/server/AlarmManagerService;Lcom/android/server/AlarmManagerService$Alarm;)Z
+HPLcom/android/server/AlarmManagerService;->isExemptFromAppStandby(Lcom/android/server/AlarmManagerService$Alarm;)Z
+HPLcom/android/server/appwidget/AppWidgetServiceImpl$HostId;-><init>(IILjava/lang/String;)V
+HPLcom/android/server/content/ContentService$ObserverCall;->run()V
+HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$uF0ibEnnXe7Lxunxb98QQLJjgZM;-><init>(Lcom/android/server/statusbar/StatusBarManagerService;IZZ)V
+HPLcom/android/server/statusbar/StatusBarManagerService$1;->topAppWindowChanged(IZZ)V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->access$1600(Lcom/android/server/statusbar/StatusBarManagerService$UiState;Z)V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->access$1700(Lcom/android/server/statusbar/StatusBarManagerService$UiState;Z)V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->setFullscreen(Z)V
+HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->setImmersive(Z)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->access$600(Lcom/android/server/statusbar/StatusBarManagerService;IZZ)V
+HPLcom/android/server/statusbar/StatusBarManagerService;->enforceStatusBar()V
+HPLcom/android/server/statusbar/StatusBarManagerService;->getUiState(I)Lcom/android/server/statusbar/StatusBarManagerService$UiState;
+HPLcom/android/server/statusbar/StatusBarManagerService;->topAppWindowChanged(IZZ)V
+HPLcom/android/server/wm/InsetsStateController;->onControlFakeTargetChanged(ILcom/android/server/wm/InsetsControlTarget;)V
+HPLcom/android/server/wm/LocalAnimationAdapter$AnimationSpec;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V
+HPLcom/android/server/wm/LocalAnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;)V
+HPLcom/android/server/wm/PersisterQueue;->access$100(Lcom/android/server/wm/PersisterQueue;)Ljava/util/ArrayList;
+HPLcom/android/server/wm/PersisterQueue;->access$200(Lcom/android/server/wm/PersisterQueue;)Ljava/util/ArrayList;
+HPLcom/android/server/wm/PersisterQueue;->access$300(Lcom/android/server/wm/PersisterQueue;)V
+HPLcom/android/server/wm/TaskSnapshotPersister;->access$100(Lcom/android/server/wm/TaskSnapshotPersister;)Ljava/lang/Object;
+HPLcom/android/server/wm/TaskSnapshotPersister;->access$200(Lcom/android/server/wm/TaskSnapshotPersister;)Z
+HPLcom/android/server/wm/TaskSnapshotPersister;->access$300(Lcom/android/server/wm/TaskSnapshotPersister;)Ljava/util/ArrayDeque;
+HPLcom/android/server/wm/TaskSnapshotPersister;->access$402(Lcom/android/server/wm/TaskSnapshotPersister;Z)Z
+HPLcom/android/server/wm/WindowAnimationSpec;->findTranslateAnimation(Landroid/view/animation/Animation;)Landroid/view/animation/TranslateAnimation;
+HPLcom/android/server/wm/WindowAnimationSpec;->writeToProtoInner(Landroid/util/proto/ProtoOutputStream;)V
+HPLcom/android/server/wm/WindowContainer;->access$100(Lcom/android/server/wm/WindowContainer;)Landroid/util/Pools$SynchronizedPool;
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 06d9395cd7d6..7828050223f4 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.ContentSuggestionsManager;
import android.app.contentsuggestions.IClassificationsCallback;
import android.app.contentsuggestions.ISelectionsCallback;
import android.app.contentsuggestions.SelectionsRequest;
@@ -97,15 +98,19 @@ public final class ContentSuggestionsPerUserService extends
void provideContextImageLocked(int taskId, @NonNull Bundle imageContextRequestExtras) {
RemoteContentSuggestionsService service = ensureRemoteServiceLocked();
if (service != null) {
- ActivityManager.TaskSnapshot snapshot =
- mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false);
GraphicBuffer snapshotBuffer = null;
int colorSpaceId = 0;
- if (snapshot != null) {
- snapshotBuffer = snapshot.getSnapshot();
- ColorSpace colorSpace = snapshot.getColorSpace();
- if (colorSpace != null) {
- colorSpaceId = colorSpace.getId();
+
+ // Skip taking TaskSnapshot when bitmap is provided.
+ if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
+ ActivityManager.TaskSnapshot snapshot =
+ mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false);
+ if (snapshot != null) {
+ snapshotBuffer = snapshot.getSnapshot();
+ ColorSpace colorSpace = snapshot.getColorSpace();
+ if (colorSpace != null) {
+ colorSpaceId = colorSpace.getId();
+ }
}
}
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index 024afe25f98e..6641b5be651d 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -23,6 +23,8 @@ import android.content.res.Configuration;
import android.os.UserHandle;
import android.os.UserManager;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
+
import java.util.List;
import java.util.Set;
@@ -153,35 +155,6 @@ public abstract class UsageStatsManagerInternal {
*/
public abstract int[] getIdleUidsForUser(@UserIdInt int userId);
- /**
- * Sets up a listener for changes to packages being accessed.
- * @param listener A listener within the system process.
- */
- public abstract void addAppIdleStateChangeListener(
- AppIdleStateChangeListener listener);
-
- /**
- * Removes a listener that was previously added for package usage state changes.
- * @param listener The listener within the system process to remove.
- */
- public abstract void removeAppIdleStateChangeListener(
- AppIdleStateChangeListener listener);
-
- public static abstract class AppIdleStateChangeListener {
-
- /** Callback to inform listeners that the idle state has changed to a new bucket. */
- public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
- boolean idle, int bucket, int reason);
-
- /**
- * Optional callback to inform the listener that the app has transitioned into
- * an active state due to user interaction.
- */
- public void onUserInteractionStarted(String packageName, @UserIdInt int userId) {
- // No-op by default
- }
- }
-
/** Backup/Restore API */
public abstract byte[] getBackupPayload(@UserIdInt int userId, String key);
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index b41e95fee15c..ff0044f6f1ad 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -99,6 +99,8 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.LocalLog;
import com.android.internal.util.StatLogger;
import com.android.server.AppStateTracker.Listener;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
@@ -1599,7 +1601,9 @@ class AlarmManagerService extends SystemService {
LocalServices.getService(DeviceIdleInternal.class);
mUsageStatsManagerInternal =
LocalServices.getService(UsageStatsManagerInternal.class);
- mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker());
+ AppStandbyInternal appStandbyInternal =
+ LocalServices.getService(AppStandbyInternal.class);
+ appStandbyInternal.addListener(new AppStandbyTracker());
mAppStateTracker = LocalServices.getService(AppStateTracker.class);
mAppStateTracker.addListener(mForceAppStandbyListener);
@@ -4468,7 +4472,7 @@ class AlarmManagerService extends SystemService {
* Tracking of app assignments to standby buckets
*/
private final class AppStandbyTracker extends
- UsageStatsManagerInternal.AppIdleStateChangeListener {
+ AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
boolean idle, int bucket, int reason) {
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index da760b6f7ffd..5eff2c5c1d37 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -24,7 +24,6 @@ import android.app.IActivityManager;
import android.app.IUidObserver;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -58,6 +57,8 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.StatLogger;
import com.android.server.AppStateTrackerProto.ExemptedPackage;
import com.android.server.AppStateTrackerProto.RunAnyInBackgroundRestrictedPackages;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -89,7 +90,7 @@ public class AppStateTracker {
IAppOpsService mAppOpsService;
PowerManagerInternal mPowerManagerInternal;
StandbyTracker mStandbyTracker;
- UsageStatsManagerInternal mUsageStatsManagerInternal;
+ AppStandbyInternal mAppStandbyInternal;
private final MyHandler mHandler;
@@ -420,8 +421,7 @@ public class AppStateTracker {
mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
- mUsageStatsManagerInternal = Preconditions.checkNotNull(
- injectUsageStatsManagerInternal());
+ mAppStandbyInternal = Preconditions.checkNotNull(injectAppStandbyInternal());
mFlagsObserver = new FeatureFlagsObserver();
mFlagsObserver.register();
@@ -429,7 +429,7 @@ public class AppStateTracker {
mForceAllAppStandbyForSmallBattery =
mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
mStandbyTracker = new StandbyTracker();
- mUsageStatsManagerInternal.addAppIdleStateChangeListener(mStandbyTracker);
+ mAppStandbyInternal.addListener(mStandbyTracker);
try {
mIActivityManager.registerUidObserver(new UidObserver(),
@@ -494,8 +494,8 @@ public class AppStateTracker {
}
@VisibleForTesting
- UsageStatsManagerInternal injectUsageStatsManagerInternal() {
- return LocalServices.getService(UsageStatsManagerInternal.class);
+ AppStandbyInternal injectAppStandbyInternal() {
+ return LocalServices.getService(AppStandbyInternal.class);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0bb72cb9d554..ce0e9e7e56cf 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -26,6 +26,7 @@ import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
+import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
@@ -528,6 +529,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int EVENT_SET_ACCEPT_PARTIAL_CONNECTIVITY = 45;
/**
+ * Event for NetworkMonitor to inform ConnectivityService that the probe status has changed.
+ * Both of the arguments are bitmasks, and the value of bits come from
+ * INetworkMonitor.NETWORK_VALIDATION_PROBE_*.
+ * arg1 = A bitmask to describe which probes are completed.
+ * arg2 = A bitmask to describe which probes are successful.
+ */
+ public static final int EVENT_PROBE_STATUS_CHANGED = 46;
+
+ /**
* Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
* should be shown.
*/
@@ -2663,6 +2673,41 @@ public class ConnectivityService extends IConnectivityManager.Stub
switch (msg.what) {
default:
return false;
+ case EVENT_PROBE_STATUS_CHANGED: {
+ final Integer netId = (Integer) msg.obj;
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
+ if (nai == null) {
+ break;
+ }
+ final boolean probePrivateDnsCompleted =
+ ((msg.arg1 & NETWORK_VALIDATION_PROBE_PRIVDNS) != 0);
+ final boolean privateDnsBroken =
+ ((msg.arg2 & NETWORK_VALIDATION_PROBE_PRIVDNS) == 0);
+ if (probePrivateDnsCompleted) {
+ if (nai.networkCapabilities.isPrivateDnsBroken() != privateDnsBroken) {
+ nai.networkCapabilities.setPrivateDnsBroken(privateDnsBroken);
+ final int oldScore = nai.getCurrentScore();
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ }
+ // Only show the notification when the private DNS is broken and the
+ // PRIVATE_DNS_BROKEN notification hasn't shown since last valid.
+ if (privateDnsBroken && !nai.networkMisc.hasShownBroken) {
+ showNetworkNotification(nai, NotificationType.PRIVATE_DNS_BROKEN);
+ }
+ nai.networkMisc.hasShownBroken = privateDnsBroken;
+ } else if (nai.networkCapabilities.isPrivateDnsBroken()) {
+ // If probePrivateDnsCompleted is false but nai.networkCapabilities says
+ // private DNS is broken, it means this network is being reevaluated.
+ // Either probing private DNS is not necessary any more or it hasn't been
+ // done yet. In either case, the networkCapabilities should be updated to
+ // reflect the new status.
+ nai.networkCapabilities.setPrivateDnsBroken(false);
+ final int oldScore = nai.getCurrentScore();
+ updateCapabilities(oldScore, nai, nai.networkCapabilities);
+ nai.networkMisc.hasShownBroken = false;
+ }
+ break;
+ }
case EVENT_NETWORK_TESTED: {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;
@@ -2705,14 +2750,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
if (valid) {
handleFreshlyValidatedNetwork(nai);
- // Clear NO_INTERNET, PARTIAL_CONNECTIVITY and LOST_INTERNET
- // notifications if network becomes valid.
+ // Clear NO_INTERNET, PRIVATE_DNS_BROKEN, PARTIAL_CONNECTIVITY and
+ // LOST_INTERNET notifications if network becomes valid.
mNotifier.clearNotification(nai.network.netId,
NotificationType.NO_INTERNET);
mNotifier.clearNotification(nai.network.netId,
NotificationType.LOST_INTERNET);
mNotifier.clearNotification(nai.network.netId,
NotificationType.PARTIAL_CONNECTIVITY);
+ mNotifier.clearNotification(nai.network.netId,
+ NotificationType.PRIVATE_DNS_BROKEN);
+ // If network becomes valid, the hasShownBroken should be reset for
+ // that network so that the notification will be fired when the private
+ // DNS is broken again.
+ nai.networkMisc.hasShownBroken = false;
}
} else if (partialConnectivityChanged) {
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -2863,6 +2914,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
+ public void notifyProbeStatusChanged(int probesCompleted, int probesSucceeded) {
+ mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
+ EVENT_PROBE_STATUS_CHANGED,
+ probesCompleted, probesSucceeded, new Integer(mNetId)));
+ }
+
+ @Override
public void showProvisioningNotification(String action, String packageName) {
final Intent intent = new Intent(action);
intent.setPackage(packageName);
@@ -3679,6 +3737,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
// High priority because it is only displayed for explicitly selected networks.
highPriority = true;
break;
+ case PRIVATE_DNS_BROKEN:
+ action = Settings.ACTION_WIRELESS_SETTINGS;
+ // High priority because we should let user know why there is no internet.
+ highPriority = true;
+ break;
case LOST_INTERNET:
action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION;
// High priority because it could help the user avoid unexpected data usage.
@@ -3696,7 +3759,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
Intent intent = new Intent(action);
- if (type != NotificationType.LOGGED_IN) {
+ if (type != NotificationType.LOGGED_IN && type != NotificationType.PRIVATE_DNS_BROKEN) {
intent.setData(Uri.fromParts("netId", Integer.toString(nai.network.netId), null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName("com.android.settings",
@@ -5162,6 +5225,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
ns.assertValidFromUid(Binder.getCallingUid());
}
+ private void ensureValid(NetworkCapabilities nc) {
+ ensureValidNetworkSpecifier(nc);
+ if (nc.isPrivateDnsBroken()) {
+ throw new IllegalArgumentException("Can't request broken private DNS");
+ }
+ }
+
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
@@ -5195,7 +5265,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (timeoutMs < 0) {
throw new IllegalArgumentException("Bad timeout specified");
}
- ensureValidNetworkSpecifier(networkCapabilities);
+ ensureValid(networkCapabilities);
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId(), type);
@@ -5337,7 +5407,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
// can't request networks.
restrictBackgroundRequestForCaller(nc);
- ensureValidNetworkSpecifier(nc);
+ ensureValid(nc);
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
@@ -5355,7 +5425,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
}
- ensureValidNetworkSpecifier(networkCapabilities);
+ ensureValid(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
@@ -5841,6 +5911,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
newNc.removeCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
}
+ newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken());
return newNc;
}
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 18009e191482..190e6cf2d35c 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -115,7 +115,8 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements
}
@Override
- public boolean startInstallation(long systemSize, long userdataSize) throws RemoteException {
+ public boolean startInstallation(String name, long size, boolean readOnly)
+ throws RemoteException {
// priority from high to low: sysprop -> sdcard -> /data
String path = SystemProperties.get("os.aot.path");
if (path.isEmpty()) {
@@ -137,11 +138,18 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements
}
Slog.i(TAG, "startInstallation -> " + path);
}
+ IGsiService service = getGsiService();
GsiInstallParams installParams = new GsiInstallParams();
installParams.installDir = path;
- installParams.gsiSize = systemSize;
- installParams.userdataSize = userdataSize;
- return getGsiService().beginGsiInstall(installParams) == 0;
+ installParams.name = name;
+ installParams.size = size;
+ installParams.wipe = readOnly;
+ installParams.readOnly = readOnly;
+ if (service.beginGsiInstall(installParams) != 0) {
+ Slog.i(TAG, "Failed to install " + name);
+ return false;
+ }
+ return true;
}
@Override
diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/GnssManagerService.java
index 44a823475b7e..274e2f1b53f8 100644
--- a/services/core/java/com/android/server/GnssManagerService.java
+++ b/services/core/java/com/android/server/GnssManagerService.java
@@ -18,6 +18,7 @@ package com.android.server;
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
@@ -300,7 +301,7 @@ public class GnssManagerService {
* @return true if callback is successfully added, false otherwise
*/
public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
- @NonNull String listenerIdentity) {
+ @Nullable String featureId, @NonNull String listenerIdentity) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
@@ -319,7 +320,7 @@ public class GnssManagerService {
CallerIdentity callerIdentity =
new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
- listenerIdentity);
+ featureId, listenerIdentity);
synchronized (mGnssBatchingLock) {
mGnssBatchingCallback = callback;
mGnssBatchingDeathCallback =
@@ -497,6 +498,7 @@ public class GnssManagerService {
private <TListener extends IInterface> boolean addGnssDataListenerLocked(
TListener listener,
String packageName,
+ @Nullable String featureId,
@NonNull String listenerIdentifier,
RemoteListenerHelper<TListener> gnssDataProvider,
ArrayMap<IBinder,
@@ -517,7 +519,7 @@ public class GnssManagerService {
CallerIdentity callerIdentity =
new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
- listenerIdentifier);
+ featureId, listenerIdentifier);
LinkedListener<TListener> linkedListener =
new LocationManagerServiceUtils.LinkedListener<>(
listener, listenerIdentifier, callerIdentity, binderDeathCallback);
@@ -605,11 +607,13 @@ public class GnssManagerService {
* @param packageName name of requesting package
* @return true if listener is successfully registered, false otherwise
*/
- public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) {
+ public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
+ @Nullable String featureId) {
synchronized (mGnssStatusListeners) {
return addGnssDataListenerLocked(
listener,
packageName,
+ featureId,
"Gnss status",
mGnssStatusProvider,
mGnssStatusListeners,
@@ -636,12 +640,13 @@ public class GnssManagerService {
* @return true if listener is successfully added, false otherwise
*/
public boolean addGnssMeasurementsListener(
- IGnssMeasurementsListener listener, String packageName,
+ IGnssMeasurementsListener listener, String packageName, @Nullable String featureId,
@NonNull String listenerIdentifier) {
synchronized (mGnssMeasurementsListeners) {
return addGnssDataListenerLocked(
listener,
packageName,
+ featureId,
listenerIdentifier,
mGnssMeasurementsProvider,
mGnssMeasurementsListeners,
@@ -695,11 +700,12 @@ public class GnssManagerService {
*/
public boolean addGnssNavigationMessageListener(
IGnssNavigationMessageListener listener, String packageName,
- @NonNull String listenerIdentifier) {
+ @Nullable String featureId, @NonNull String listenerIdentifier) {
synchronized (mGnssNavigationMessageListeners) {
return addGnssDataListenerLocked(
listener,
packageName,
+ featureId,
listenerIdentifier,
mGnssNavigationMessageProvider,
mGnssNavigationMessageListeners,
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 1d22b824aff9..0a63bf89b21f 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1000,6 +1000,12 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void onReportLocation(Location location) {
+ // likelihood of a 0,0 bug is far greater than this being a valid location
+ if (!isMock() && location.getLatitude() == 0 && location.getLongitude() == 0) {
+ Slog.w(TAG, "blocking 0,0 location from " + mName + " provider");
+ return;
+ }
+
synchronized (mLock) {
handleLocationChangedLocked(location, this);
}
@@ -1227,9 +1233,9 @@ public class LocationManagerService extends ILocationManager.Stub {
PowerManager.WakeLock mWakeLock;
private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
- String packageName, WorkSource workSource, boolean hideFromAppOps,
- @NonNull String listenerIdentifier) {
- super(new CallerIdentity(uid, pid, packageName, listenerIdentifier),
+ String packageName, @Nullable String featureId, WorkSource workSource,
+ boolean hideFromAppOps, @NonNull String listenerIdentifier) {
+ super(new CallerIdentity(uid, pid, packageName, featureId, listenerIdentifier),
"LocationListener");
mListener = listener;
mPendingIntent = intent;
@@ -1354,7 +1360,8 @@ public class LocationManagerService extends ILocationManager.Stub {
if (!currentlyMonitoring) {
if (allowMonitoring) {
return mAppOps.startOpNoThrow(op, mCallerIdentity.mUid,
- mCallerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
+ mCallerIdentity.mPackageName, false, mCallerIdentity.mFeatureId, null)
+ == AppOpsManager.MODE_ALLOWED;
}
} else {
if (!allowMonitoring
@@ -1539,11 +1546,11 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
- String listenerIdentifier) {
+ String featureId, String listenerIdentifier) {
Preconditions.checkNotNull(listenerIdentifier);
return mGnssManagerService == null ? false : mGnssManagerService.addGnssBatchingCallback(
- callback, packageName, listenerIdentifier);
+ callback, packageName, featureId, listenerIdentifier);
}
@Override
@@ -1706,10 +1713,10 @@ public class LocationManagerService extends ILocationManager.Stub {
}
private boolean reportLocationAccessNoThrow(int pid, int uid, String packageName,
- int allowedResolutionLevel, @Nullable String message) {
+ @Nullable String featureId, int allowedResolutionLevel, @Nullable String message) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
- if (mAppOps.noteOpNoThrow(op, uid, packageName, null, message)
+ if (mAppOps.noteOpNoThrow(op, uid, packageName, featureId, message)
!= AppOpsManager.MODE_ALLOWED) {
return false;
}
@@ -2143,12 +2150,12 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("mLock")
private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
- String packageName, WorkSource workSource, boolean hideFromAppOps,
- @NonNull String listenerIdentifier) {
+ String packageName, @Nullable String featureId, WorkSource workSource,
+ boolean hideFromAppOps, @NonNull String listenerIdentifier) {
IBinder binder = listener.asBinder();
Receiver receiver = mReceivers.get(binder);
if (receiver == null) {
- receiver = new Receiver(listener, null, pid, uid, packageName, workSource,
+ receiver = new Receiver(listener, null, pid, uid, packageName, featureId, workSource,
hideFromAppOps, listenerIdentifier);
if (!receiver.linkToListenerDeathNotificationLocked(
receiver.getListener().asBinder())) {
@@ -2161,10 +2168,11 @@ public class LocationManagerService extends ILocationManager.Stub {
@GuardedBy("mLock")
private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
- WorkSource workSource, boolean hideFromAppOps, @NonNull String listenerIdentifier) {
+ @Nullable String featureId, WorkSource workSource, boolean hideFromAppOps,
+ @NonNull String listenerIdentifier) {
Receiver receiver = mReceivers.get(intent);
if (receiver == null) {
- receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
+ receiver = new Receiver(null, intent, pid, uid, packageName, featureId, workSource,
hideFromAppOps, listenerIdentifier);
mReceivers.put(intent, receiver);
}
@@ -2227,7 +2235,8 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
- PendingIntent intent, String packageName, String listenerIdentifier) {
+ PendingIntent intent, String packageName, String featureId,
+ String listenerIdentifier) {
Preconditions.checkNotNull(listenerIdentifier);
synchronized (mLock) {
@@ -2283,11 +2292,11 @@ public class LocationManagerService extends ILocationManager.Stub {
Receiver receiver;
if (intent != null) {
- receiver = getReceiverLocked(intent, pid, uid, packageName, workSource,
- hideFromAppOps, listenerIdentifier);
+ receiver = getReceiverLocked(intent, pid, uid, packageName, featureId,
+ workSource, hideFromAppOps, listenerIdentifier);
} else {
- receiver = getReceiverLocked(listener, pid, uid, packageName, workSource,
- hideFromAppOps, listenerIdentifier);
+ receiver = getReceiverLocked(listener, pid, uid, packageName, featureId,
+ workSource, hideFromAppOps, listenerIdentifier);
}
requestLocationUpdatesLocked(sanitizedRequest, receiver, uid, packageName);
} finally {
@@ -2356,9 +2365,10 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
Receiver receiver;
if (intent != null) {
- receiver = getReceiverLocked(intent, pid, uid, packageName, null, false, "");
+ receiver = getReceiverLocked(intent, pid, uid, packageName, null, null, false, "");
} else {
- receiver = getReceiverLocked(listener, pid, uid, packageName, null, false, "");
+ receiver = getReceiverLocked(listener, pid, uid, packageName, null, null, false,
+ "");
}
long identity = Binder.clearCallingIdentity();
@@ -2402,7 +2412,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public Location getLastLocation(LocationRequest r, String packageName) {
+ public Location getLastLocation(LocationRequest r, String packageName, String featureId) {
synchronized (mLock) {
LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
@@ -2477,8 +2487,8 @@ public class LocationManagerService extends ILocationManager.Stub {
}
// Don't report location access if there is no last location to deliver.
if (lastLocation != null) {
- if (!reportLocationAccessNoThrow(pid, uid, packageName, allowedResolutionLevel,
- null)) {
+ if (!reportLocationAccessNoThrow(pid, uid, packageName, featureId,
+ allowedResolutionLevel, null)) {
if (D) {
Log.d(TAG, "not returning last loc for no op app: " + packageName);
}
@@ -2495,9 +2505,9 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean getCurrentLocation(LocationRequest locationRequest,
ICancellationSignal remoteCancellationSignal, ILocationListener listener,
- String packageName, String listenerIdentifier) {
+ String packageName, String featureId, String listenerIdentifier) {
// side effect of validating locationRequest and packageName
- Location lastLocation = getLastLocation(locationRequest, packageName);
+ Location lastLocation = getLastLocation(locationRequest, packageName, featureId);
if (lastLocation != null) {
long locationAgeMs = TimeUnit.NANOSECONDS.toMillis(
SystemClock.elapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos());
@@ -2532,7 +2542,8 @@ public class LocationManagerService extends ILocationManager.Stub {
}
}
- requestLocationUpdates(locationRequest, listener, null, packageName, listenerIdentifier);
+ requestLocationUpdates(locationRequest, listener, null, packageName, featureId,
+ listenerIdentifier);
CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
remoteCancellationSignal);
if (cancellationSignal != null) {
@@ -2582,7 +2593,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
- String packageName, String listenerIdentifier) {
+ String packageName, String featureId, String listenerIdentifier) {
Preconditions.checkNotNull(listenerIdentifier);
if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2630,7 +2641,7 @@ public class LocationManagerService extends ILocationManager.Stub {
}
mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
- uid, packageName, listenerIdentifier);
+ uid, packageName, featureId, listenerIdentifier);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2666,9 +2677,10 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) {
+ public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName,
+ String featureId) {
return mGnssManagerService == null ? false : mGnssManagerService.registerGnssStatusCallback(
- listener, packageName);
+ listener, packageName, featureId);
}
@Override
@@ -2678,12 +2690,12 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean addGnssMeasurementsListener(IGnssMeasurementsListener listener,
- String packageName, String listenerIdentifier) {
+ String packageName, String featureId, String listenerIdentifier) {
Preconditions.checkNotNull(listenerIdentifier);
return mGnssManagerService == null ? false
- : mGnssManagerService.addGnssMeasurementsListener(listener, packageName,
- listenerIdentifier);
+ : mGnssManagerService.addGnssMeasurementsListener(listener, packageName, featureId,
+ listenerIdentifier);
}
@Override
@@ -2711,12 +2723,12 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
- String packageName, String listenerIdentifier) {
+ String packageName, String featureId, String listenerIdentifier) {
Preconditions.checkNotNull(listenerIdentifier);
return mGnssManagerService == null ? false
: mGnssManagerService.addGnssNavigationMessageListener(listener, packageName,
- listenerIdentifier);
+ featureId, listenerIdentifier);
}
@Override
@@ -3022,6 +3034,7 @@ public class LocationManagerService extends ILocationManager.Stub {
receiver.mCallerIdentity.mPid,
receiver.mCallerIdentity.mUid,
receiver.mCallerIdentity.mPackageName,
+ receiver.mCallerIdentity.mFeatureId,
receiver.mAllowedResolutionLevel,
"Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) {
if (D) {
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 54504c3c1e24..3ba2210504d6 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -508,7 +508,7 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
}
if (key.requestIntent != null) {
pw.print(prefix); pw.print("requestIntent=");
- pw.println(key.requestIntent.toShortString(false, true, true, true));
+ pw.println(key.requestIntent.toShortString(false, true, true, false));
}
if (sent || canceled) {
pw.print(prefix); pw.print("sent="); pw.print(sent);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index cc4b160332e5..5106b0e435d7 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -256,7 +256,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
}
if (intent != null) {
intent.getIntent().writeToProto(proto, ServiceRecordProto.INTENT, false, true, false,
- true);
+ false);
}
proto.write(ServiceRecordProto.PACKAGE_NAME, packageName);
proto.write(ServiceRecordProto.PROCESS_NAME, processName);
@@ -358,7 +358,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("intent={");
- pw.print(intent.getIntent().toShortString(false, true, false, true));
+ pw.print(intent.getIntent().toShortString(false, true, false, false));
pw.println('}');
pw.print(prefix); pw.print("packageName="); pw.println(packageName);
pw.print(prefix); pw.print("processName="); pw.println(processName);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 175419117898..0ea913f340b0 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -560,8 +560,8 @@ class UserController implements Handler.Callback {
Slog.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
// Pre-created user was started right after creation so services could properly
// intialize it; it should be stopped right away as it's not really a "real" user.
- // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
- // on SystemService instead.
+ // TODO(b/143092698): in the long-term, it might be better to add a onCreateUser()
+ // callback on SystemService instead.
stopUser(userInfo.id, /* force= */ true, /* stopUserCallback= */ null,
/* keyEvictedCallback= */ null);
return;
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 087c84f1a42f..67d358910de1 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -23,7 +23,6 @@ import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNO
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.ActivityManager;
import android.attention.AttentionManagerInternal;
import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
import android.content.BroadcastReceiver;
@@ -300,7 +299,8 @@ public class AttentionManagerService extends SystemService {
@GuardedBy("mLock")
@VisibleForTesting
protected UserState getOrCreateCurrentUserStateLocked() {
- return getOrCreateUserStateLocked(ActivityManager.getCurrentUser());
+ // Doesn't need to cache the states of different users.
+ return getOrCreateUserStateLocked(0);
}
@GuardedBy("mLock")
@@ -318,7 +318,8 @@ public class AttentionManagerService extends SystemService {
@Nullable
@VisibleForTesting
protected UserState peekCurrentUserStateLocked() {
- return peekUserStateLocked(ActivityManager.getCurrentUser());
+ // Doesn't need to cache the states of different users.
+ return peekUserStateLocked(0);
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 6010b1dc88c4..f7ac04041ed6 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -57,13 +57,6 @@ import java.io.PrintWriter;
private final @NonNull AudioService mAudioService;
private final @NonNull Context mContext;
- /** Forced device usage for communications sent to AudioSystem */
- private int mForcedUseForComm;
- /**
- * Externally reported force device usage state returned by getters: always consistent
- * with requests by setters */
- private int mForcedUseForCommExt;
-
// Manages all connected devices, only ever accessed on the message loop
private final AudioDeviceInventory mDeviceInventory;
// Manages notifications to BT service
@@ -71,24 +64,34 @@ import java.io.PrintWriter;
//-------------------------------------------------------------------
- // we use a different lock than mDeviceStateLock so as not to create
- // lock contention between enqueueing a message and handling them
- private static final Object sLastDeviceConnectionMsgTimeLock = new Object();
- @GuardedBy("sLastDeviceConnectionMsgTimeLock")
+ /**
+ * Lock to guard:
+ * - any changes to the message queue: enqueueing or removing any message
+ * - state of A2DP enabled
+ * - force use for communication + SCO changes
+ */
+ private final Object mDeviceBrokerLock = new Object();
+
+ @GuardedBy("mDeviceBrokerLock")
private static long sLastDeviceConnectMsgTime = 0;
- // General lock to be taken whenever the state of the audio devices is to be checked or changed
- private final Object mDeviceStateLock = new Object();
- // Request to override default use of A2DP for media.
- @GuardedBy("mDeviceStateLock")
+ /** Request to override default use of A2DP for media */
+ @GuardedBy("mDeviceBrokerLock")
private boolean mBluetoothA2dpEnabled;
- // lock always taken when accessing AudioService.mSetModeDeathHandlers
- // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055
- /*package*/ final Object mSetModeLock = new Object();
+ /** Forced device usage for communications sent to AudioSystem */
+ @GuardedBy("mDeviceBrokerLock")
+ private int mForcedUseForComm;
+ /**
+ * Externally reported force device usage state returned by getters: always consistent
+ * with requests by setters */
+ @GuardedBy("mDeviceBrokerLock")
+ private int mForcedUseForCommExt;
+
//-------------------------------------------------------------------
+ /** Normal constructor used by AudioService */
/*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) {
mContext = context;
mAudioService = service;
@@ -127,38 +130,37 @@ import java.io.PrintWriter;
// All post* methods are asynchronous
/*package*/ void onSystemReady() {
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.onSystemReady();
- }
- }
+ mBtHelper.onSystemReady();
}
/*package*/ void onAudioServerDied() {
// Restore forced usage for communications and record
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
AudioSystem.setParameters(
"BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off"));
onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied");
onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied");
+
+ // restore devices
+ sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
- // restore devices
- sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
/*package*/ void setForceUse_Async(int useCase, int config, String eventSource) {
- sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
- useCase, config, eventSource);
+ synchronized (mDeviceBrokerLock) {
+ sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
+ useCase, config, eventSource);
+ }
}
/*package*/ void toggleHdmiIfConnected_Async() {
- sendMsgNoDelay(MSG_TOGGLE_HDMI, SENDMSG_QUEUE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_TOGGLE_HDMI, SENDMSG_QUEUE);
+ }
}
/*package*/ void disconnectAllBluetoothProfiles() {
- synchronized (mDeviceStateLock) {
mBtHelper.disconnectAllBluetoothProfiles();
- }
}
/**
@@ -168,15 +170,11 @@ import java.io.PrintWriter;
* @param intent
*/
/*package*/ void receiveBtEvent(@NonNull Intent intent) {
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.receiveBtEvent(intent);
- }
- }
+ mBtHelper.receiveBtEvent(intent);
}
/*package*/ void setBluetoothA2dpOn_Async(boolean on, String source) {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
if (mBluetoothA2dpEnabled == on) {
return;
}
@@ -196,7 +194,7 @@ import java.io.PrintWriter;
* @return true if speakerphone state changed
*/
/*package*/ boolean setSpeakerphoneOn(boolean on, String eventSource) {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
final boolean wasOn = isSpeakerphoneOn();
if (on) {
if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
@@ -214,7 +212,7 @@ import java.io.PrintWriter;
}
/*package*/ boolean isSpeakerphoneOn() {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
}
}
@@ -223,9 +221,7 @@ import java.io.PrintWriter;
@AudioService.ConnectionState int state, String address, String name,
String caller) {
//TODO move logging here just like in setBluetooth* methods
- synchronized (mDeviceStateLock) {
- mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
- }
+ mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
}
private static final class BtDeviceConnectionInfo {
@@ -259,27 +255,24 @@ import java.io.PrintWriter;
final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile,
suppressNoisyIntent, a2dpVolume);
- // when receiving a request to change the connection state of a device, this last request
- // is the source of truth, so cancel all previous requests
- removeAllA2dpConnectionEvents(device);
-
- sendLMsgNoDelay(
- state == BluetoothProfile.STATE_CONNECTED
- ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
- : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
- SENDMSG_QUEUE, info);
- }
-
- /** remove all previously scheduled connection and disconnection events for the given device */
- private void removeAllA2dpConnectionEvents(@NonNull BluetoothDevice device) {
- mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
- device);
- mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION,
- device);
- mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
- device);
- mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- device);
+ synchronized (mDeviceBrokerLock) {
+ // when receiving a request to change the connection state of a device, this last
+ // request is the source of truth, so cancel all previous requests
+ mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
+ device);
+ mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION,
+ device);
+ mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
+ device);
+ mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ device);
+
+ sendLMsgNoDelay(
+ state == BluetoothProfile.STATE_CONNECTED
+ ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
+ : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
+ SENDMSG_QUEUE, info);
+ }
}
private static final class HearingAidDeviceConnectionInfo {
@@ -305,28 +298,31 @@ import java.io.PrintWriter;
boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo(
device, state, suppressNoisyIntent, musicDevice, eventSource);
- sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
+ }
}
// never called by system components
/*package*/ void setBluetoothScoOnByApp(boolean on) {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
}
}
/*package*/ boolean isBluetoothScoOnForApp() {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
}
}
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
//Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
- synchronized (mDeviceStateLock) {
+ final boolean isBtScoOn = mBtHelper.isBluetoothScoOn();
+ synchronized (mDeviceBrokerLock) {
if (on) {
// do not accept SCO ON if SCO audio is not connected
- if (!mBtHelper.isBluetoothScoOn()) {
+ if (!isBtScoOn) {
mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
return;
}
@@ -346,58 +342,55 @@ import java.io.PrintWriter;
}
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
- synchronized (mDeviceStateLock) {
- return mDeviceInventory.startWatchingRoutes(observer);
- }
+ return mDeviceInventory.startWatchingRoutes(observer);
+
}
/*package*/ AudioRoutesInfo getCurAudioRoutes() {
- synchronized (mDeviceStateLock) {
- return mDeviceInventory.getCurAudioRoutes();
- }
+ return mDeviceInventory.getCurAudioRoutes();
}
/*package*/ boolean isAvrcpAbsoluteVolumeSupported() {
- synchronized (mDeviceStateLock) {
- return mBtHelper.isAvrcpAbsoluteVolumeSupported();
- }
+ return mBtHelper.isAvrcpAbsoluteVolumeSupported();
}
/*package*/ boolean isBluetoothA2dpOn() {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
return mBluetoothA2dpEnabled;
}
}
/*package*/ void postSetAvrcpAbsoluteVolumeIndex(int index) {
- sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index);
+ synchronized (mDeviceBrokerLock) {
+ sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index);
+ }
}
/*package*/ void postSetHearingAidVolumeIndex(int index, int streamType) {
- sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
+ synchronized (mDeviceBrokerLock) {
+ sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
+ }
}
/*package*/ void postDisconnectBluetoothSco(int exceptPid) {
- sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid);
+ synchronized (mDeviceBrokerLock) {
+ sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid);
+ }
}
/*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
- sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
+ }
}
- @GuardedBy("mSetModeLock")
/*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
@NonNull String eventSource) {
- synchronized (mDeviceStateLock) {
- mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
- }
+ mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
}
- @GuardedBy("mSetModeLock")
/*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
- synchronized (mDeviceStateLock) {
- mBtHelper.stopBluetoothScoForClient(cb, eventSource);
- }
+ mBtHelper.stopBluetoothScoForClient(cb, eventSource);
}
//---------------------------------------------------------------------
@@ -460,77 +453,109 @@ import java.io.PrintWriter;
//---------------------------------------------------------------------
// Message handling on behalf of helper classes
/*package*/ void postBroadcastScoConnectionState(int state) {
- sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
+ synchronized (mDeviceBrokerLock) {
+ sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
+ }
}
/*package*/ void postBroadcastBecomingNoisy() {
- sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
+ }
}
/*package*/ void postA2dpSinkConnection(@AudioService.BtProfileConnectionState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
- sendILMsg(state == BluetoothA2dp.STATE_CONNECTED
- ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED
- : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- SENDMSG_QUEUE,
- state, btDeviceInfo, delay);
+ synchronized (mDeviceBrokerLock) {
+ sendILMsg(state == BluetoothA2dp.STATE_CONNECTED
+ ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED
+ : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ SENDMSG_QUEUE,
+ state, btDeviceInfo, delay);
+ }
}
/*package*/ void postA2dpSourceConnection(@AudioService.BtProfileConnectionState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
- sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
- state, btDeviceInfo, delay);
+ synchronized (mDeviceBrokerLock) {
+ sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
+ state, btDeviceInfo, delay);
+ }
}
/*package*/ void postSetWiredDeviceConnectionState(
AudioDeviceInventory.WiredDeviceConnectionState connectionState, int delay) {
- sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE,
+ connectionState, delay);
+ }
}
/*package*/ void postSetHearingAidConnectionState(
@AudioService.BtProfileConnectionState int state,
@NonNull BluetoothDevice device, int delay) {
- sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE,
- state,
- device,
- delay);
+ synchronized (mDeviceBrokerLock) {
+ sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE,
+ state,
+ device,
+ delay);
+ }
}
/*package*/ void postDisconnectA2dp() {
- sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
+ }
}
/*package*/ void postDisconnectA2dpSink() {
- sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
+ }
}
/*package*/ void postDisconnectHearingAid() {
- sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
+ }
}
/*package*/ void postDisconnectHeadset() {
- sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
+ }
}
/*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
+ }
}
/*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
+ }
}
/*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE,
+ headsetProfile);
+ }
}
/*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
- hearingAidProfile);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
+ hearingAidProfile);
+ }
}
/*package*/ void postScoClientDied(Object obj) {
- sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
+ }
}
//---------------------------------------------------------------------
@@ -545,7 +570,7 @@ import java.io.PrintWriter;
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).append(" src:").append(source).toString();
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
mBluetoothA2dpEnabled = on;
mBrokerHandler.removeMessages(MSG_IIL_SET_FORCE_BT_A2DP_USE);
onSetForceUse(
@@ -557,71 +582,85 @@ import java.io.PrintWriter;
/*package*/ boolean handleDeviceConnection(boolean connect, int device, String address,
String deviceName) {
- synchronized (mDeviceStateLock) {
- return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
- }
+ return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
}
/*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
- sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
- btDeviceInfo);
+ synchronized (mDeviceBrokerLock) {
+ sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
+ btDeviceInfo);
+ }
}
/*package*/ void handleFailureToConnectToBtHeadsetService(int delay) {
- sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay);
+ synchronized (mDeviceBrokerLock) {
+ sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay);
+ }
}
/*package*/ void handleCancelFailureToConnectToBtHeadsetService() {
- mBrokerHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
+ synchronized (mDeviceBrokerLock) {
+ mBrokerHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
+ }
}
/*package*/ void postReportNewRoutes() {
- sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
+ synchronized (mDeviceBrokerLock) {
+ sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
+ }
}
/*package*/ void cancelA2dpDockTimeout() {
- mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
+ synchronized (mDeviceBrokerLock) {
+ mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
+ }
}
+ // FIXME: used by?
/*package*/ void postA2dpActiveDeviceChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
- sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
+ synchronized (mDeviceBrokerLock) {
+ sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
+ }
}
/*package*/ boolean hasScheduledA2dpDockTimeout() {
- return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
+ synchronized (mDeviceBrokerLock) {
+ return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
+ }
}
// must be called synchronized on mConnectedDevices
/*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
- return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice))
- || mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
+ synchronized (mDeviceBrokerLock) {
+ return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
+ new BtHelper.BluetoothA2dpDeviceInfo(btDevice))
+ || mBrokerHandler.hasMessages(
+ MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
+ }
}
/*package*/ void setA2dpDockTimeout(String address, int a2dpCodec, int delayMs) {
- sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
+ synchronized (mDeviceBrokerLock) {
+ sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
+ }
}
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
- synchronized (mDeviceStateLock) {
- mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
- }
+ mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
}
/*package*/ boolean getBluetoothA2dpEnabled() {
- synchronized (mDeviceStateLock) {
+ synchronized (mDeviceBrokerLock) {
return mBluetoothA2dpEnabled;
}
}
/*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) {
- synchronized (mDeviceStateLock) {
- return mBtHelper.getA2dpCodec(device);
- }
+ return mBtHelper.getA2dpCodec(device);
}
/*package*/ void dump(PrintWriter pw, String prefix) {
@@ -709,156 +748,101 @@ import java.io.PrintWriter;
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESTORE_DEVICES:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onRestoreDevices();
- mBtHelper.onAudioServerDiedRestoreA2dp();
- }
+ mDeviceInventory.onRestoreDevices();
+ mBtHelper.onAudioServerDiedRestoreA2dp();
break;
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetWiredDeviceConnectionState(
- (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
- }
+ mDeviceInventory.onSetWiredDeviceConnectionState(
+ (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
break;
case MSG_I_BROADCAST_BT_CONNECTION_STATE:
- synchronized (mDeviceStateLock) {
- mBtHelper.onBroadcastScoConnectionState(msg.arg1);
- }
+ mBtHelper.onBroadcastScoConnectionState(msg.arg1);
break;
case MSG_IIL_SET_FORCE_USE: // intended fall-through
case MSG_IIL_SET_FORCE_BT_A2DP_USE:
onSetForceUse(msg.arg1, msg.arg2, (String) msg.obj);
break;
case MSG_REPORT_NEW_ROUTES:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onReportNewRoutes();
- }
+ mDeviceInventory.onReportNewRoutes();
break;
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetA2dpSinkConnectionState(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
- }
+ mDeviceInventory.onSetA2dpSinkConnectionState(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
break;
case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetA2dpSourceConnectionState(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
- }
+ mDeviceInventory.onSetA2dpSourceConnectionState(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
break;
case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onSetHearingAidConnectionState(
- (BluetoothDevice) msg.obj, msg.arg1,
- mAudioService.getHearingAidStreamType());
- }
+ mDeviceInventory.onSetHearingAidConnectionState(
+ (BluetoothDevice) msg.obj, msg.arg1,
+ mAudioService.getHearingAidStreamType());
break;
case MSG_BT_HEADSET_CNCT_FAILED:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.resetBluetoothSco();
- }
- }
+ mBtHelper.resetBluetoothSco();
break;
case MSG_IL_BTA2DP_DOCK_TIMEOUT:
// msg.obj == address of BTA2DP device
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
- }
+ mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
break;
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
final int a2dpCodec;
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
- synchronized (mDeviceStateLock) {
- a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
- mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
- BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
- }
+ // FIXME why isn't the codec coming with the request? codec should be
+ // provided by BT when it calls
+ // AudioManager.handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)
+ a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
+ mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
+ new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
+ BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
break;
case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
onSendBecomingNoisyIntent();
break;
case MSG_II_SET_HEARING_AID_VOLUME:
- synchronized (mDeviceStateLock) {
- mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
- }
+ mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
break;
case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME:
- synchronized (mDeviceStateLock) {
- mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
- }
+ mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
break;
case MSG_I_DISCONNECT_BT_SCO:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.disconnectBluetoothSco(msg.arg1);
- }
- }
+ mBtHelper.disconnectBluetoothSco(msg.arg1);
break;
case MSG_L_SCOCLIENT_DIED:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.scoClientDied(msg.obj);
- }
- }
+ mBtHelper.scoClientDied(msg.obj);
break;
case MSG_TOGGLE_HDMI:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onToggleHdmi();
- }
+ mDeviceInventory.onToggleHdmi();
break;
case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj,
- BtHelper.EVENT_ACTIVE_DEVICE_CHANGE);
- }
+ mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj,
+ BtHelper.EVENT_ACTIVE_DEVICE_CHANGE);
break;
case MSG_DISCONNECT_A2DP:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectA2dp();
- }
+ mDeviceInventory.disconnectA2dp();
break;
case MSG_DISCONNECT_A2DP_SINK:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectA2dpSink();
- }
+ mDeviceInventory.disconnectA2dpSink();
break;
case MSG_DISCONNECT_BT_HEARING_AID:
- synchronized (mDeviceStateLock) {
- mDeviceInventory.disconnectHearingAid();
- }
+ mDeviceInventory.disconnectHearingAid();
break;
case MSG_DISCONNECT_BT_HEADSET:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.disconnectHeadset();
- }
- }
+ mBtHelper.disconnectHeadset();
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP:
- synchronized (mDeviceStateLock) {
- mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
- }
+ mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK:
- synchronized (mDeviceStateLock) {
- mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
- }
+ mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID:
- synchronized (mDeviceStateLock) {
- mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
- }
+ mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET:
- synchronized (mSetModeLock) {
- synchronized (mDeviceStateLock) {
- mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
- }
- }
+ mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
break;
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION:
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: {
@@ -871,11 +855,9 @@ import java.io.PrintWriter;
+ " addr=" + info.mDevice.getAddress()
+ " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy
+ " vol=" + info.mVolume)).printLog(TAG));
- synchronized (mDeviceStateLock) {
- mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
- info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
- AudioSystem.DEVICE_NONE, info.mVolume);
- }
+ mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
+ info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
+ AudioSystem.DEVICE_NONE, info.mVolume);
} break;
case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: {
final HearingAidDeviceConnectionInfo info =
@@ -885,10 +867,8 @@ import java.io.PrintWriter;
+ " addr=" + info.mDevice.getAddress()
+ " supprNoisy=" + info.mSupprNoisy
+ " src=" + info.mEventSource)).printLog(TAG));
- synchronized (mDeviceStateLock) {
- mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
- info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
- }
+ mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
+ info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
} break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
@@ -973,46 +953,57 @@ import java.io.PrintWriter;
/** If the msg is already queued, queue this one and leave the old. */
private static final int SENDMSG_QUEUE = 2;
+ @GuardedBy("mDeviceBrokerLock")
private void sendMsg(int msg, int existingMsgPolicy, int delay) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, null, delay);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendILMsg(int msg, int existingMsgPolicy, int arg, Object obj, int delay) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, obj, delay);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendLMsg(int msg, int existingMsgPolicy, Object obj, int delay) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, delay);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendIMsg(int msg, int existingMsgPolicy, int arg, int delay) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, null, delay);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendMsgNoDelay(int msg, int existingMsgPolicy) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, null, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendIMsgNoDelay(int msg, int existingMsgPolicy, int arg) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, null, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendIIMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2) {
sendIILMsg(msg, existingMsgPolicy, arg1, arg2, null, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendILMsgNoDelay(int msg, int existingMsgPolicy, int arg, Object obj) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, obj, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendLMsgNoDelay(int msg, int existingMsgPolicy, Object obj) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendIILMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj) {
sendIILMsg(msg, existingMsgPolicy, arg1, arg2, obj, 0);
}
+ @GuardedBy("mDeviceBrokerLock")
private void sendIILMsg(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj,
int delay) {
if (existingMsgPolicy == SENDMSG_REPLACE) {
@@ -1031,31 +1022,29 @@ import java.io.PrintWriter;
Binder.restoreCallingIdentity(identity);
}
- synchronized (sLastDeviceConnectionMsgTimeLock) {
- long time = SystemClock.uptimeMillis() + delay;
-
- switch (msg) {
- case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
- case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
- case MSG_IL_BTA2DP_DOCK_TIMEOUT:
- case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
- case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
- if (sLastDeviceConnectMsgTime >= time) {
- // add a little delay to make sure messages are ordered as expected
- time = sLastDeviceConnectMsgTime + 30;
- }
- sLastDeviceConnectMsgTime = time;
- break;
- default:
- break;
- }
+ long time = SystemClock.uptimeMillis() + delay;
- mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
- time);
+ switch (msg) {
+ case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
+ case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
+ case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
+ case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
+ case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
+ case MSG_IL_BTA2DP_DOCK_TIMEOUT:
+ case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
+ case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
+ if (sLastDeviceConnectMsgTime >= time) {
+ // add a little delay to make sure messages are ordered as expected
+ time = sLastDeviceConnectMsgTime + 30;
+ }
+ sLastDeviceConnectMsgTime = time;
+ break;
+ default:
+ break;
}
+
+ mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
+ time);
}
//-------------------------------------------------------------
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 90973a888a9d..3933fb2d5f46 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -159,7 +159,6 @@ public class AudioDeviceInventory {
}
// only public for mocking/spying
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@VisibleForTesting
public void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo,
@AudioService.BtProfileConnectionState int state) {
@@ -284,7 +283,6 @@ public class AudioDeviceInventory {
}
}
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ void onBluetoothA2dpActiveDeviceChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
final BluetoothDevice btDevice = btInfo.getBtDevice();
@@ -557,7 +555,6 @@ public class AudioDeviceInventory {
}
// only public for mocking/spying
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@VisibleForTesting
public void setBluetoothA2dpDeviceConnectionState(
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a6ac17dc9b6a..cc50e37d2b61 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -470,12 +470,11 @@ public class AudioService extends IAudioService.Stub
// List of binder death handlers for setMode() client processes.
// The last process to have called setMode() is at the top of the list.
- // package-private so it can be accessed in AudioDeviceBroker.getSetModeDeathHandlers
- //TODO candidate to be moved to separate class that handles synchronization
- @GuardedBy("mDeviceBroker.mSetModeLock")
- /*package*/ final ArrayList<SetModeDeathHandler> mSetModeDeathHandlers =
+ private final ArrayList<SetModeDeathHandler> mSetModeDeathHandlers =
new ArrayList<SetModeDeathHandler>();
+ private volatile int mCurrentModeOwnerPid = 0;
+
// true if boot sequence has been completed
private boolean mSystemReady;
// true if Intent.ACTION_USER_SWITCHED has ever been received
@@ -1018,7 +1017,14 @@ public class AudioService extends IAudioService.Stub
synchronized (mAudioPolicies) {
for (AudioPolicyProxy policy : mAudioPolicies.values()) {
- policy.connectMixes();
+ final int status = policy.connectMixes();
+ if (status != AudioSystem.SUCCESS) {
+ // note that PERMISSION_DENIED may also indicate trouble getting to APService
+ Log.e(TAG, "onAudioServerDied: error "
+ + AudioSystem.audioSystemErrorToString(status)
+ + " when connecting mixes for policy " + policy.toLogFriendlyString());
+ policy.release();
+ }
}
}
@@ -3185,15 +3191,10 @@ public class AudioService extends IAudioService.Stub
* @return 0 if nobody owns the mode
*/
/*package*/ int getModeOwnerPid() {
- int modeOwnerPid = 0;
- try {
- modeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
- } catch (Exception e) {
- // nothing to do, modeOwnerPid is not modified
- }
- return modeOwnerPid;
+ return mCurrentModeOwnerPid;
}
+
private class SetModeDeathHandler implements IBinder.DeathRecipient {
private IBinder mCb; // To be notified of client's death
private int mPid;
@@ -3207,7 +3208,7 @@ public class AudioService extends IAudioService.Stub
public void binderDied() {
int oldModeOwnerPid = 0;
int newModeOwnerPid = 0;
- synchronized (mDeviceBroker.mSetModeLock) {
+ synchronized (mSetModeDeathHandlers) {
Log.w(TAG, "setMode() client died");
if (!mSetModeDeathHandlers.isEmpty()) {
oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
@@ -3218,11 +3219,15 @@ public class AudioService extends IAudioService.Stub
} else {
newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, TAG);
}
- }
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
- mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
+
+ if (newModeOwnerPid != oldModeOwnerPid) {
+ mCurrentModeOwnerPid = newModeOwnerPid;
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
+ // connections not started by the application changing the mode when pid changes
+ if (newModeOwnerPid != 0) {
+ mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
+ }
+ }
}
}
@@ -3245,15 +3250,17 @@ public class AudioService extends IAudioService.Stub
/** @see AudioManager#setMode(int) */
public void setMode(int mode, IBinder cb, String callingPackage) {
- if (DEBUG_MODE) { Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); }
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
+ }
if (!checkAudioSettingsPermission("setMode()")) {
return;
}
- if ( (mode == AudioSystem.MODE_IN_CALL) &&
- (mContext.checkCallingOrSelfPermission(
+ if ((mode == AudioSystem.MODE_IN_CALL)
+ && (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
- != PackageManager.PERMISSION_GRANTED)) {
+ != PackageManager.PERMISSION_GRANTED)) {
Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
@@ -3265,7 +3272,7 @@ public class AudioService extends IAudioService.Stub
int oldModeOwnerPid = 0;
int newModeOwnerPid = 0;
- synchronized (mDeviceBroker.mSetModeLock) {
+ synchronized (mSetModeDeathHandlers) {
if (!mSetModeDeathHandlers.isEmpty()) {
oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
}
@@ -3273,17 +3280,21 @@ public class AudioService extends IAudioService.Stub
mode = mMode;
}
newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);
- }
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
- mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
+
+ if (newModeOwnerPid != oldModeOwnerPid) {
+ mCurrentModeOwnerPid = newModeOwnerPid;
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
+ // SCO connections not started by the application changing the mode when pid changes
+ if (newModeOwnerPid != 0) {
+ mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
+ }
+ }
}
}
// setModeInt() returns a valid PID if the audio mode was successfully set to
// any mode other than NORMAL.
- @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("mSetModeDeathHandlers")
private int setModeInt(int mode, IBinder cb, int pid, String caller) {
if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller="
+ caller + ")"); }
@@ -3622,9 +3633,7 @@ public class AudioService extends IAudioService.Stub
!mSystemReady) {
return;
}
- synchronized (mDeviceBroker.mSetModeLock) {
- mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
- }
+ mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
}
/** @see AudioManager#stopBluetoothSco() */
@@ -3636,9 +3645,7 @@ public class AudioService extends IAudioService.Stub
final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).toString();
- synchronized (mDeviceBroker.mSetModeLock) {
- mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
- }
+ mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
}
@@ -4399,7 +4406,7 @@ public class AudioService extends IAudioService.Stub
// NOTE: Locking order for synchronized objects related to volume or ringer mode management:
// 1 mScoclient OR mSafeMediaVolumeState
- // 2 mSetModeLock
+ // 2 mSetModeDeathHandlers
// 3 mSettingsLock
// 4 VolumeStreamState.class
private class VolumeStreamState {
@@ -7019,16 +7026,8 @@ public class AudioService extends IAudioService.Stub
}
public void binderDied() {
- synchronized (mAudioPolicies) {
- Log.i(TAG, "audio policy " + mPolicyCallback + " died");
- release();
- mAudioPolicies.remove(mPolicyCallback.asBinder());
- }
- if (mIsVolumeController) {
- synchronized (mExtVolumeControllerLock) {
- mExtVolumeController = null;
- }
- }
+ Log.i(TAG, "audio policy " + mPolicyCallback + " died");
+ release();
}
String getRegistrationId() {
@@ -7052,9 +7051,20 @@ public class AudioService extends IAudioService.Stub
Log.e(TAG, "Fail to unregister Audiopolicy callback from MediaProjection");
}
}
+ if (mIsVolumeController) {
+ synchronized (mExtVolumeControllerLock) {
+ mExtVolumeController = null;
+ }
+ }
final long identity = Binder.clearCallingIdentity();
AudioSystem.registerPolicyMixes(mMixes, false);
Binder.restoreCallingIdentity(identity);
+ synchronized (mAudioPolicies) {
+ mAudioPolicies.remove(mPolicyCallback.asBinder());
+ }
+ try {
+ mPolicyCallback.notifyUnregistration();
+ } catch (RemoteException e) { }
}
boolean hasMixAffectingUsage(int usage, int excludedFlags) {
@@ -7105,7 +7115,7 @@ public class AudioService extends IAudioService.Stub
}
}
- int connectMixes() {
+ @AudioSystem.AudioSystemError int connectMixes() {
final long identity = Binder.clearCallingIdentity();
int status = AudioSystem.registerPolicyMixes(mMixes, true);
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 9f1a6bd15ac3..625b6b690443 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -171,8 +171,6 @@ public class BtHelper {
//----------------------------------------------------------------------
// Interface for AudioDeviceBroker
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
@@ -245,8 +243,6 @@ public class BtHelper {
return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void receiveBtEvent(Intent intent) {
final String action = intent.getAction();
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
@@ -333,8 +329,6 @@ public class BtHelper {
*
* @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
*/
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
checkScoAudioState();
if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
@@ -343,8 +337,6 @@ public class BtHelper {
clearAllScoClients(exceptPid, true);
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, true);
@@ -364,8 +356,6 @@ public class BtHelper {
Binder.restoreCallingIdentity(ident);
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, false);
@@ -423,8 +413,6 @@ public class BtHelper {
mDeviceBroker.postDisconnectHearingAid();
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() {
clearAllScoClients(0, false);
mScoAudioState = SCO_STATE_INACTIVE;
@@ -433,8 +421,6 @@ public class BtHelper {
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectHeadset() {
setBtScoActiveDevice(null);
mBluetoothHeadset = null;
@@ -480,8 +466,6 @@ public class BtHelper {
/*eventSource*/ "mBluetoothProfileServiceListener");
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
@@ -568,8 +552,6 @@ public class BtHelper {
return result;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void setBtScoActiveDevice(BluetoothDevice btDevice) {
Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
@@ -652,8 +634,6 @@ public class BtHelper {
};
//----------------------------------------------------------------------
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void scoClientDied(Object obj) {
final ScoClient client = (ScoClient) obj;
Log.w(TAG, "SCO client died");
@@ -684,8 +664,6 @@ public class BtHelper {
mDeviceBroker.postScoClientDied(this);
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void incCount(int scoAudioMode) {
if (!requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode)) {
@@ -705,8 +683,6 @@ public class BtHelper {
mStartcount++;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void decCount() {
if (mStartcount == 0) {
@@ -726,8 +702,6 @@ public class BtHelper {
}
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void clearCount(boolean stopSco) {
if (mStartcount != 0) {
@@ -764,8 +738,6 @@ public class BtHelper {
return count;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private boolean requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
@@ -959,8 +931,6 @@ public class BtHelper {
return null;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void clearAllScoClients(int exceptPid, boolean stopSco) {
ScoClient savedClient = null;
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java
index 35e8f56cf36d..35e8f56cf36d 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java
diff --git a/core/java/com/android/server/backup/UsageStatsBackupHelper.java b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
index d6a70d369cdc..d6a70d369cdc 100644
--- a/core/java/com/android/server/backup/UsageStatsBackupHelper.java
+++ b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 619c21e7646b..44c81fc09b2a 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -988,18 +988,6 @@ public class BiometricService extends SystemService {
}
}
- private String getErrorString(int modality, int error, int vendorCode) {
- for (AuthenticatorWrapper authenticator : mAuthenticators) {
- if (authenticator.modality == modality) {
- // TODO(b/141025588): Refactor IBiometricServiceReceiver.aidl#onError(...) to not
- // ask for a String error message, but derive it from the error code instead.
- return "";
- }
- }
- Slog.w(TAG, "Unable to get error string for modality: " + modality);
- return null;
- }
-
private void logDialogDismissed(int reason) {
if (reason == BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED) {
// Explicit auth, authentication confirmed.
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 854f16aeb54e..9ac9955493cc 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -19,7 +19,7 @@ package com.android.server.compat;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.os.Process;
+import android.os.UserHandle;
import android.util.Slog;
import android.util.StatsLog;
@@ -54,8 +54,8 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
@Override
- public void reportChangeByPackageName(long changeId, String packageName) {
- ApplicationInfo appInfo = getApplicationInfo(packageName);
+ public void reportChangeByPackageName(long changeId, String packageName, int userId) {
+ ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
if (appInfo == null) {
return;
}
@@ -80,8 +80,8 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
@Override
- public boolean isChangeEnabledByPackageName(long changeId, String packageName) {
- ApplicationInfo appInfo = getApplicationInfo(packageName);
+ public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
+ ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
if (appInfo == null) {
return true;
}
@@ -96,7 +96,8 @@ public class PlatformCompat extends IPlatformCompat.Stub {
}
boolean enabled = true;
for (String packageName : packages) {
- enabled = enabled && isChangeEnabledByPackageName(changeId, packageName);
+ enabled = enabled && isChangeEnabledByPackageName(changeId, packageName,
+ UserHandle.getUserId(uid));
}
return enabled;
}
@@ -127,10 +128,9 @@ public class PlatformCompat extends IPlatformCompat.Stub {
mChangeReporter.resetReportedChanges(appInfo.uid);
}
- private ApplicationInfo getApplicationInfo(String packageName) {
+ private ApplicationInfo getApplicationInfo(String packageName, int userId) {
try {
- return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0,
- Process.myUid());
+ return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0, userId);
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "No installed package " + packageName);
}
diff --git a/services/core/java/com/android/server/compat/PlatformCompatNative.java b/services/core/java/com/android/server/compat/PlatformCompatNative.java
index 839967139baa..85dfbf411667 100644
--- a/services/core/java/com/android/server/compat/PlatformCompatNative.java
+++ b/services/core/java/com/android/server/compat/PlatformCompatNative.java
@@ -29,8 +29,8 @@ public class PlatformCompatNative extends IPlatformCompatNative.Stub {
}
@Override
- public void reportChangeByPackageName(long changeId, String packageName) {
- mPlatformCompat.reportChangeByPackageName(changeId, packageName);
+ public void reportChangeByPackageName(long changeId, String packageName, int userId) {
+ mPlatformCompat.reportChangeByPackageName(changeId, packageName, userId);
}
@Override
@@ -39,8 +39,8 @@ public class PlatformCompatNative extends IPlatformCompatNative.Stub {
}
@Override
- public boolean isChangeEnabledByPackageName(long changeId, String packageName) {
- return mPlatformCompat.isChangeEnabledByPackageName(changeId, packageName);
+ public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
+ return mPlatformCompat.isChangeEnabledByPackageName(changeId, packageName, userId);
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 077c4057a3a0..d13e6753d1b0 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -53,7 +53,8 @@ public class NetworkNotificationManager {
NO_INTERNET(SystemMessage.NOTE_NETWORK_NO_INTERNET),
LOGGED_IN(SystemMessage.NOTE_NETWORK_LOGGED_IN),
PARTIAL_CONNECTIVITY(SystemMessage.NOTE_NETWORK_PARTIAL_CONNECTIVITY),
- SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN);
+ SIGN_IN(SystemMessage.NOTE_NETWORK_SIGN_IN),
+ PRIVATE_DNS_BROKEN(SystemMessage.NOTE_NETWORK_PRIVATE_DNS_BROKEN);
public final int eventId;
@@ -175,13 +176,23 @@ public class NetworkNotificationManager {
}
Resources r = Resources.getSystem();
- CharSequence title;
- CharSequence details;
+ final CharSequence title;
+ final CharSequence details;
int icon = getIcon(transportType, notifyType);
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet,
WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
details = r.getString(R.string.wifi_no_internet_detailed);
+ } else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) {
+ if (transportType == TRANSPORT_CELLULAR) {
+ title = r.getString(R.string.mobile_no_internet);
+ } else if (transportType == TRANSPORT_WIFI) {
+ title = r.getString(R.string.wifi_no_internet,
+ WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
+ } else {
+ title = r.getString(R.string.other_networks_no_internet);
+ }
+ details = r.getString(R.string.private_dns_broken_detailed);
} else if (notifyType == NotificationType.PARTIAL_CONNECTIVITY
&& transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.network_partial_connectivity,
@@ -357,8 +368,10 @@ public class NetworkNotificationManager {
}
switch (t) {
case SIGN_IN:
- return 5;
+ return 6;
case PARTIAL_CONNECTIVITY:
+ return 5;
+ case PRIVATE_DNS_BROKEN:
return 4;
case NO_INTERNET:
return 3;
diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java
index 61e5d1f31f73..75ba5b8c212e 100644
--- a/services/core/java/com/android/server/location/CallerIdentity.java
+++ b/services/core/java/com/android/server/location/CallerIdentity.java
@@ -17,6 +17,7 @@
package com.android.server.location;
import android.annotation.NonNull;
+import android.annotation.Nullable;
/**
* Represents the calling process's uid, pid, and package name.
@@ -25,13 +26,15 @@ public class CallerIdentity {
public final int mUid;
public final int mPid;
public final String mPackageName;
+ public final @Nullable String mFeatureId;
public final @NonNull String mListenerIdentifier;
- public CallerIdentity(int uid, int pid, String packageName,
+ public CallerIdentity(int uid, int pid, String packageName, @Nullable String featureId,
@NonNull String listenerIdentifier) {
mUid = uid;
mPid = pid;
mPackageName = packageName;
+ mFeatureId = featureId;
mListenerIdentifier = listenerIdentifier;
}
}
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index 4f10a715a166..17a21694e725 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -17,6 +17,7 @@
package com.android.server.location;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
@@ -152,7 +153,7 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
}
public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent,
- int allowedResolutionLevel, int uid, String packageName,
+ int allowedResolutionLevel, int uid, String packageName, @Nullable String featureId,
@NonNull String listenerIdentifier) {
if (D) {
Slog.d(TAG, "addFence: request=" + request + ", geofence=" + geofence
@@ -160,8 +161,8 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
}
GeofenceState state = new GeofenceState(geofence,
- request.getExpireAt(), allowedResolutionLevel, uid, packageName, listenerIdentifier,
- intent);
+ request.getExpireAt(), allowedResolutionLevel, uid, packageName, featureId,
+ listenerIdentifier, intent);
synchronized (mLock) {
// first make sure it doesn't already exist
for (int i = mFences.size() - 1; i >= 0; i--) {
@@ -304,7 +305,7 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
int op = LocationManagerService.resolutionLevelToOp(state.mAllowedResolutionLevel);
if (op >= 0) {
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, state.mUid,
- state.mPackageName, null, state.mListenerIdentifier)
+ state.mPackageName, state.mFeatureId, state.mListenerIdentifier)
!= AppOpsManager.MODE_ALLOWED) {
if (D) {
Slog.d(TAG, "skipping geofence processing for no op app: "
diff --git a/services/core/java/com/android/server/location/GeofenceState.java b/services/core/java/com/android/server/location/GeofenceState.java
index fe0719df1ced..a91a1dcb78e7 100644
--- a/services/core/java/com/android/server/location/GeofenceState.java
+++ b/services/core/java/com/android/server/location/GeofenceState.java
@@ -18,6 +18,7 @@
package com.android.server.location;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.location.Geofence;
import android.location.Location;
@@ -39,6 +40,7 @@ public class GeofenceState {
public final int mAllowedResolutionLevel;
public final int mUid;
public final String mPackageName;
+ public final @Nullable String mFeatureId;
public final @NonNull String mListenerIdentifier;
public final PendingIntent mIntent;
@@ -46,7 +48,8 @@ public class GeofenceState {
double mDistanceToCenter; // current distance to center of fence
public GeofenceState(Geofence fence, long expireAt, int allowedResolutionLevel, int uid,
- String packageName, @NonNull String listenerIdentifier, PendingIntent intent) {
+ String packageName, @Nullable String featureId, @NonNull String listenerIdentifier,
+ PendingIntent intent) {
mState = STATE_UNKNOWN;
mDistanceToCenter = Double.MAX_VALUE;
@@ -55,6 +58,7 @@ public class GeofenceState {
mAllowedResolutionLevel = allowedResolutionLevel;
mUid = uid;
mPackageName = packageName;
+ mFeatureId = featureId;
mListenerIdentifier = listenerIdentifier;
mIntent = intent;
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 25b544fbcb86..60ce1f4d56f7 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -182,7 +182,7 @@ public abstract class RemoteListenerHelper<TListener extends IInterface> {
}
return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
- callerIdentity.mPackageName, null,
+ callerIdentity.mPackageName, callerIdentity.mFeatureId,
"Location sent to " + callerIdentity.mListenerIdentifier)
== AppOpsManager.MODE_ALLOWED;
}
diff --git a/services/core/java/com/android/server/location/TEST_MAPPING b/services/core/java/com/android/server/location/TEST_MAPPING
new file mode 100644
index 000000000000..2e21fa67a967
--- /dev/null
+++ b/services/core/java/com/android/server/location/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsLocationCoarseTestCases"
+ },
+ {
+ "name": "CtsLocationNoneTestCases"
+ }
+ ]
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 71c7b23a3a0d..0f8561e5afd9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -16,14 +16,22 @@
package com.android.server.locksettings;
+import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+
import android.app.ActivityManager;
+import android.app.admin.PasswordMetrics;
import android.os.ShellCommand;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.PasswordValidationError;
import java.io.PrintWriter;
+import java.util.List;
class LockSettingsShellCommand extends ShellCommand {
@@ -70,18 +78,19 @@ class LockSettingsShellCommand extends ShellCommand {
if (!checkCredential()) {
return -1;
}
+ boolean success = true;
switch (cmd) {
case COMMAND_SET_PATTERN:
- runSetPattern();
+ success = runSetPattern();
break;
case COMMAND_SET_PASSWORD:
- runSetPassword();
+ success = runSetPassword();
break;
case COMMAND_SET_PIN:
- runSetPin();
+ success = runSetPin();
break;
case COMMAND_CLEAR:
- runClear();
+ success = runClear();
break;
case COMMAND_SP:
runChangeSp();
@@ -102,7 +111,7 @@ class LockSettingsShellCommand extends ShellCommand {
getErrPrintWriter().println("Unknown command: " + cmd);
break;
}
- return 0;
+ return success ? 0 : -1;
} catch (Exception e) {
getErrPrintWriter().println("Error while executing command: " + cmd);
e.printStackTrace(getErrPrintWriter());
@@ -201,34 +210,66 @@ class LockSettingsShellCommand extends ShellCommand {
}
}
- private void runSetPattern() {
- mLockPatternUtils.setLockCredential(
- LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
- mNew.getBytes())),
- getOldCredential(),
- mCurrentUserId);
+ private boolean runSetPattern() {
+ final LockscreenCredential pattern = LockscreenCredential.createPattern(
+ LockPatternUtils.byteArrayToPattern(mNew.getBytes()));
+ if (!isNewCredentialSufficient(pattern)) {
+ return false;
+ }
+ mLockPatternUtils.setLockCredential(pattern, getOldCredential(), mCurrentUserId);
getOutPrintWriter().println("Pattern set to '" + mNew + "'");
+ return true;
}
- private void runSetPassword() {
- mLockPatternUtils.setLockCredential(LockscreenCredential.createPassword(mNew),
- getOldCredential(),
- mCurrentUserId);
+ private boolean runSetPassword() {
+ final LockscreenCredential password = LockscreenCredential.createPassword(mNew);
+ if (!isNewCredentialSufficient(password)) {
+ return false;
+ }
+ mLockPatternUtils.setLockCredential(password, getOldCredential(), mCurrentUserId);
getOutPrintWriter().println("Password set to '" + mNew + "'");
+ return true;
}
- private void runSetPin() {
- mLockPatternUtils.setLockCredential(LockscreenCredential.createPin(mNew),
- getOldCredential(),
- mCurrentUserId);
+ private boolean runSetPin() {
+ final LockscreenCredential pin = LockscreenCredential.createPin(mNew);
+ if (!isNewCredentialSufficient(pin)) {
+ return false;
+ }
+ mLockPatternUtils.setLockCredential(pin, getOldCredential(), mCurrentUserId);
getOutPrintWriter().println("Pin set to '" + mNew + "'");
+ return true;
}
- private void runClear() {
- mLockPatternUtils.setLockCredential(LockscreenCredential.createNone(),
- getOldCredential(),
- mCurrentUserId);
+ private boolean runClear() {
+ LockscreenCredential none = LockscreenCredential.createNone();
+ if (!isNewCredentialSufficient(none)) {
+ return false;
+ }
+ mLockPatternUtils.setLockCredential(none, getOldCredential(), mCurrentUserId);
getOutPrintWriter().println("Lock credential cleared");
+ return true;
+ }
+
+ private boolean isNewCredentialSufficient(LockscreenCredential credential) {
+ final PasswordMetrics requiredMetrics =
+ mLockPatternUtils.getRequestedPasswordMetrics(mCurrentUserId);
+ final List<PasswordValidationError> errors;
+ if (credential.isPassword() || credential.isPin()) {
+ errors = PasswordMetrics.validatePassword(requiredMetrics, PASSWORD_COMPLEXITY_NONE,
+ credential.isPin(), credential.getCredential());
+ } else {
+ PasswordMetrics metrics = new PasswordMetrics(
+ credential.isPattern() ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_NONE);
+ errors = PasswordMetrics.validatePasswordMetrics(
+ requiredMetrics, PASSWORD_COMPLEXITY_NONE, false /* isPin */, metrics);
+ }
+ if (!errors.isEmpty()) {
+ getOutPrintWriter().println(
+ "New credential doesn't satisfy admin policies: " + errors.get(0));
+ return false;
+ }
+ return true;
}
private void runSetDisabled() {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 09be474a5598..388214bffcb2 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -229,6 +229,8 @@ import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import libcore.io.IoUtils;
@@ -363,7 +365,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
"com.android.server.net.action.SNOOZE_RAPID";
/**
- * Indicates the maximum wait time for admin data to be available;
+ * Indicates the maximum wait time for admin data to be available.
*/
private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
@@ -396,6 +398,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private NetworkStatsManagerInternal mNetworkStats;
private final INetworkManagementService mNetworkManager;
private UsageStatsManagerInternal mUsageStats;
+ private AppStandbyInternal mAppStandby;
private final Clock mClock;
private final UserManager mUserManager;
private final CarrierConfigManager mCarrierConfigManager;
@@ -734,6 +737,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+ mAppStandby = LocalServices.getService(AppStandbyInternal.class);
mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class);
synchronized (mUidRulesFirstLock) {
@@ -868,7 +872,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mContext.getSystemService(ConnectivityManager.class).registerNetworkCallback(
new NetworkRequest.Builder().build(), mNetworkCallback);
- mUsageStats.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
+ mAppStandby.addListener(new NetPolicyAppIdleStateChangeListener());
// Listen for subscriber changes
mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener(
@@ -4375,9 +4379,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return newUidRules;
}
- private class AppIdleStateChangeListener
- extends UsageStatsManagerInternal.AppIdleStateChangeListener {
-
+ private class NetPolicyAppIdleStateChangeListener extends AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
int reason) {
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
new file mode 100644
index 000000000000..9f04260242d9
--- /dev/null
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -0,0 +1,31 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsHostsideNetworkTests",
+ "file_patterns": ["(/|^)NetworkPolicy[^/]*\\.java"],
+ "options": [
+ {
+ "include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "file_patterns": ["(/|^)NetworkPolicy[^/]*\\.java"],
+ "options": [
+ {
+ "include-filter": "com.android.server.net."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cd3343bbec7b..0fc1718ad673 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7826,6 +7826,7 @@ public class NotificationManagerService extends SystemService {
R.array.config_allowedManagedServicesOnLowRamDevices)) {
if (whitelisted.equals(pkg)) {
canUseManagedServices = true;
+ break;
}
}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 714bbb97c90d..9a1b30dc2b0b 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -25,7 +25,6 @@ import android.os.Binder;
import android.os.BugreportParams;
import android.os.IDumpstate;
import android.os.IDumpstateListener;
-import android.os.IDumpstateToken;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -64,13 +63,6 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
@Override
@RequiresPermission(android.Manifest.permission.DUMP)
- public IDumpstateToken setListener(String name, IDumpstateListener listener,
- boolean getSectionDetails) {
- throw new UnsupportedOperationException("setListener is not allowed on this service");
- }
-
- @Override
- @RequiresPermission(android.Manifest.permission.DUMP)
public void startBugreport(int callingUidUnused, String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
int bugreportMode, IDumpstateListener listener) {
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index dc00cb45596c..12e8069ecae5 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,12 +32,14 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.os.Environment;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.sysprop.ApexProperties;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
@@ -46,6 +48,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -63,9 +66,9 @@ abstract class ApexManager {
static final int MATCH_FACTORY_PACKAGE = 1 << 1;
/**
- * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerNoOp} depending
- * on whenever this device supports APEX, i.e. {@link ApexProperties#updatable()} evaluates to
- * {@code true}.
+ * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex}
+ * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
+ * evaluates to {@code true}.
*/
static ApexManager create(Context systemContext) {
if (ApexProperties.updatable().orElse(false)) {
@@ -76,10 +79,28 @@ abstract class ApexManager {
throw new IllegalStateException("Required service apexservice not available");
}
} else {
- return new ApexManagerNoOp();
+ return new ApexManagerFlattenedApex();
}
}
+ /**
+ * Minimal information about APEX mount points and the original APEX package they refer to.
+ */
+ static class ActiveApexInfo {
+ public final File apexDirectory;
+ public final File preinstalledApexPath;
+
+ private ActiveApexInfo(File apexDirectory, File preinstalledApexPath) {
+ this.apexDirectory = apexDirectory;
+ this.preinstalledApexPath = preinstalledApexPath;
+ }
+ }
+
+ /**
+ * Returns {@link ActiveApexInfo} records relative to all active APEX packages.
+ */
+ abstract List<ActiveApexInfo> getActiveApexInfos();
+
abstract void systemReady();
/**
@@ -217,7 +238,8 @@ abstract class ApexManager {
* An implementation of {@link ApexManager} that should be used in case device supports updating
* APEX packages.
*/
- private static class ApexManagerImpl extends ApexManager {
+ @VisibleForTesting
+ static class ApexManagerImpl extends ApexManager {
private final IApexService mApexService;
private final Context mContext;
private final Object mLock = new Object();
@@ -257,6 +279,22 @@ abstract class ApexManager {
}
@Override
+ List<ActiveApexInfo> getActiveApexInfos() {
+ try {
+ return Arrays.stream(mApexService.getActivePackages())
+ .map(apexInfo -> new ActiveApexInfo(
+ new File(
+ Environment.getApexDirectory() + File.separator
+ + apexInfo.moduleName),
+ new File(apexInfo.modulePath))).collect(
+ Collectors.toList());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
void systemReady() {
mContext.registerReceiver(new BroadcastReceiver() {
@Override
@@ -335,8 +373,8 @@ abstract class ApexManager {
if (!packageInfo.packageName.equals(packageName)) {
continue;
}
- if ((!matchActive || isActive(packageInfo))
- && (!matchFactory || isFactory(packageInfo))) {
+ if ((matchActive && isActive(packageInfo))
+ || (matchFactory && isFactory(packageInfo))) {
return packageInfo;
}
}
@@ -547,7 +585,40 @@ abstract class ApexManager {
* An implementation of {@link ApexManager} that should be used in case device does not support
* updating APEX packages.
*/
- private static final class ApexManagerNoOp extends ApexManager {
+ private static final class ApexManagerFlattenedApex extends ApexManager {
+
+ @Override
+ List<ActiveApexInfo> getActiveApexInfos() {
+ // There is no apexd running in case of flattened apex
+ // We look up the /apex directory and identify the active APEX modules from there.
+ // As "preinstalled" path, we just report /system since in the case of flattened APEX
+ // the /apex directory is just a symlink to /system/apex.
+ List<ActiveApexInfo> result = new ArrayList<>();
+ File apexDir = Environment.getApexDirectory();
+ // In flattened configuration, init special-case the art directory and bind-mounts
+ // com.android.art.{release|debug} to com.android.art. At the time of writing, these
+ // directories are copied from the kArtApexDirNames variable in
+ // system/core/init/mount_namespace.cpp.
+ String[] skipDirs = {"com.android.art.release", "com.android.art.debug"};
+ if (apexDir.isDirectory()) {
+ File[] files = apexDir.listFiles();
+ // listFiles might be null if system server doesn't have permission to read
+ // a directory.
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory() && !file.getName().contains("@")) {
+ for (String skipDir : skipDirs) {
+ if (file.getName().equals(skipDir)) {
+ continue;
+ }
+ }
+ result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
+ }
+ }
+ }
+ }
+ return result;
+ }
@Override
void systemReady() {
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 1d3d24c27041..259200b92597 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -67,6 +67,15 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
codeRoot = Environment.getSystemExtDirectory();
} else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
codeRoot = Environment.getOdmDirectory();
+ } else if (FileUtils.contains(Environment.getApexDirectory(), codePath)) {
+ String fullPath = codePath.getAbsolutePath();
+ String[] parts = fullPath.split(File.separator);
+ if (parts.length > 2) {
+ codeRoot = new File(parts[1] + File.separator + parts[2]);
+ } else {
+ Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath);
+ codeRoot = Environment.getApexDirectory();
+ }
} else {
// Unrecognized code path; take its top real segment as the apk root:
// e.g. /something/app/blah.apk => /something
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 00c0566a1c4c..ed2bb3d53649 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -259,7 +259,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
// Don't hold mSessions lock when calling restoreSession, since it might trigger an APK
// atomic install which needs to query sessions, which requires lock on mSessions.
for (PackageInstallerSession session : stagedSessionsToRestore) {
- if (mPm.isDeviceUpgrading()) {
+ if (mPm.isDeviceUpgrading() && !session.isStagedAndInTerminalState()) {
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
"Build fingerprint has changed");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 104ce1cbd5cd..74a85d58c016 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -370,6 +370,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
/**
* Keep track of all those APKs everywhere.
@@ -762,6 +763,8 @@ public class PackageManagerService extends IPackageManager.Stub
new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT,
true /* hasPriv */, true /* hasOverlays */)));
+ private final List<SystemPartition> mDirsToScanAsSystem;
+
/**
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
*
@@ -2552,6 +2555,16 @@ public class PackageManagerService extends IPackageManager.Stub
mApexManager = ApexManager.create(mContext);
mAppsFilter = mInjector.getAppsFilter();
+ mDirsToScanAsSystem = new ArrayList<>();
+ mDirsToScanAsSystem.addAll(SYSTEM_PARTITIONS);
+ mDirsToScanAsSystem.addAll(mApexManager.getActiveApexInfos().stream()
+ .map(ai -> resolveApexToSystemPartition(ai))
+ .filter(Objects::nonNull).collect(Collectors.toList()));
+ Slog.d(TAG,
+ "Directories scanned as system partitions: [" + mDirsToScanAsSystem.stream().map(
+ d -> (d.folder.getAbsolutePath() + ":" + d.scanFlag))
+ .collect(Collectors.joining(",")) + "]");
+
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
@@ -2684,8 +2697,8 @@ public class PackageManagerService extends IPackageManager.Stub
// reside in the right directory.
final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
- for (int i = SYSTEM_PARTITIONS.size() - 1; i >= 0; i--) {
- final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
+ final SystemPartition partition = mDirsToScanAsSystem.get(i);
if (partition.overlayFolder == null) {
continue;
}
@@ -2699,8 +2712,8 @@ public class PackageManagerService extends IPackageManager.Stub
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
- for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
- final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
+ final SystemPartition partition = mDirsToScanAsSystem.get(i);
if (partition.privAppFolder != null) {
scanDirTracedLI(partition.privAppFolder, systemParseFlags,
systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0);
@@ -2878,8 +2891,8 @@ public class PackageManagerService extends IPackageManager.Stub
@ParseFlags int reparseFlags = 0;
@ScanFlags int rescanFlags = 0;
- for (int i1 = 0, size = SYSTEM_PARTITIONS.size(); i1 < size; i1++) {
- SystemPartition partition = SYSTEM_PARTITIONS.get(i1);
+ for (int i1 = 0, size = mDirsToScanAsSystem.size(); i1 < size; i1++) {
+ SystemPartition partition = mDirsToScanAsSystem.get(i1);
if (partition.containsPrivApp(scanFile)) {
reparseFlags = systemParseFlags;
rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
@@ -4092,7 +4105,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
return generatePackageInfo(ps, flags, userId);
}
- if (!matchFactoryOnly && (flags & MATCH_APEX) != 0) {
+ if ((flags & MATCH_APEX) != 0) {
return mApexManager.getPackageInfo(packageName, ApexManager.MATCH_ACTIVE_PACKAGE);
}
}
@@ -17778,6 +17791,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
static boolean locationIsPrivileged(String path) {
+ // TODO(dariofreni): include APEX partitions when they will support priv apps.
for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
SystemPartition partition = SYSTEM_PARTITIONS.get(i);
if (partition.containsPrivPath(path)) {
@@ -17787,6 +17801,18 @@ public class PackageManagerService extends IPackageManager.Stub
return false;
}
+ private static @Nullable SystemPartition resolveApexToSystemPartition(
+ ApexManager.ActiveApexInfo apexInfo) {
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ SystemPartition sp = SYSTEM_PARTITIONS.get(i);
+ if (apexInfo.preinstalledApexPath.getAbsolutePath().startsWith(
+ sp.folder.getAbsolutePath())) {
+ return new SystemPartition(apexInfo.apexDirectory, sp.scanFlag,
+ false /* hasPriv */, false /* hasOverlays */);
+ }
+ }
+ return null;
+ }
/*
* Tries to delete system package.
@@ -17897,8 +17923,8 @@ public class PackageManagerService extends IPackageManager.Stub
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
- for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
- SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
+ SystemPartition partition = mDirsToScanAsSystem.get(i);
if (partition.containsPath(codePathString)) {
scanFlags |= partition.scanFlag;
if (partition.containsPrivPath(codePathString)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index cfc5ca0f11cd..4d2512c26422 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2986,8 +2986,8 @@ public class UserManagerService extends IUserManager.Stub {
// Must start user (which will be stopped right away, through
// UserController.finishUserUnlockedCompleted) so services can properly
// intialize it.
- // TODO(b/140750212): in the long-term, we should add a onCreateUser() callback
- // on SystemService instead.
+ // TODO(b/143092698): in the long-term, it might be better to add a onCreateUser()
+ // callback on SystemService instead.
Slog.i(LOG_TAG, "starting pre-created user " + userInfo.toFullString());
final IActivityManager am = ActivityManager.getService();
try {
@@ -3003,7 +3003,7 @@ public class UserManagerService extends IUserManager.Stub {
Binder.restoreCallingIdentity(ident);
}
- // TODO(b/140750212): it's possible to reach "max users overflow" when the user is created
+ // TODO(b/143092698): it's possible to reach "max users overflow" when the user is created
// "from scratch" (i.e., not from a pre-created user) and reaches the maximum number of
// users without counting the pre-created one. Then when the pre-created is converted, the
// "effective" number of max users is exceeds. Example:
@@ -3048,7 +3048,7 @@ public class UserManagerService extends IUserManager.Stub {
* <p>Should be used only during user creation, so the pre-created user can be used (instead of
* creating and initializing a new user from scratch).
*/
- // TODO(b/140750212): add unit test
+ // TODO(b/143092698): add unit test
@GuardedBy("mUsersLock")
private @Nullable UserData getPreCreatedUserLU(@UserInfoFlag int flags) {
if (DBG) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2593c38180e2..5e1d93fdda3d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -386,6 +386,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
BurnInProtectionHelper mBurnInProtectionHelper;
private DisplayFoldController mDisplayFoldController;
AppOpsManager mAppOpsManager;
+ PackageManager mPackageManager;
private boolean mHasFeatureAuto;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
@@ -1555,10 +1556,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void launchAllAppsAction() {
Intent intent = new Intent(Intent.ACTION_ALL_APPS);
if (mHasFeatureLeanback) {
- final PackageManager pm = mContext.getPackageManager();
Intent intentLauncher = new Intent(Intent.ACTION_MAIN);
intentLauncher.addCategory(Intent.CATEGORY_HOME);
- ResolveInfo resolveInfo = pm.resolveActivityAsUser(intentLauncher,
+ ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(intentLauncher,
PackageManager.MATCH_SYSTEM_ONLY,
mCurrentUserId);
if (resolveInfo != null) {
@@ -1753,10 +1753,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
- mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH);
- mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK);
- mHasFeatureAuto = mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE);
- mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC);
+ mPackageManager = mContext.getPackageManager();
+ mHasFeatureWatch = mPackageManager.hasSystemFeature(FEATURE_WATCH);
+ mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK);
+ mHasFeatureAuto = mPackageManager.hasSystemFeature(FEATURE_AUTOMOTIVE);
+ mHasFeatureHdmiCec = mPackageManager.hasSystemFeature(FEATURE_HDMI_CEC);
mAccessibilityShortcutController =
new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId);
mLogger = new MetricsLogger();
@@ -1994,7 +1995,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING;
- if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
+ if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
}
}
@@ -2138,7 +2139,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
ApplicationInfo appInfo;
try {
- appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
+ appInfo = mPackageManager.getApplicationInfoAsUser(
attrs.packageName,
0 /* flags */,
UserHandle.getUserId(callingUid));
@@ -4914,7 +4915,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override public void run() {
if (mBootMsgDialog == null) {
int theme;
- if (mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK)) {
+ if (mPackageManager.hasSystemFeature(FEATURE_LEANBACK)) {
theme = com.android.internal.R.style.Theme_Leanback_Dialog_Alert;
} else {
theme = 0;
@@ -4943,7 +4944,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return true;
}
};
- if (mContext.getPackageManager().isDeviceUpgrading()) {
+ if (mPackageManager.isDeviceUpgrading()) {
mBootMsgDialog.setTitle(R.string.android_upgrading_title);
} else {
mBootMsgDialog.setTitle(R.string.android_start_title);
@@ -5203,7 +5204,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
ActivityInfo ai = null;
- ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser(
+ ResolveInfo info = mPackageManager.resolveActivityAsUser(
intent,
PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
mCurrentUserId);
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index e4cb19e3eb21..e72ba8d9f01b 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -17,6 +17,7 @@
package com.android.server.twilight;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -160,8 +161,13 @@ public final class TwilightService extends SystemService
// Request the device's location immediately if a previous location isn't available.
if (mLocationManager.getLastLocation() == null) {
if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
- mLocationManager.requestSingleUpdate(
- LocationManager.NETWORK_PROVIDER, this, Looper.getMainLooper());
+ mLocationManager.getCurrentLocation(
+ LocationManager.NETWORK_PROVIDER, null, getContext().getMainExecutor(),
+ this::onLocationChanged);
+ } else if (mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
+ mLocationManager.getCurrentLocation(
+ LocationManager.GPS_PROVIDER, null, getContext().getMainExecutor(),
+ this::onLocationChanged);
}
}
@@ -218,12 +224,7 @@ public final class TwilightService extends SystemService
for (int i = mListeners.size() - 1; i >= 0; --i) {
final TwilightListener listener = mListeners.keyAt(i);
final Handler handler = mListeners.valueAt(i);
- handler.post(new Runnable() {
- @Override
- public void run() {
- listener.onTwilightStateChanged(state);
- }
- });
+ handler.post(() -> listener.onTwilightStateChanged(state));
}
}
}
@@ -243,12 +244,8 @@ public final class TwilightService extends SystemService
}
@Override
- public void onLocationChanged(Location location) {
- // Location providers may erroneously return (0.0, 0.0) when they fail to determine the
- // device's location. These location updates can be safely ignored since the chance of a
- // user actually being at these coordinates is quite low.
- if (location != null
- && !(location.getLongitude() == 0.0 && location.getLatitude() == 0.0)) {
+ public void onLocationChanged(@Nullable Location location) {
+ if (location != null) {
Slog.d(TAG, "onLocationChanged:"
+ " provider=" + location.getProvider()
+ " accuracy=" + location.getAccuracy()
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index b131ab66da9f..5e4f75cca10d 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -116,6 +116,14 @@ final class AccessibilityController {
return result;
}
+ /**
+ * Sets a callback for observing which windows are touchable for the purposes
+ * of accessibility on specified display.
+ *
+ * @param displayId The logical display id.
+ * @param callback The callback.
+ * @return {@code false} if display id is not valid or an embedded display.
+ */
public boolean setWindowsForAccessibilityCallbackLocked(int displayId,
WindowsForAccessibilityCallback callback) {
if (callback != null) {
@@ -129,7 +137,7 @@ final class AccessibilityController {
if (display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null) {
// The window observer of this embedded display had been set from
// window manager after setting its parent window.
- return true;
+ return false;
} else {
throw new IllegalStateException(
"Windows for accessibility callback of display "
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3853841b63ff..d0af6936e2c6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -299,10 +299,10 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.ReferrerIntent;
-import com.android.internal.R;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.XmlUtils;
import com.android.server.AttributeCache;
@@ -751,7 +751,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" launchedFromPackage="); pw.print(launchedFromPackage);
pw.print(" userId="); pw.println(mUserId);
pw.print(prefix); pw.print("app="); pw.println(app);
- pw.print(prefix); pw.println(intent.toInsecureStringWithClip());
+ pw.print(prefix); pw.println(intent.toInsecureString());
pw.print(prefix); pw.print("rootOfTask="); pw.print(isRootOfTask());
pw.print(" task="); pw.println(task);
pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
@@ -846,7 +846,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (intent == null) {
pw.println("null");
} else {
- pw.println(intent.toShortString(false, true, false, true));
+ pw.println(intent.toShortString(false, true, false, false));
}
}
}
@@ -1251,9 +1251,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// TODO(b/36505427): Maybe this call should be moved inside
// updateOverrideConfiguration()
task.updateOverrideConfigurationFromLaunchBounds();
- // Make sure override configuration is up-to-date before using to create window
- // controller.
- updateOverrideConfiguration();
+ // Make sure size-compat is up-to-date before using to create window controller.
+ updateSizeCompatMode();
task.addActivityToTop(this);
@@ -6488,7 +6487,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* density or bounds from its parent.
*/
boolean inSizeCompatMode() {
- if (!shouldUseSizeCompatMode()) {
+ if (mCompatDisplayInsets == null || !shouldUseSizeCompatMode()) {
return false;
}
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
@@ -6566,58 +6565,54 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
- private void updateOverrideConfiguration() {
- final Configuration overrideConfig = mTmpConfig;
- overrideConfig.setTo(getRequestedOverrideConfiguration());
-
- if (shouldUseSizeCompatMode()) {
- if (mCompatDisplayInsets != null) {
- // The override configuration is set only once in size compatibility mode.
- return;
- }
- final Configuration parentConfig = getParent().getConfiguration();
- if (!hasProcess() && !isConfigurationCompatible(parentConfig)) {
- // Don't compute when launching in fullscreen and the fixed orientation is not the
- // current orientation. It is more accurately to compute the override bounds from
- // the updated configuration after the fixed orientation is applied.
- return;
- }
-
- // Ensure the screen related fields are set. It is used to prevent activity relaunch
- // when moving between displays. For screenWidthDp and screenWidthDp, because they
- // are relative to bounds and density, they will be calculated in
- // {@link TaskRecord#computeConfigResourceOverrides} and the result will also be
- // relatively fixed.
- overrideConfig.colorMode = parentConfig.colorMode;
- overrideConfig.densityDpi = parentConfig.densityDpi;
- overrideConfig.screenLayout = parentConfig.screenLayout
- & (Configuration.SCREENLAYOUT_LONG_MASK
- | Configuration.SCREENLAYOUT_SIZE_MASK);
- // The smallest screen width is the short side of screen bounds. Because the bounds
- // and density won't be changed, smallestScreenWidthDp is also fixed.
- overrideConfig.smallestScreenWidthDp = parentConfig.smallestScreenWidthDp;
-
- // The role of CompatDisplayInsets is like the override bounds.
- final ActivityDisplay display = getDisplay();
- if (display != null && display.mDisplayContent != null) {
- mCompatDisplayInsets = new CompatDisplayInsets(display.mDisplayContent);
- }
- } else {
- // We must base this on the parent configuration, because we set our override
- // configuration's appBounds based on the result of this method. If we used our own
- // configuration, it would be influenced by past invocations.
- computeBounds(mTmpBounds, getParent().getWindowConfiguration().getAppBounds());
-
- if (mTmpBounds.equals(getRequestedOverrideBounds())) {
- // The bounds is not changed or the activity is resizable (both the 2 bounds are
- // empty).
- return;
- }
+ private void updateSizeCompatMode() {
+ if (mCompatDisplayInsets != null || !shouldUseSizeCompatMode()) {
+ // The override configuration is set only once in size compatibility mode.
+ return;
+ }
+ final Configuration parentConfig = getParent().getConfiguration();
+ if (!hasProcess() && !isConfigurationCompatible(parentConfig)) {
+ // Don't compute when launching in fullscreen and the fixed orientation is not the
+ // current orientation. It is more accurately to compute the override bounds from
+ // the updated configuration after the fixed orientation is applied.
+ return;
+ }
- overrideConfig.windowConfiguration.setBounds(mTmpBounds);
+ Configuration overrideConfig = getRequestedOverrideConfiguration();
+ final Configuration fullConfig = getConfiguration();
+
+ // Ensure the screen related fields are set. It is used to prevent activity relaunch
+ // when moving between displays. For screenWidthDp and screenWidthDp, because they
+ // are relative to bounds and density, they will be calculated in
+ // {@link TaskRecord#computeConfigResourceOverrides} and the result will also be
+ // relatively fixed.
+ overrideConfig.colorMode = fullConfig.colorMode;
+ overrideConfig.densityDpi = fullConfig.densityDpi;
+ overrideConfig.screenLayout = fullConfig.screenLayout
+ & (Configuration.SCREENLAYOUT_LONG_MASK
+ | Configuration.SCREENLAYOUT_SIZE_MASK);
+ // The smallest screen width is the short side of screen bounds. Because the bounds
+ // and density won't be changed, smallestScreenWidthDp is also fixed.
+ overrideConfig.smallestScreenWidthDp = fullConfig.smallestScreenWidthDp;
+ if (info.isFixedOrientation()) {
+ // lock rotation too. When in size-compat, onConfigurationChanged will watch for and
+ // apply runtime rotation changes.
+ overrideConfig.windowConfiguration.setRotation(
+ fullConfig.windowConfiguration.getRotation());
+ }
+
+ // The role of CompatDisplayInsets is like the override bounds.
+ final ActivityDisplay display = getDisplay();
+ if (display != null) {
+ mCompatDisplayInsets = new CompatDisplayInsets(display.mDisplayContent,
+ getWindowConfiguration().getBounds(),
+ getWindowConfiguration().tasksAreFloating());
}
+ }
- onRequestedOverrideConfigurationChanged(overrideConfig);
+ private void clearSizeCompatMode() {
+ mCompatDisplayInsets = null;
+ onRequestedOverrideConfigurationChanged(EMPTY);
}
@Override
@@ -6638,13 +6633,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
void resolveOverrideConfiguration(Configuration newParentConfiguration) {
+ Configuration resolvedConfig = getResolvedOverrideConfiguration();
if (mCompatDisplayInsets != null) {
resolveSizeCompatModeConfiguration(newParentConfiguration);
} else {
super.resolveOverrideConfiguration(newParentConfiguration);
+ applyAspectRatio(resolvedConfig.windowConfiguration.getBounds(),
+ newParentConfiguration.windowConfiguration.getAppBounds(),
+ newParentConfiguration.windowConfiguration.getBounds());
// If the activity has override bounds, the relative configuration (e.g. screen size,
// layout) needs to be resolved according to the bounds.
- if (task != null && !matchParentBounds()) {
+ if (task != null && !resolvedConfig.windowConfiguration.getBounds().isEmpty()) {
task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
newParentConfiguration);
}
@@ -6662,82 +6661,71 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* inheriting the parent bounds.
*/
private void resolveSizeCompatModeConfiguration(Configuration newParentConfiguration) {
+ super.resolveOverrideConfiguration(newParentConfiguration);
final Configuration resolvedConfig = getResolvedOverrideConfiguration();
final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
- final int parentRotation = newParentConfiguration.windowConfiguration.getRotation();
- final int parentOrientation = newParentConfiguration.orientation;
- int orientation = getConfiguration().orientation;
- if (orientation != parentOrientation && isConfigurationCompatible(newParentConfiguration)) {
- // The activity is compatible to apply the orientation change or it requests different
- // fixed orientation.
- orientation = parentOrientation;
- } else {
- if (!resolvedBounds.isEmpty()
- // The decor insets may be different according to the rotation.
- && getWindowConfiguration().getRotation() == parentRotation) {
- // Keep the computed resolved override configuration.
- return;
- }
- final int requestedOrientation = getRequestedConfigurationOrientation();
- if (requestedOrientation != ORIENTATION_UNDEFINED) {
- orientation = requestedOrientation;
- }
- }
+ Rect parentBounds = new Rect(newParentConfiguration.windowConfiguration.getBounds());
- super.resolveOverrideConfiguration(newParentConfiguration);
+ int orientation = getRequestedConfigurationOrientation();
+ if (orientation == ORIENTATION_UNDEFINED) {
+ orientation = newParentConfiguration.orientation;
+ }
+ int rotation = resolvedConfig.windowConfiguration.getRotation();
+ if (rotation == ROTATION_UNDEFINED) {
+ rotation = newParentConfiguration.windowConfiguration.getRotation();
+ }
- boolean useParentOverrideBounds = false;
- final Rect displayBounds = mTmpBounds;
+ // Use compat insets to lock width and height. We should not use the parent width and height
+ // because apps in compat mode should have a constant width and height. The compat insets
+ // are locked when the app is first launched and are never changed after that, so we can
+ // rely on them to contain the original and unchanging width and height of the app.
+ final Rect compatDisplayBounds = mTmpBounds;
+ mCompatDisplayInsets.getDisplayBoundsByRotation(compatDisplayBounds, rotation);
final Rect containingAppBounds = new Rect();
- if (task.handlesOrientationChangeFromDescendant()) {
- // Prefer to use the orientation which is determined by this activity to calculate
- // bounds because the parent will follow the requested orientation.
- mCompatDisplayInsets.getDisplayBoundsByOrientation(displayBounds, orientation);
- } else {
- // The parent hierarchy doesn't handle the orientation changes. This is usually because
- // the aspect ratio of display is close to square or the display rotation is fixed.
- // In this case, task will compute override bounds to fit the app with respect to the
- // requested orientation. So here we perform similar calculation to have consistent
- // bounds even the original parent hierarchies were changed.
- final int baseOrientation = task.getParent().getConfiguration().orientation;
- mCompatDisplayInsets.getDisplayBoundsByOrientation(displayBounds, baseOrientation);
- task.computeFullscreenBounds(containingAppBounds, this, displayBounds, baseOrientation);
- useParentOverrideBounds = !containingAppBounds.isEmpty();
- }
-
- // The offsets will be non-zero if the parent has override bounds.
- final int containingOffsetX = containingAppBounds.left;
- final int containingOffsetY = containingAppBounds.top;
- if (!useParentOverrideBounds) {
- containingAppBounds.set(displayBounds);
- }
- if (parentRotation != ROTATION_UNDEFINED) {
- // Ensure the container bounds won't overlap with the decors.
- TaskRecord.intersectWithInsetsIfFits(containingAppBounds, displayBounds,
- mCompatDisplayInsets.mNonDecorInsets[parentRotation]);
- }
-
- computeBounds(resolvedBounds, containingAppBounds);
- if (resolvedBounds.isEmpty()) {
- // Use the entire available bounds because there is no restriction.
- resolvedBounds.set(useParentOverrideBounds ? containingAppBounds : displayBounds);
- } else {
- // The offsets are included in width and height by {@link #computeBounds}, so we have to
- // restore it.
- resolvedBounds.left += containingOffsetX;
- resolvedBounds.top += containingOffsetY;
- }
+ mCompatDisplayInsets.getFrameByOrientation(containingAppBounds, orientation);
+
+ // Center containingAppBounds horizontally and aligned to top of parent. Both
+ // are usually the same unless the app was frozen with an orientation letterbox.
+ int left = compatDisplayBounds.left + compatDisplayBounds.width() / 2
+ - containingAppBounds.width() / 2;
+ resolvedBounds.set(left, compatDisplayBounds.top, left + containingAppBounds.width(),
+ compatDisplayBounds.top + containingAppBounds.height());
+
+ if (rotation != ROTATION_UNDEFINED) {
+ // Ensure the parent and container bounds won't overlap with insets.
+ TaskRecord.intersectWithInsetsIfFits(containingAppBounds, compatDisplayBounds,
+ mCompatDisplayInsets.mNonDecorInsets[rotation]);
+ TaskRecord.intersectWithInsetsIfFits(parentBounds, compatDisplayBounds,
+ mCompatDisplayInsets.mNonDecorInsets[rotation]);
+ }
+
+ applyAspectRatio(resolvedBounds, containingAppBounds, compatDisplayBounds);
+
+ // Center horizontally in parent and align to top of parent - this is a UX choice
+ left = parentBounds.left + parentBounds.width() / 2 - resolvedBounds.width() / 2;
+ resolvedBounds.set(left, parentBounds.top, left + resolvedBounds.width(),
+ parentBounds.top + resolvedBounds.height());
+
+ // We want to get as much of the app on the screen even if insets cover it. This is because
+ // insets change but an app's bounds are more permanent after launch. After computing insets
+ // and horizontally centering resolvedBounds, the resolvedBounds may end up outside parent
+ // bounds. This is okay only if the resolvedBounds exceed their parent on the bottom and
+ // right, because that is clipped when the final bounds are computed. To reach this state,
+ // we first try and push the app as much inside the parent towards the top and left (the
+ // min). The app may then end up outside the parent by going too far left and top, so we
+ // push it back into the parent by taking the max with parent left and top.
+ Rect fullParentBounds = newParentConfiguration.windowConfiguration.getBounds();
+ resolvedBounds.offsetTo(Math.max(fullParentBounds.left,
+ Math.min(fullParentBounds.right - resolvedBounds.width(), resolvedBounds.left)),
+ Math.max(fullParentBounds.top,
+ Math.min(fullParentBounds.bottom - resolvedBounds.height(),
+ resolvedBounds.top)));
+
+ // Use resolvedBounds to compute other override configurations such as appBounds
task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
mCompatDisplayInsets);
- // The horizontal inset included in width is not needed if the activity cannot fill the
- // parent, because the offset will be applied by {@link ActivityRecord#mSizeCompatBounds}.
- final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
- final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
- if (resolvedBounds.width() < parentAppBounds.width()) {
- resolvedBounds.right -= resolvedAppBounds.left;
- }
// Use parent orientation if it cannot be decided by bounds, so the activity can fit inside
// the parent bounds appropriately.
if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) {
@@ -6812,6 +6800,33 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
+ if (mCompatDisplayInsets != null) {
+ Configuration overrideConfig = getRequestedOverrideConfiguration();
+ // Adapt to changes in orientation locking. The app is still non-resizable, but
+ // it can change which orientation is fixed. If the fixed orientation changes,
+ // update the rotation used on the "compat" display
+ boolean wasFixedOrient =
+ overrideConfig.windowConfiguration.getRotation() != ROTATION_UNDEFINED;
+ int requestedOrient = getRequestedConfigurationOrientation();
+ if (requestedOrient != ORIENTATION_UNDEFINED
+ && requestedOrient != getConfiguration().orientation
+ // The task orientation depends on the top activity orientation, so it
+ // should match. If it doesn't, just wait until it does.
+ && requestedOrient == getParent().getConfiguration().orientation
+ && (overrideConfig.windowConfiguration.getRotation()
+ != getParent().getWindowConfiguration().getRotation())) {
+ overrideConfig.windowConfiguration.setRotation(
+ getParent().getWindowConfiguration().getRotation());
+ onRequestedOverrideConfigurationChanged(overrideConfig);
+ return;
+ } else if (wasFixedOrient && requestedOrient == ORIENTATION_UNDEFINED
+ && (overrideConfig.windowConfiguration.getRotation()
+ != ROTATION_UNDEFINED)) {
+ overrideConfig.windowConfiguration.setRotation(ROTATION_UNDEFINED);
+ onRequestedOverrideConfigurationChanged(overrideConfig);
+ return;
+ }
+ }
final int prevWinMode = getWindowingMode();
mTmpPrevBounds.set(getBounds());
super.onConfigurationChanged(newParentConfig);
@@ -6836,6 +6851,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mSizeCompatScale = 1f;
updateSurfacePosition();
}
+ } else if (overrideBounds.isEmpty()) {
+ mSizeCompatBounds = null;
+ mSizeCompatScale = 1f;
+ updateSurfacePosition();
}
adjustPinnedStackAndInitChangeTransitionIfNeeded(prevWinMode, getWindowingMode());
@@ -6854,7 +6873,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (visible) {
// It may toggle the UI for user to restart the size compatibility mode activity.
display.handleActivitySizeCompatModeIfNeeded(this);
- } else if (shouldUseSizeCompatMode()) {
+ } else if (mCompatDisplayInsets != null) {
// The override changes can only be obtained from display, because we don't have the
// difference of full configuration in each hierarchy.
final int displayChanges = display.getLastOverrideConfigurationChanges();
@@ -6918,22 +6937,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/**
- * Computes the bounds to fit the Activity within the bounds of the {@link Configuration}.
+ * Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is
+ * made to outBounds.
*/
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
- private void computeBounds(Rect outBounds, Rect containingAppBounds) {
- outBounds.setEmpty();
+ private void applyAspectRatio(Rect outBounds, Rect containingAppBounds,
+ Rect containingBounds) {
final float maxAspectRatio = info.maxAspectRatio;
final ActivityStack stack = getActivityStack();
final float minAspectRatio = info.minAspectRatio;
- if (task == null || stack == null || task.inMultiWindowMode()
+ if (task == null || stack == null || (inMultiWindowMode() && !shouldUseSizeCompatMode())
|| (maxAspectRatio == 0 && minAspectRatio == 0)
|| isInVrUiMode(getConfiguration())) {
- // We don't set override configuration if that activity task isn't fullscreen. I.e. the
- // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for
- // the activity. This is indicated by an empty {@link outBounds}. We also don't set it
- // if we are in VR mode.
+ // We don't enforce aspect ratio if the activity task is in multiwindow unless it
+ // is in size-compat mode. We also don't set it if we are in VR mode.
return;
}
@@ -6991,12 +7009,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (containingAppWidth <= activityWidth && containingAppHeight <= activityHeight) {
// The display matches or is less than the activity aspect ratio, so nothing else to do.
- // Return the existing bounds. If this method is running for the first time,
- // {@link #getRequestedOverrideBounds()} will be empty (representing no override). If
- // the method has run before, then effect of {@link #getRequestedOverrideBounds()} will
- // already have been applied to the value returned from {@link getConfiguration}. Refer
- // to {@link TaskRecord#computeConfigResourceOverrides()}.
- outBounds.set(getRequestedOverrideBounds());
return;
}
@@ -7004,7 +7016,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Also account for the left / top insets (e.g. from display cutouts), which will be clipped
// away later in {@link TaskRecord#computeConfigResourceOverrides()}. Otherwise, the app
// bounds would end up too small.
- outBounds.set(0, 0, activityWidth + containingAppBounds.left,
+ outBounds.set(containingBounds.left, containingBounds.top,
+ activityWidth + containingAppBounds.left,
activityHeight + containingAppBounds.top);
}
@@ -7067,7 +7080,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mLastReportedDisplayId = newDisplayId;
}
// TODO(b/36505427): Is there a better place to do this?
- updateOverrideConfiguration();
+ updateSizeCompatMode();
// Short circuit: if the two full configurations are equal (the common case), then there is
// nothing to do. We test the full configuration instead of the global and merged override
@@ -7358,13 +7371,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Reset the existing override configuration so it can be updated according to the latest
// configuration.
- getRequestedOverrideConfiguration().unset();
- getResolvedOverrideConfiguration().unset();
- mCompatDisplayInsets = null;
+ clearSizeCompatMode();
if (visible) {
// Configuration will be ensured when becoming visible, so if it is already visible,
// then the manual update is needed.
- updateOverrideConfiguration();
+ updateSizeCompatMode();
}
if (!attachedToProcess()) {
@@ -7743,8 +7754,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* compatibility mode activity compute the configuration without relying on its current display.
*/
static class CompatDisplayInsets {
- final int mDisplayWidth;
- final int mDisplayHeight;
+ private final int mDisplayWidth;
+ private final int mDisplayHeight;
+ private final int mWidth;
+ private final int mHeight;
/**
* The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It
@@ -7758,9 +7771,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
final Rect[] mStableInsets = new Rect[4];
- CompatDisplayInsets(DisplayContent display) {
+ /**
+ * Sets bounds to {@link TaskRecord} bounds. For apps in freeform, the task bounds are the
+ * parent bounds from the app's perspective. No insets because within a window.
+ */
+ CompatDisplayInsets(DisplayContent display, Rect activityBounds, boolean isFloating) {
mDisplayWidth = display.mBaseDisplayWidth;
mDisplayHeight = display.mBaseDisplayHeight;
+ mWidth = activityBounds.width();
+ mHeight = activityBounds.height();
+ if (isFloating) {
+ Rect emptyRect = new Rect();
+ for (int rotation = 0; rotation < 4; rotation++) {
+ mNonDecorInsets[rotation] = emptyRect;
+ mStableInsets[rotation] = emptyRect;
+ }
+ return;
+ }
final DisplayPolicy policy = display.getDisplayPolicy();
for (int rotation = 0; rotation < 4; rotation++) {
mNonDecorInsets[rotation] = new Rect();
@@ -7783,9 +7810,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
outBounds.set(0, 0, dw, dh);
}
- void getDisplayBoundsByOrientation(Rect outBounds, int orientation) {
- final int longSide = Math.max(mDisplayWidth, mDisplayHeight);
- final int shortSide = Math.min(mDisplayWidth, mDisplayHeight);
+ void getFrameByOrientation(Rect outBounds, int orientation) {
+ final int longSide = Math.max(mWidth, mHeight);
+ final int shortSide = Math.min(mWidth, mHeight);
final boolean isLandscape = orientation == ORIENTATION_LANDSCAPE;
outBounds.set(0, 0, isLandscape ? longSide : shortSide,
isLandscape ? shortSide : longSide);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 887ece544d3e..191e87056c05 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2247,7 +2247,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// Complete + brief == give a summary. Isn't that obvious?!?
if (lastTask.intent != null) {
pw.print(prefix); pw.print(" ");
- pw.println(lastTask.intent.toInsecureStringWithClip());
+ pw.println(lastTask.intent.toInsecureString());
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 14df505e8986..014b682ff8d6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -54,6 +54,7 @@ import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
@@ -569,6 +570,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean mSupportsPictureInPicture;
boolean mSupportsMultiDisplay;
boolean mForceResizableActivities;
+ boolean mSizeCompatFreeform;
final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
@@ -744,6 +746,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
final boolean forceResizable = Settings.Global.getInt(
resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
+ final boolean sizeCompatFreeform = Settings.Global.getInt(
+ resolver, DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
// Transfer any global setting for forcing RTL layout, into a System Property
DisplayProperties.debug_force_rtl(forceRtl);
@@ -757,6 +761,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
mForceResizableActivities = forceResizable;
+ mSizeCompatFreeform = sizeCompatFreeform;
final boolean multiWindowFormEnabled = freeformWindowManagement
|| supportsSplitScreenMultiWindow
|| supportsPictureInPicture
@@ -3393,6 +3398,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (stack.inFreeformWindowingMode()) {
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ } else if (!mSizeCompatFreeform) {
+ throw new IllegalStateException("Size-compat windows are currently not"
+ + "freeform-enabled");
} else if (stack.getParent().inFreeformWindowingMode()) {
// If the window is on a freeform display, set it to undefined. It will be
// resolved to freeform and it can adjust windowing mode when the display mode
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index bc95481dcee9..11f09d0f1343 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -56,8 +56,6 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
if (mIsImeLayoutDrawn && mShowImeRunner != null) {
// Show IME if InputMethodService requested to be shown and it's layout has finished.
mShowImeRunner.run();
- mIsImeLayoutDrawn = false;
- mShowImeRunner = null;
}
}
@@ -74,10 +72,19 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
mDisplayContent.mInputMethodTarget.showInsets(
WindowInsets.Type.ime(), true /* fromIme */);
}
- mImeTargetFromIme = null;
+ abortShowImePostLayout();
};
}
+ /**
+ * Abort any pending request to show IME post layout.
+ */
+ void abortShowImePostLayout() {
+ mImeTargetFromIme = null;
+ mIsImeLayoutDrawn = false;
+ mShowImeRunner = null;
+ }
+
private boolean isImeTargetFromDisplayContentAndImeSame() {
// IMMS#mLastImeTargetWindow always considers focused window as
// IME target, however DisplayContent#computeImeTarget() can compute
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 9712277495df..31145deb449e 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -43,10 +43,8 @@ import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.Build;
import android.util.Slog;
import android.view.Gravity;
import android.view.View;
@@ -65,15 +63,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskLaunchParamsModifier" : TAG_ATM;
private static final boolean DEBUG = false;
- // A mask for SUPPORTS_SCREEN that indicates the activity supports resize.
- private static final int SUPPORTS_SCREEN_RESIZEABLE_MASK =
- ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES
- | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
- | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
- | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS
- | ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES
- | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
-
// Screen size of Nexus 5x
private static final int DEFAULT_PORTRAIT_PHONE_WIDTH_DP = 412;
private static final int DEFAULT_PORTRAIT_PHONE_HEIGHT_DP = 732;
@@ -253,10 +242,9 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
if (display.inFreeformWindowingMode()) {
if (launchMode == WINDOWING_MODE_PINNED) {
if (DEBUG) appendLog("picture-in-picture");
- } else if (isTaskForcedMaximized(root)) {
- // We're launching an activity that probably can't handle resizing nicely, so force
- // it to be maximized even someone suggests launching it in freeform using launch
- // options.
+ } else if (!mSupervisor.mService.mSizeCompatFreeform && !root.isResizeable()) {
+ // We're launching an activity in size-compat mode and they aren't allowed in
+ // freeform, so force it to be maximized.
launchMode = WINDOWING_MODE_FULLSCREEN;
outParams.mBounds.setEmpty();
if (DEBUG) appendLog("forced-maximize");
@@ -460,28 +448,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier {
}
/**
- * Returns if task is forced to maximize.
- *
- * There are several cases where we force a task to maximize:
- * 1) Root activity is targeting pre-Donut, which by default can't handle multiple screen
- * densities, so resizing will likely cause issues;
- * 2) Root activity doesn't declare any flag that it supports any screen density, so resizing
- * may also cause issues;
- * 3) Root activity is not resizeable, for which we shouldn't allow user resize it.
- *
- * @param root the root activity to check against.
- * @return {@code true} if it should be forced to maximize; {@code false} otherwise.
- */
- private boolean isTaskForcedMaximized(@NonNull ActivityRecord root) {
- if (root.info.applicationInfo.targetSdkVersion < Build.VERSION_CODES.DONUT
- || (root.info.applicationInfo.flags & SUPPORTS_SCREEN_RESIZEABLE_MASK) == 0) {
- return true;
- }
-
- return !root.isResizeable();
- }
-
- /**
* Resolves activity requested orientation to 4 categories:
* 1) {@link ActivityInfo#SCREEN_ORIENTATION_LOCKED} indicating app wants to lock down
* orientation;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index ed07f30fc9dc..e1123fa05b7d 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -126,6 +126,11 @@ class TaskPositioningController {
synchronized (mService.mGlobalLock) {
final Task task = displayContent.findTaskForResizePoint(x, y);
if (task != null) {
+ if (!task.isResizeable()) {
+ // The task is not resizable, so don't do anything when the user drags the
+ // the resize handles.
+ return;
+ }
if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
task.preserveOrientationOnResize(), x, y)) {
return;
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 299b32cce039..89499d695dd0 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -519,18 +519,7 @@ class TaskRecord extends ConfigurationContainer {
mAtmService.deferWindowLayout();
try {
- if (!isResizeable()) {
- Slog.w(TAG, "resizeTask: task " + this + " not resizeable.");
- return true;
- }
-
- // If this is a forced resize, let it go through even if the bounds is not changing,
- // as we might need a relayout due to surface size change (to/from fullscreen).
final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
- if (equivalentRequestedOverrideBounds(bounds) && !forced) {
- // Nothing to do here...
- return true;
- }
if (mTask == null) {
// Task doesn't exist in window manager yet (e.g. was restored from recents).
@@ -2065,7 +2054,10 @@ class TaskRecord extends ConfigurationContainer {
} else {
// Apply the given non-decor and stable insets to calculate the corresponding bounds
// for screen size of configuration.
- final int rotation = parentConfig.windowConfiguration.getRotation();
+ int rotation = inOutConfig.windowConfiguration.getRotation();
+ if (rotation == ROTATION_UNDEFINED) {
+ rotation = parentConfig.windowConfiguration.getRotation();
+ }
if (rotation != ROTATION_UNDEFINED && compatInsets != null) {
mTmpNonDecorBounds.set(bounds);
mTmpStableBounds.set(bounds);
@@ -2379,14 +2371,14 @@ class TaskRecord extends ConfigurationContainer {
if (intent != null) {
StringBuilder sb = new StringBuilder(128);
sb.append(prefix); sb.append("intent={");
- intent.toShortString(sb, false, true, false, true);
+ intent.toShortString(sb, false, true, false, false);
sb.append('}');
pw.println(sb.toString());
}
if (affinityIntent != null) {
StringBuilder sb = new StringBuilder(128);
sb.append(prefix); sb.append("affinityIntent={");
- affinityIntent.toShortString(sb, false, true, false, true);
+ affinityIntent.toShortString(sb, false, true, false, false);
sb.append('}');
pw.println(sb.toString());
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 0cb4826fdfc3..d2249724ae6e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -216,7 +216,7 @@ public abstract class WindowManagerInternal {
*
* @param displayId The logical display id.
* @param callbacks The callbacks to invoke.
- * @return {@code false} if display id is not valid.
+ * @return {@code false} if display id is not valid or an embedded display.
*/
public abstract boolean setMagnificationCallbacks(int displayId,
@Nullable MagnificationCallbacks callbacks);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 63ce1b1c7ad7..085b0f66702a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -39,6 +39,7 @@ import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURES_EX
import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP;
import static android.provider.DeviceConfig.WindowManager.KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -717,6 +718,8 @@ public class WindowManagerService extends IWindowManager.Stub
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT);
private final Uri mForceResizableUri = Settings.Global.getUriFor(
DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
+ private final Uri mSizeCompatFreeformUri = Settings.Global.getUriFor(
+ DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM);
public SettingsObserver() {
super(new Handler());
@@ -737,6 +740,8 @@ public class WindowManagerService extends IWindowManager.Stub
UserHandle.USER_ALL);
resolver.registerContentObserver(mFreeformWindowUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(mSizeCompatFreeformUri, false, this,
+ UserHandle.USER_ALL);
}
@Override
@@ -775,6 +780,11 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
+ if (mSizeCompatFreeformUri.equals(uri)) {
+ updateSizeCompatFreeform();
+ return;
+ }
+
@UpdateAnimationScaleMode
final int mode;
if (mWindowAnimationScaleUri.equals(uri)) {
@@ -844,6 +854,14 @@ public class WindowManagerService extends IWindowManager.Stub
mAtmService.mForceResizableActivities = forceResizable;
}
+
+ void updateSizeCompatFreeform() {
+ ContentResolver resolver = mContext.getContentResolver();
+ final boolean sizeCompatFreeform = Settings.Global.getInt(resolver,
+ DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM, 0) != 0;
+
+ mAtmService.mSizeCompatFreeform = sizeCompatFreeform;
+ }
}
PowerManager mPowerManager;
@@ -7331,6 +7349,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc != null && dc.mInputMethodTarget != null) {
+ // If there was a pending IME show(), reset it as IME has been
+ // requested to be hidden.
+ dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
dc.mInputMethodTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b9cf29ad7790..991241dd645f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2319,29 +2319,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
final Region region = inputWindowHandle.touchableRegion;
setTouchableRegionCropIfNeeded(inputWindowHandle);
- final Rect appOverrideBounds = mActivityRecord != null
- ? mActivityRecord.getResolvedOverrideBounds() : null;
- if (appOverrideBounds != null && !appOverrideBounds.isEmpty()) {
- // There may have touchable letterboxes around the activity, so in order to let the
- // letterboxes are able to receive touch event and slip to activity, the activity with
- // compatibility bounds cannot occupy full screen touchable region.
- if (modal) {
- // A modal window uses the whole compatibility bounds.
- flags |= FLAG_NOT_TOUCH_MODAL;
- mTmpRect.set(0, 0, appOverrideBounds.width(), appOverrideBounds.height());
- } else {
- // Non-modal uses the application based frame.
- mTmpRect.set(mWindowFrames.mCompatFrame);
- }
- // The offset of compatibility bounds is applied to surface of {@link #ActivityRecord}
- // and frame, so it is unnecessary to translate twice in surface based coordinates.
- final int surfaceOffsetX = mActivityRecord.hasSizeCompatBounds()
- ? mActivityRecord.getBounds().left : 0;
- mTmpRect.offset(surfaceOffsetX - mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
- region.set(mTmpRect);
- return flags;
- }
-
if (modal && mActivityRecord != null) {
// Limit the outer touch to the activity stack region.
flags |= FLAG_NOT_TOUCH_MODAL;
@@ -2398,6 +2375,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Translate to surface based coordinates.
region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
+ // TODO(b/139804591): sizecompat layout needs to be reworked. Currently mFrame is post-
+ // scaling but the existing logic doesn't expect that. The result is that the already-
+ // scaled region ends up getting sent to surfaceflinger which then applies the scale
+ // (again). Until this is resolved, apply an inverse-scale here.
+ if (mActivityRecord != null && mActivityRecord.hasSizeCompatBounds()
+ && mGlobalScale != 1.f) {
+ region.scale(mInvGlobalScale);
+ }
+
return flags;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 59996ccd5530..5a1d552bf325 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -62,4 +62,6 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
String packageName, boolean hasGrant) {
return false;
}
+
+ public void setLocationEnabled(ComponentName who, boolean locationEnabled) {}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 453d05da8377..5e49c7a9e5d1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4183,7 +4183,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private boolean passwordQualityInvocationOrderCheckEnabled(String packageName, int userId) {
try {
return mIPlatformCompat.isChangeEnabledByPackageName(ADMIN_APP_PASSWORD_COMPLEXITY,
- packageName);
+ packageName, userId);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
}
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 1ca96ed80e5e..08cdbfc55cfe 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,10 +1,14 @@
java_library_static {
name: "services.net",
- srcs: ["java/**/*.java"],
+ srcs: [
+ ":tethering-services-srcs",
+ "java/**/*.java",
+ ],
static_libs: [
"dnsresolver_aidl_interface-V2-java",
"netd_aidl_interface-java",
"networkstack-client",
+ "tethering-client",
],
}
@@ -17,3 +21,17 @@ filegroup {
"java/android/net/netlink/*.java",
],
}
+
+filegroup {
+ name: "services-tethering-shared-srcs",
+ srcs: [
+ ":framework-annotations",
+ "java/android/net/ConnectivityModuleConnector.java",
+ "java/android/net/NetworkStackClient.java",
+ "java/android/net/ip/InterfaceController.java",
+ "java/android/net/util/InterfaceParams.java",
+ "java/android/net/util/NetdService.java",
+ "java/android/net/util/NetworkConstants.java",
+ "java/android/net/util/SharedLog.java"
+ ],
+}
diff --git a/services/net/java/android/net/netlink/InetDiagMessage.java b/services/net/java/android/net/netlink/InetDiagMessage.java
index 31a2556f2041..ca07630d3e63 100644
--- a/services/net/java/android/net/netlink/InetDiagMessage.java
+++ b/services/net/java/android/net/netlink/InetDiagMessage.java
@@ -26,6 +26,7 @@ import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.NETLINK_INET_DIAG;
+import android.annotation.Nullable;
import android.net.util.SocketUtils;
import android.system.ErrnoException;
import android.util.Log;
@@ -53,7 +54,35 @@ public class InetDiagMessage extends NetlinkMessage {
private static final int TIMEOUT_MS = 500;
public static byte[] InetDiagReqV2(int protocol, InetSocketAddress local,
- InetSocketAddress remote, int family, short flags) {
+ InetSocketAddress remote, int family, short flags) {
+ return InetDiagReqV2(protocol, local, remote, family, flags, 0 /* pad */,
+ 0 /* idiagExt */, StructInetDiagReqV2.INET_DIAG_REQ_V2_ALL_STATES);
+ }
+
+ /**
+ * Construct an inet_diag_req_v2 message. This method will throw {@code NullPointerException}
+ * if local and remote are not both null or both non-null.
+ *
+ * @param protocol the request protocol type. This should be set to one of IPPROTO_TCP,
+ * IPPROTO_UDP, or IPPROTO_UDPLITE.
+ * @param local local socket address of the target socket. This will be packed into a
+ * {@Code StructInetDiagSockId}. Request to diagnose for all sockets if both of
+ * local or remote address is null.
+ * @param remote remote socket address of the target socket. This will be packed into a
+ * {@Code StructInetDiagSockId}. Request to diagnose for all sockets if both of
+ * local or remote address is null.
+ * @param family the ip family of the request message. This should be set to either AF_INET or
+ * AF_INET6 for IPv4 or IPv6 sockets respectively.
+ * @param flags message flags. See &lt;linux_src&gt;/include/uapi/linux/netlink.h.
+ * @param pad for raw socket protocol specification.
+ * @param idiagExt a set of flags defining what kind of extended information to report.
+ * @param state a bit mask that defines a filter of socket states.
+ *
+ * @return bytes array representation of the message
+ **/
+ public static byte[] InetDiagReqV2(int protocol, @Nullable InetSocketAddress local,
+ @Nullable InetSocketAddress remote, int family, short flags, int pad, int idiagExt,
+ int state) throws NullPointerException {
final byte[] bytes = new byte[StructNlMsgHdr.STRUCT_SIZE + StructInetDiagReqV2.STRUCT_SIZE];
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
byteBuffer.order(ByteOrder.nativeOrder());
@@ -63,9 +92,9 @@ public class InetDiagMessage extends NetlinkMessage {
nlMsgHdr.nlmsg_type = SOCK_DIAG_BY_FAMILY;
nlMsgHdr.nlmsg_flags = flags;
nlMsgHdr.pack(byteBuffer);
+ final StructInetDiagReqV2 inetDiagReqV2 =
+ new StructInetDiagReqV2(protocol, local, remote, family, pad, idiagExt, state);
- final StructInetDiagReqV2 inetDiagReqV2 = new StructInetDiagReqV2(protocol, local, remote,
- family);
inetDiagReqV2.pack(byteBuffer);
return bytes;
}
diff --git a/services/net/java/android/net/netlink/StructInetDiagReqV2.java b/services/net/java/android/net/netlink/StructInetDiagReqV2.java
index 49a93258e714..2268113044d5 100644
--- a/services/net/java/android/net/netlink/StructInetDiagReqV2.java
+++ b/services/net/java/android/net/netlink/StructInetDiagReqV2.java
@@ -16,10 +16,10 @@
package android.net.netlink;
-import static java.nio.ByteOrder.BIG_ENDIAN;
+import android.annotation.Nullable;
+
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
/**
* struct inet_diag_req_v2
@@ -40,41 +40,58 @@ import java.nio.ByteOrder;
public class StructInetDiagReqV2 {
public static final int STRUCT_SIZE = 8 + StructInetDiagSockId.STRUCT_SIZE;
- private final byte sdiag_family;
- private final byte sdiag_protocol;
- private final StructInetDiagSockId id;
- private final int INET_DIAG_REQ_V2_ALL_STATES = (int) 0xffffffff;
-
+ private final byte mSdiagFamily;
+ private final byte mSdiagProtocol;
+ private final byte mIdiagExt;
+ private final byte mPad;
+ private final StructInetDiagSockId mId;
+ private final int mState;
+ public static final int INET_DIAG_REQ_V2_ALL_STATES = (int) 0xffffffff;
public StructInetDiagReqV2(int protocol, InetSocketAddress local, InetSocketAddress remote,
- int family) {
- sdiag_family = (byte) family;
- sdiag_protocol = (byte) protocol;
- id = new StructInetDiagSockId(local, remote);
+ int family) {
+ this(protocol, local, remote, family, 0 /* pad */, 0 /* extension */,
+ INET_DIAG_REQ_V2_ALL_STATES);
+ }
+
+ public StructInetDiagReqV2(int protocol, @Nullable InetSocketAddress local,
+ @Nullable InetSocketAddress remote, int family, int pad, int extension, int state)
+ throws NullPointerException {
+ mSdiagFamily = (byte) family;
+ mSdiagProtocol = (byte) protocol;
+ // Request for all sockets if no specific socket is requested. Specify the local and remote
+ // socket address information for target request socket.
+ if ((local == null) != (remote == null)) {
+ throw new NullPointerException("Local and remote must be both null or both non-null");
+ }
+ mId = ((local != null && remote != null) ? new StructInetDiagSockId(local, remote) : null);
+ mPad = (byte) pad;
+ mIdiagExt = (byte) extension;
+ mState = state;
}
public void pack(ByteBuffer byteBuffer) {
// The ByteOrder must have already been set by the caller.
- byteBuffer.put((byte) sdiag_family);
- byteBuffer.put((byte) sdiag_protocol);
- byteBuffer.put((byte) 0);
- byteBuffer.put((byte) 0);
- byteBuffer.putInt(INET_DIAG_REQ_V2_ALL_STATES);
- id.pack(byteBuffer);
+ byteBuffer.put((byte) mSdiagFamily);
+ byteBuffer.put((byte) mSdiagProtocol);
+ byteBuffer.put((byte) mIdiagExt);
+ byteBuffer.put((byte) mPad);
+ byteBuffer.putInt(mState);
+ if (mId != null) mId.pack(byteBuffer);
}
@Override
public String toString() {
- final String familyStr = NetlinkConstants.stringForAddressFamily(sdiag_family);
- final String protocolStr = NetlinkConstants.stringForAddressFamily(sdiag_protocol);
+ final String familyStr = NetlinkConstants.stringForAddressFamily(mSdiagFamily);
+ final String protocolStr = NetlinkConstants.stringForAddressFamily(mSdiagProtocol);
return "StructInetDiagReqV2{ "
+ "sdiag_family{" + familyStr + "}, "
+ "sdiag_protocol{" + protocolStr + "}, "
- + "idiag_ext{" + 0 + ")}, "
- + "pad{" + 0 + "}, "
- + "idiag_states{" + Integer.toHexString(INET_DIAG_REQ_V2_ALL_STATES) + "}, "
- + id.toString()
+ + "idiag_ext{" + mIdiagExt + ")}, "
+ + "pad{" + mPad + "}, "
+ + "idiag_states{" + Integer.toHexString(mState) + "}, "
+ + ((mId != null) ? mId.toString() : "inet_diag_sockid=null")
+ "}";
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 7b7b8e6c628a..9e7b80567263 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -85,6 +85,7 @@ import androidx.test.filters.FlakyTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.usage.AppStandbyInternal;
import org.junit.After;
import org.junit.Before;
@@ -108,7 +109,7 @@ public class AlarmManagerServiceTest {
private long mAppStandbyWindow;
private AlarmManagerService mService;
- private UsageStatsManagerInternal.AppIdleStateChangeListener mAppStandbyListener;
+ private AppStandbyInternal.AppIdleStateChangeListener mAppStandbyListener;
private AlarmManagerService.ChargingReceiver mChargingReceiver;
@Mock
private ContentResolver mMockResolver;
@@ -119,6 +120,8 @@ public class AlarmManagerServiceTest {
@Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock
+ private AppStandbyInternal mAppStandbyInternal;
+ @Mock
private AppStateTracker mAppStateTracker;
@Mock
private AlarmManagerService.ClockReceiver mClockReceiver;
@@ -257,6 +260,8 @@ public class AlarmManagerServiceTest {
doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
doReturn(null)
.when(() -> LocalServices.getService(DeviceIdleInternal.class));
+ doReturn(mAppStandbyInternal).when(
+ () -> LocalServices.getService(AppStandbyInternal.class));
doReturn(mUsageStatsManagerInternal).when(
() -> LocalServices.getService(UsageStatsManagerInternal.class));
when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
@@ -289,9 +294,9 @@ public class AlarmManagerServiceTest {
assertEquals(0, mService.mConstants.MIN_FUTURITY);
assertEquals(0, mService.mConstants.MIN_INTERVAL);
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
- ArgumentCaptor<UsageStatsManagerInternal.AppIdleStateChangeListener> captor =
- ArgumentCaptor.forClass(UsageStatsManagerInternal.AppIdleStateChangeListener.class);
- verify(mUsageStatsManagerInternal).addAppIdleStateChangeListener(captor.capture());
+ ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
+ ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class);
+ verify(mAppStandbyInternal).addListener(captor.capture());
mAppStandbyListener = captor.getValue();
ArgumentCaptor<AlarmManagerService.ChargingReceiver> chargingReceiverCaptor =
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 80d11290fdd4..1f4656ae54a4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -45,7 +45,6 @@ import android.app.IActivityManager;
import android.app.IUidObserver;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -70,6 +69,8 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.server.AppStateTracker.Listener;
+import com.android.server.usage.AppStandbyInternal;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import org.junit.Before;
import org.junit.Test;
@@ -128,8 +129,8 @@ public class AppStateTrackerTest {
}
@Override
- UsageStatsManagerInternal injectUsageStatsManagerInternal() {
- return mMockUsageStatsManagerInternal;
+ AppStandbyInternal injectAppStandbyInternal() {
+ return mMockAppStandbyInternal;
}
@Override
@@ -175,7 +176,7 @@ public class AppStateTrackerTest {
private PowerManagerInternal mMockPowerManagerInternal;
@Mock
- private UsageStatsManagerInternal mMockUsageStatsManagerInternal;
+ private AppStandbyInternal mMockAppStandbyInternal;
private MockContentResolver mMockContentResolver;
@@ -271,7 +272,7 @@ public class AppStateTrackerTest {
verify(mMockContext).registerReceiver(
receiverCaptor.capture(), any(IntentFilter.class));
- verify(mMockUsageStatsManagerInternal).addAppIdleStateChangeListener(
+ verify(mMockAppStandbyInternal).addListener(
appIdleStateChangeListenerCaptor.capture());
mIUidObserver = uidObserverArgumentCaptor.getValue();
diff --git a/services/tests/servicestests/res/raw/apex_test.apex b/services/tests/servicestests/res/raw/apex_test.apex
new file mode 100644
index 000000000000..19b1c5e2e1c6
--- /dev/null
+++ b/services/tests/servicestests/res/raw/apex_test.apex
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
index 4a9dd9776a1b..0605d9e18069 100644
--- a/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/DynamicSystemServiceTest.java
@@ -36,7 +36,7 @@ public class DynamicSystemServiceTest extends AndroidTestCase {
public void test1() {
assertTrue("dynamic_system service available", mService != null);
try {
- mService.startInstallation(1 << 20, 8 << 30);
+ mService.startInstallation("userdata", 8L << 30, false);
fail("DynamicSystemService did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected
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 5c2ad94720f0..29a8dada7d89 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -126,6 +126,22 @@ public class AudioDeviceBrokerTest {
doTestConnectionDisconnectionReconnection(AudioService.BECOMING_NOISY_DELAY_MS / 2);
}
+ /**
+ * Verify connecting an A2DP sink will call into AudioService to unmute media
+ */
+ @Test
+ public void testA2dpConnectionUnmutesMedia() throws Exception {
+ Log.i(TAG, "testA2dpConnectionUnmutesMedia");
+ Assert.assertNotNull("invalid null BT device", mFakeBtDevice);
+
+ mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
+ BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1);
+ Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
+ verify(mMockAudioService, times(1)).postAccessoryPlugMediaUnmute(
+ ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
+
+ }
+
private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection)
throws Exception {
when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC))
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index 16176c029f8c..8c2d172cd2da 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -17,11 +17,13 @@
package com.android.server.locksettings;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import static junit.framework.Assert.assertEquals;
-import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.never;
@@ -34,6 +36,8 @@ import static java.io.FileDescriptor.in;
import static java.io.FileDescriptor.out;
import android.app.ActivityManager;
+import android.app.admin.PasswordMetrics;
+import android.app.admin.PasswordPolicy;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
@@ -96,8 +100,7 @@ public class LockSettingsShellCommandTest {
assertEquals(-1, mCommand.exec(mBinder, in, out, err,
new String[] { "set-pin", "--old", "1234" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils, never()).setLockCredential(any(), any(),
- anyInt(), anyBoolean());
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
}
@Test
@@ -109,6 +112,8 @@ public class LockSettingsShellCommandTest {
PASSWORD_QUALITY_NUMERIC);
when(mLockPatternUtils.checkCredential(
LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_NUMERIC));
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "set-pin", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
@@ -119,6 +124,23 @@ public class LockSettingsShellCommandTest {
}
@Test
+ public void testChangePin_nonCompliant() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+ PASSWORD_QUALITY_NUMERIC);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_ALPHABETIC));
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-pin", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
+ }
+
+ @Test
public void testChangePin_noLockScreen() throws Exception {
when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
@@ -137,16 +159,35 @@ public class LockSettingsShellCommandTest {
PASSWORD_QUALITY_ALPHABETIC);
when(mLockPatternUtils.checkCredential(
LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_ALPHABETIC));
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
- new String[] { "set-password", "--old", "1234", "4321" },
+ new String[] { "set-password", "--old", "1234", "abcd" },
mShellCallback, mResultReceiver));
verify(mLockPatternUtils).setLockCredential(
- LockscreenCredential.createPassword("4321"),
+ LockscreenCredential.createPassword("abcd"),
LockscreenCredential.createPassword("1234"),
mUserId);
}
@Test
+ public void testChangePassword_nonCompliant() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+ PASSWORD_QUALITY_ALPHABETIC);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_COMPLEX));
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-password", "--old", "1234", "weakpassword" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
+ }
+
+ @Test
public void testChangePassword_noLockScreen() throws Exception {
when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
@@ -164,6 +205,8 @@ public class LockSettingsShellCommandTest {
when(mLockPatternUtils.checkCredential(
LockscreenCredential.createPattern(stringToPattern("1234")),
mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_SOMETHING));
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "set-pattern", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
@@ -174,6 +217,22 @@ public class LockSettingsShellCommandTest {
}
@Test
+ public void testChangePattern_nonCompliant() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPattern(stringToPattern("1234")),
+ mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_NUMERIC));
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-pattern", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
+ }
+
+ @Test
public void testChangePattern_noLockScreen() throws Exception {
when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
@@ -191,6 +250,8 @@ public class LockSettingsShellCommandTest {
when(mLockPatternUtils.checkCredential(
LockscreenCredential.createPattern(stringToPattern("1234")),
mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_UNSPECIFIED));
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "clear", "--old", "1234" },
mShellCallback, mResultReceiver));
@@ -200,7 +261,29 @@ public class LockSettingsShellCommandTest {
mUserId);
}
+ @Test
+ public void testClear_nonCompliant() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
+ when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPattern(stringToPattern("1234")),
+ mUserId, null)).thenReturn(true);
+ when(mLockPatternUtils.getRequestedPasswordMetrics(mUserId))
+ .thenReturn(metricsForAdminQuality(PASSWORD_QUALITY_SOMETHING));
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "clear", "--old", "1234" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(), anyInt());
+ }
+
private List<LockPatternView.Cell> stringToPattern(String str) {
return LockPatternUtils.byteArrayToPattern(str.getBytes());
}
+
+ private PasswordMetrics metricsForAdminQuality(int quality) {
+ PasswordPolicy policy = new PasswordPolicy();
+ policy.quality = quality;
+ return policy.getMinMetrics();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 8a489047f179..46db97834dae 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -122,6 +122,7 @@ import android.os.RemoteException;
import android.os.SimpleClock;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
@@ -196,6 +197,7 @@ import java.util.stream.Collectors;
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
+@Presubmit
public class NetworkPolicyManagerServiceTest {
private static final String TAG = "NetworkPolicyManagerServiceTest";
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
new file mode 100644
index 000000000000..6bb4202fa759
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+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.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+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.apex.ApexInfo;
+import android.apex.ApexSessionInfo;
+import android.apex.IApexService;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.os.FileUtils;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.servicestests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ApexManagerTest {
+ private static final String TEST_APEX_PKG = "com.android.apex.test";
+ private static final int TEST_SESSION_ID = 99999999;
+ private static final int[] TEST_CHILD_SESSION_ID = {8888, 7777};
+ private ApexManager mApexManager;
+ private Context mContext;
+
+ private IApexService mApexService = mock(IApexService.class);
+
+ @Before
+ public void setUp() throws RemoteException {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mApexManager = new ApexManager.ApexManagerImpl(mContext, mApexService);
+ }
+
+ @Test
+ public void testGetPackageInfo_setFlagsMatchActivePackage() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false));
+ final PackageInfo activePkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG,
+ ApexManager.MATCH_ACTIVE_PACKAGE);
+
+ assertThat(activePkgPi).isNotNull();
+ assertThat(activePkgPi.packageName).contains(TEST_APEX_PKG);
+
+ final PackageInfo factoryPkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG,
+ ApexManager.MATCH_FACTORY_PACKAGE);
+
+ assertThat(factoryPkgPi).isNull();
+ }
+
+ @Test
+ public void testGetPackageInfo_setFlagsMatchFactoryPackage() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+ PackageInfo factoryPkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG,
+ ApexManager.MATCH_FACTORY_PACKAGE);
+
+ assertThat(factoryPkgPi).isNotNull();
+ assertThat(factoryPkgPi.packageName).contains(TEST_APEX_PKG);
+
+ final PackageInfo activePkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG,
+ ApexManager.MATCH_ACTIVE_PACKAGE);
+
+ assertThat(activePkgPi).isNull();
+ }
+
+ @Test
+ public void testGetPackageInfo_setFlagsNone() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+
+ assertThat(mApexManager.getPackageInfo(TEST_APEX_PKG, 0)).isNull();
+ }
+
+ @Test
+ public void testGetActivePackages() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, true));
+
+ assertThat(mApexManager.getActivePackages()).isNotEmpty();
+ }
+
+ @Test
+ public void testGetActivePackages_noneActivePackages() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+
+ assertThat(mApexManager.getActivePackages()).isEmpty();
+ }
+
+ @Test
+ public void testGetFactoryPackages() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+
+ assertThat(mApexManager.getFactoryPackages()).isNotEmpty();
+ }
+
+ @Test
+ public void testGetFactoryPackages_noneFactoryPackages() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false));
+
+ assertThat(mApexManager.getFactoryPackages()).isEmpty();
+ }
+
+ @Test
+ public void testGetInactivePackages() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+
+ assertThat(mApexManager.getInactivePackages()).isNotEmpty();
+ }
+
+ @Test
+ public void testGetInactivePackages_noneInactivePackages() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false));
+
+ assertThat(mApexManager.getInactivePackages()).isEmpty();
+ }
+
+ @Test
+ public void testIsApexPackage() throws RemoteException {
+ when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+
+ assertThat(mApexManager.isApexPackage(TEST_APEX_PKG)).isTrue();
+ }
+
+ @Test
+ public void testIsApexSupported() {
+ assertThat(mApexManager.isApexSupported()).isTrue();
+ }
+
+ @Test
+ public void testGetStagedSessionInfo() throws RemoteException {
+ when(mApexService.getStagedSessionInfo(anyInt())).thenReturn(
+ getFakeStagedSessionInfo());
+
+ mApexManager.getStagedSessionInfo(TEST_SESSION_ID);
+ verify(mApexService, times(1)).getStagedSessionInfo(TEST_SESSION_ID);
+ }
+
+ @Test
+ public void testGetStagedSessionInfo_unKnownStagedSessionId() throws RemoteException {
+ when(mApexService.getStagedSessionInfo(anyInt())).thenReturn(
+ getFakeUnknownSessionInfo());
+
+ assertThat(mApexManager.getStagedSessionInfo(TEST_SESSION_ID)).isNull();
+ }
+
+ @Test
+ public void testSubmitStagedSession_throwPackageManagerException() throws RemoteException {
+ doAnswer(invocation -> {
+ throw new Exception();
+ }).when(mApexService).submitStagedSession(anyInt(), any(), any());
+
+ assertThrows(PackageManagerException.class,
+ () -> mApexManager.submitStagedSession(TEST_SESSION_ID, TEST_CHILD_SESSION_ID));
+ }
+
+ @Test
+ public void testSubmitStagedSession_throwRunTimeException() throws RemoteException {
+ doThrow(RemoteException.class).when(mApexService).submitStagedSession(anyInt(), any(),
+ any());
+
+ assertThrows(RuntimeException.class,
+ () -> mApexManager.submitStagedSession(TEST_SESSION_ID, TEST_CHILD_SESSION_ID));
+ }
+
+ @Test
+ public void testMarkStagedSessionReady_throwPackageManagerException() throws RemoteException {
+ doAnswer(invocation -> {
+ throw new Exception();
+ }).when(mApexService).markStagedSessionReady(anyInt());
+
+ assertThrows(PackageManagerException.class,
+ () -> mApexManager.markStagedSessionReady(TEST_SESSION_ID));
+ }
+
+ @Test
+ public void testMarkStagedSessionReady_throwRunTimeException() throws RemoteException {
+ doThrow(RemoteException.class).when(mApexService).markStagedSessionReady(anyInt());
+
+ assertThrows(RuntimeException.class,
+ () -> mApexManager.markStagedSessionReady(TEST_SESSION_ID));
+ }
+
+ @Test
+ public void testAbortActiveSession_remoteException() throws RemoteException {
+ doThrow(RemoteException.class).when(mApexService).abortActiveSession();
+
+ try {
+ assertThat(mApexManager.abortActiveSession()).isFalse();
+ } catch (Exception e) {
+ throw new AssertionError("ApexManager should not raise Exception");
+ }
+ }
+
+ @Test
+ public void testMarkStagedSessionSuccessful_throwRemoteException() throws RemoteException {
+ doThrow(RemoteException.class).when(mApexService).markStagedSessionSuccessful(anyInt());
+
+ assertThrows(RuntimeException.class,
+ () -> mApexManager.markStagedSessionSuccessful(TEST_SESSION_ID));
+ }
+
+ @Test
+ public void testUninstallApex_throwException_returnFalse() throws RemoteException {
+ doAnswer(invocation -> {
+ throw new Exception();
+ }).when(mApexService).unstagePackages(any());
+
+ assertThat(mApexManager.uninstallApex(TEST_APEX_PKG)).isFalse();
+ }
+
+ private ApexInfo[] createApexInfo(boolean isActive, boolean isFactory) {
+ File apexFile = copyRawResourceToFile(TEST_APEX_PKG, R.raw.apex_test);
+ ApexInfo apexInfo = new ApexInfo();
+ apexInfo.isActive = isActive;
+ apexInfo.isFactory = isFactory;
+ apexInfo.moduleName = TEST_APEX_PKG;
+ apexInfo.modulePath = apexFile.getPath();
+ apexInfo.versionCode = 191000070;
+
+ return new ApexInfo[]{apexInfo};
+ }
+
+ private ApexSessionInfo getFakeStagedSessionInfo() {
+ ApexSessionInfo stagedSessionInfo = new ApexSessionInfo();
+ stagedSessionInfo.sessionId = TEST_SESSION_ID;
+ stagedSessionInfo.isStaged = true;
+
+ return stagedSessionInfo;
+ }
+
+ private ApexSessionInfo getFakeUnknownSessionInfo() {
+ ApexSessionInfo stagedSessionInfo = new ApexSessionInfo();
+ stagedSessionInfo.sessionId = TEST_SESSION_ID;
+ stagedSessionInfo.isUnknown = true;
+
+ return stagedSessionInfo;
+ }
+
+ /**
+ * Copies a specified {@code resourceId} to a temp file. Returns a non-null file if the copy
+ * succeeded
+ */
+ File copyRawResourceToFile(String baseName, int resourceId) {
+ File outFile;
+ try {
+ outFile = File.createTempFile(baseName, ".apex");
+ } catch (IOException e) {
+ throw new AssertionError("CreateTempFile IOException" + e);
+ }
+
+ try (InputStream is = mContext.getResources().openRawResource(resourceId);
+ FileOutputStream os = new FileOutputStream(outFile)) {
+ assertThat(FileUtils.copy(is, os)).isGreaterThan(0L);
+ } catch (FileNotFoundException e) {
+ throw new AssertionError("File not found exception " + e);
+ } catch (IOException e) {
+ throw new AssertionError("IOException" + e);
+ }
+
+ return outFile;
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index fff32215493b..4daf6d3e601e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -38,9 +38,11 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
import android.app.TaskStackListener;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
@@ -348,10 +350,12 @@ public class ActivityDisplayTests extends ActivityTestsBase {
final ActivityRecord activity = createFullscreenStackWithSimpleActivityAt(
display).topRunningActivityLocked();
activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
+ when(activity.getRequestedOrientation()).thenReturn(
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
activity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
activity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- activity.getTaskRecord().getConfiguration().windowConfiguration.setAppBounds(
- 0, 0, 1000, 2000);
+ activity.visible = true;
+ activity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
final ArrayList<CompletableFuture<IBinder>> resultWrapper = new ArrayList<>();
mService.getTaskChangeNotificationController().registerTaskStackListener(
@@ -363,11 +367,14 @@ public class ActivityDisplayTests extends ActivityTestsBase {
}
});
- // Expect the exact token when the activity is in size compatibility mode.
- activity.getResolvedOverrideConfiguration().windowConfiguration.setAppBounds(
- 0, 0, 800, 1600);
resultWrapper.add(new CompletableFuture<>());
- display.handleActivitySizeCompatModeIfNeeded(activity);
+
+ // resize the display to exercise size-compat mode
+ final DisplayContent displayContent = display.mDisplayContent;
+ displayContent.mBaseDisplayHeight = (int) (0.8f * displayContent.mBaseDisplayHeight);
+ Configuration c = new Configuration();
+ displayContent.computeScreenConfiguration(c);
+ display.onRequestedOverrideConfigurationChanged(c);
assertEquals(activity.appToken, resultWrapper.get(0).get(2, TimeUnit.SECONDS));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index bf1508a34872..cc78ec7c86c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -40,7 +40,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
@@ -70,10 +69,12 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
+import android.app.WindowConfiguration;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.PauseActivityItem;
@@ -485,6 +486,43 @@ public class ActivityRecordTests extends ActivityTestsBase {
}
@Test
+ public void testSizeCompatMode_KeepBoundsWhenChangingFromFreeformToFullscreen() {
+ setupDisplayContentForCompatDisplayInsets();
+
+ // put display in freeform mode
+ ActivityDisplay display = mActivity.getDisplay();
+ final Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
+ c.windowConfiguration.setBounds(new Rect(0, 0, 2000, 1000));
+ c.densityDpi = 300;
+ c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ display.onRequestedOverrideConfigurationChanged(c);
+
+ // launch compat activity in freeform and store bounds
+ when(mActivity.getRequestedOrientation()).thenReturn(
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ mTask.getRequestedOverrideConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+ mTask.setBounds(100, 100, 400, 600);
+ mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.visible = true;
+ ensureActivityConfiguration();
+
+ final Rect bounds = new Rect(mActivity.getBounds());
+ final int density = mActivity.getConfiguration().densityDpi;
+
+ // change display configuration to fullscreen
+ c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ display.onRequestedOverrideConfigurationChanged(c);
+
+ // check if dimensions stay the same
+ assertTrue(mActivity.inSizeCompatMode());
+ assertEquals(bounds.width(), mActivity.getBounds().width());
+ assertEquals(bounds.height(), mActivity.getBounds().height());
+ assertEquals(density, mActivity.getConfiguration().densityDpi);
+ assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, mActivity.getWindowingMode());
+ }
+
+ @Test
public void testSizeCompatMode_FixedAspectRatioBoundsWithDecor() {
setupDisplayContentForCompatDisplayInsets();
final int decorHeight = 200; // e.g. The device has cutout.
@@ -501,11 +539,17 @@ public class ActivityRecordTests extends ActivityTestsBase {
return null;
}).when(policy).getNonDecorInsetsLw(anyInt() /* rotation */, anyInt() /* width */,
anyInt() /* height */, any() /* displayCutout */, any() /* outInsets */);
+ // set appBounds to incorporate decor
+ final Configuration c =
+ new Configuration(mStack.getDisplay().getRequestedOverrideConfiguration());
+ c.windowConfiguration.getAppBounds().top = decorHeight;
+ mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
doReturn(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
.when(mActivity).getRequestedOrientation();
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
+ mActivity.visible = true;
ensureActivityConfiguration();
// The parent configuration doesn't change since the first resolved configuration, so the
// activity shouldn't be in the size compatibility mode.
@@ -555,7 +599,10 @@ public class ActivityRecordTests extends ActivityTestsBase {
// Move the non-resizable activity to the new display.
mStack.reparent(newDisplay, true /* onTop */, false /* displayRemoved */);
- assertEquals(originalBounds, mActivity.getWindowConfiguration().getBounds());
+ assertEquals(originalBounds.width(),
+ mActivity.getWindowConfiguration().getBounds().width());
+ assertEquals(originalBounds.height(),
+ mActivity.getWindowConfiguration().getBounds().height());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
assertTrue(mActivity.inSizeCompatMode());
}
@@ -566,9 +613,11 @@ public class ActivityRecordTests extends ActivityTestsBase {
when(mActivity.getRequestedOrientation()).thenReturn(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
- mTask.getConfiguration().orientation = ORIENTATION_PORTRAIT;
+ mTask.getRequestedOverrideConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.visible = true;
+
ensureActivityConfiguration();
final Rect originalBounds = new Rect(mActivity.getBounds());
@@ -576,7 +625,10 @@ public class ActivityRecordTests extends ActivityTestsBase {
setupDisplayAndParentSize(1000, 2000);
ensureActivityConfiguration();
- assertEquals(originalBounds, mActivity.getWindowConfiguration().getBounds());
+ assertEquals(originalBounds.width(),
+ mActivity.getWindowConfiguration().getBounds().width());
+ assertEquals(originalBounds.height(),
+ mActivity.getWindowConfiguration().getBounds().height());
assertTrue(mActivity.inSizeCompatMode());
}
@@ -584,13 +636,16 @@ public class ActivityRecordTests extends ActivityTestsBase {
public void testSizeCompatMode_FixedScreenLayoutSizeBits() {
final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO
| Configuration.SCREENLAYOUT_SIZE_NORMAL;
+ final int layoutMask = Configuration.SCREENLAYOUT_LONG_MASK
+ | Configuration.SCREENLAYOUT_SIZE_MASK
+ | Configuration.SCREENLAYOUT_LAYOUTDIR_MASK;
mTask.getRequestedOverrideConfiguration().screenLayout = fixedScreenLayout
| Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
prepareFixedAspectRatioUnresizableActivity();
// The initial configuration should inherit from parent.
- assertEquals(mTask.getConfiguration().screenLayout,
- mActivity.getConfiguration().screenLayout);
+ assertEquals(mTask.getConfiguration().screenLayout & layoutMask,
+ mActivity.getConfiguration().screenLayout & layoutMask);
mTask.getConfiguration().screenLayout = Configuration.SCREENLAYOUT_LAYOUTDIR_RTL
| Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_LARGE;
@@ -598,7 +653,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
// The size and aspect ratio bits don't change, but the layout direction should be updated.
assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_RTL,
- mActivity.getConfiguration().screenLayout);
+ mActivity.getConfiguration().screenLayout & layoutMask);
}
@Test
@@ -618,13 +673,18 @@ public class ActivityRecordTests extends ActivityTestsBase {
| ActivityInfo.CONFIG_WINDOW_CONFIGURATION)
.when(display).getLastOverrideConfigurationChanges();
mActivity.onConfigurationChanged(mTask.getConfiguration());
+ when(display.getLastOverrideConfigurationChanges()).thenCallRealMethod();
// The override configuration should not change so it is still in size compatibility mode.
assertTrue(mActivity.inSizeCompatMode());
- // Simulate the display changes density.
- doReturn(ActivityInfo.CONFIG_DENSITY).when(display).getLastOverrideConfigurationChanges();
+ // Change display density
+ final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
+ displayContent.mBaseDisplayDensity = (int) (0.7f * displayContent.mBaseDisplayDensity);
+ final Configuration c = new Configuration();
+ displayContent.computeScreenConfiguration(c);
mService.mAmInternal = mock(ActivityManagerInternal.class);
- mActivity.onConfigurationChanged(mTask.getConfiguration());
+ mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
+
// The override configuration should be reset and the activity's process will be killed.
assertFalse(mActivity.inSizeCompatMode());
verify(mActivity).restartProcessIfVisible();
@@ -1242,6 +1302,8 @@ public class ActivityRecordTests extends ActivityTestsBase {
c.windowConfiguration.setBounds(new Rect(0, 0, width, height));
c.windowConfiguration.setAppBounds(0, 0, width, height);
c.windowConfiguration.setRotation(ROTATION_0);
+ c.orientation = width > height
+ ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
return displayContent;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index d43fe63c04d1..80f0851c27a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -42,7 +43,6 @@ import android.content.res.Configuration;
import android.os.Build;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
-import android.util.Pair;
import android.view.DisplayInfo;
import com.android.server.AttributeCache;
@@ -388,7 +388,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
private final RootActivityContainer mRootActivityContainer;
private ActivityDisplay mDisplay;
private int mStackId = -1;
- private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
+ private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
private int mActivityType = ACTIVITY_TYPE_STANDARD;
private boolean mOnTop = true;
private boolean mCreateActivity = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 7449efda69ac..1fb6a563aa40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -60,11 +60,9 @@ public class AppChangeTransitionTests extends WindowTestsBase {
private ActivityRecord mActivity;
public void setUpOnDisplay(DisplayContent dc) {
- mStack = createTaskStackOnDisplay(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, dc);
- mTask = createTaskInStack(mStack, 0 /* userId */);
- mActivity = WindowTestUtils.createTestActivityRecord(dc);
-
- mTask.addChild(mActivity, 0);
+ mActivity = createTestActivityRecord(dc, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
+ mTask = mActivity.getTask();
+ mStack = mTask.mStack;
// Set a remote animator with snapshot disabled. Snapshots don't work in wmtests.
RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
@@ -167,7 +165,7 @@ public class AppChangeTransitionTests extends WindowTestsBase {
// setup currently defaults to no snapshot.
setUpOnDisplay(mDisplayContent);
- mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ mTask.mTaskRecord.setWindowingMode(WINDOWING_MODE_FREEFORM);
assertEquals(1, mDisplayContent.mChangingApps.size());
assertTrue(mActivity.isInChangeTransition());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index dd85f69b7160..5a141ae983f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -410,7 +410,8 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
}
@Test
- public void testForceMaximizesPreDApp() {
+ public void testForceMaximizesUnresizeableApp() {
+ mService.mSizeCompatFreeform = false;
final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
WINDOWING_MODE_FREEFORM);
@@ -422,7 +423,7 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
mCurrent.mBounds.set(0, 0, 200, 100);
- mActivity.info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUPCAKE;
+ mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
@@ -432,46 +433,29 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
}
@Test
- public void testForceMaximizesAppWithoutMultipleDensitySupport() {
+ public void testLaunchesAppInWindowOnFreeformDisplay() {
+ mService.mSizeCompatFreeform = true;
final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
WINDOWING_MODE_FREEFORM);
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
- options.setLaunchBounds(new Rect(0, 0, 200, 100));
-
- mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
- mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
- mCurrent.mBounds.set(0, 0, 200, 100);
-
- mActivity.info.applicationInfo.flags = 0;
-
- assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
- mActivity, /* source */ null, options, mCurrent, mResult));
-
- assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
- WINDOWING_MODE_FREEFORM);
- }
-
- @Test
- public void testForceMaximizesUnresizeableApp() {
- final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
- WINDOWING_MODE_FREEFORM);
+ Rect expectedLaunchBounds = new Rect(0, 0, 200, 100);
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
- options.setLaunchBounds(new Rect(0, 0, 200, 100));
+ options.setLaunchBounds(expectedLaunchBounds);
mCurrent.mPreferredDisplayId = freeformDisplay.mDisplayId;
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
- mCurrent.mBounds.set(0, 0, 200, 100);
+ mCurrent.mBounds.set(expectedLaunchBounds);
mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null,
mActivity, /* source */ null, options, mCurrent, mResult));
- assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+ assertEquals(expectedLaunchBounds, mResult.mBounds);
+
+ assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
WINDOWING_MODE_FREEFORM);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index dc89f5080c4b..f8d49ad18664 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -64,6 +66,7 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
any(InputChannel.class))).thenReturn(true);
mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
+ mWindow.getTask().setResizeable(RESIZE_MODE_RESIZEABLE);
mWindow.mInputChannel = new InputChannel();
mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
doReturn(mock(InputMonitor.class)).when(mDisplayContent).getInputMonitor();
@@ -129,4 +132,23 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
assertFalse(mTarget.isPositioningLocked());
assertNull(mTarget.getDragWindowHandleLocked());
}
+
+ @Test
+ public void testHandleTapOutsideNonResizableTask() {
+ assertFalse(mTarget.isPositioningLocked());
+ assertNull(mTarget.getDragWindowHandleLocked());
+
+ final DisplayContent content = mock(DisplayContent.class);
+ doReturn(mWindow.getTask()).when(content).findTaskForResizePoint(anyInt(), anyInt());
+ assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
+
+ mWindow.getTask().setResizeable(RESIZE_MODE_UNRESIZEABLE);
+
+ mTarget.handleTapOutsideTask(content, 0, 0);
+ // Wait until the looper processes finishTaskPositioning.
+ assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
+
+ assertFalse(mTarget.isPositioningLocked());
+ }
+
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 0f8fb0417d01..a4e38f15e0e7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -403,7 +403,9 @@ public class TaskRecordTests extends ActivityTestsBase {
// Without limiting to be inside the parent bounds, the out screen size should keep relative
// to the input bounds.
final ActivityRecord.CompatDisplayInsets compatIntsets =
- new ActivityRecord.CompatDisplayInsets(displayContent);
+ new ActivityRecord.CompatDisplayInsets(displayContent, new Rect(0, 0,
+ displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight),
+ false);
task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi,
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 5b9a785663e2..2fc03c7f13d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -21,12 +21,15 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -119,7 +122,7 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testRemoveContainer() {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
assertNotNull(stack);
assertNotNull(task);
@@ -135,10 +138,10 @@ public class TaskStackTests extends WindowTestsBase {
@Test
public void testRemoveContainer_deferRemoval() {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
// Stack removal is deferred if one of its child is animating.
- task.setLocalIsAnimating(true);
+ doReturn(true).when(task).isSelfAnimating();
stack.removeIfPossible();
// For the case of deferred removal the task controller will still be connected to the its
@@ -157,8 +160,7 @@ public class TaskStackTests extends WindowTestsBase {
public void testReparent() {
// Create first stack on primary display.
final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1);
- task1.mOnDisplayChangedCalled = false;
+ final Task task1 = createTaskInStack(stack1, 0 /* userId */);
// Create second display and put second stack on it.
final DisplayContent dc = createNewDisplay();
@@ -170,7 +172,7 @@ public class TaskStackTests extends WindowTestsBase {
final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1);
final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2);
assertEquals(stack1PositionInParent, stack2PositionInParent + 1);
- assertTrue(task1.mOnDisplayChangedCalled);
+ verify(task1, times(1)).onDisplayChanged(any());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 2ba1834ea286..4dfa26644fa9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -16,11 +16,16 @@
package com.android.server.wm;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import android.graphics.Point;
import android.graphics.Rect;
@@ -45,7 +50,7 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testRemoveContainer() {
final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ActivityRecord activity =
WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
@@ -59,11 +64,11 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testRemoveContainer_deferRemoval() {
final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ActivityRecord activity =
WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
- task.mShouldDeferRemoval = true;
+ doReturn(true).when(task).shouldDeferRemoval();
task.removeIfPossible();
// For the case of deferred removal the task will still be connected to the its app token
@@ -81,9 +86,9 @@ public class TaskTests extends WindowTestsBase {
@Test
public void testReparent() {
final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1);
+ final Task task = createTaskInStack(stackController1, 0 /* userId */);
final TaskStack stackController2 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stackController2);
+ final Task task2 = createTaskInStack(stackController2, 0 /* userId */);
boolean gotException = false;
try {
@@ -104,34 +109,33 @@ public class TaskTests extends WindowTestsBase {
task.reparent(stackController2, 0, false/* moveParents */);
assertEquals(stackController2, task.getParent());
- assertEquals(0, task.positionInParent());
- assertEquals(1, task2.positionInParent());
+ assertEquals(0, task.getParent().mChildren.indexOf(task));
+ assertEquals(1, task2.getParent().mChildren.indexOf(task2));
}
@Test
public void testReparent_BetweenDisplays() {
// Create first stack on primary display.
final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1);
- task.mOnDisplayChangedCalled = false;
+ final Task task = createTaskInStack(stack1, 0 /* userId */);
assertEquals(mDisplayContent, stack1.getDisplayContent());
// Create second display and put second stack on it.
final DisplayContent dc = createNewDisplay();
final TaskStack stack2 = createTaskStackOnDisplay(dc);
- final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2);
+ final Task task2 = createTaskInStack(stack2, 0 /* userId */);
// Reparent and check state
task.reparent(stack2, 0, false /* moveParents */);
assertEquals(stack2, task.getParent());
- assertEquals(0, task.positionInParent());
- assertEquals(1, task2.positionInParent());
- assertTrue(task.mOnDisplayChangedCalled);
+ assertEquals(0, task.getParent().mChildren.indexOf(task));
+ assertEquals(1, task2.getParent().mChildren.indexOf(task2));
+ verify(task, times(1)).onDisplayChanged(any());
}
@Test
public void testBounds() {
final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent);
- final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1);
+ final Task task = createTaskInStack(stack1, 0 /* userId */);
// Check that setting bounds also updates surface position
Rect bounds = new Rect(10, 10, 100, 200);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 5c547c224225..8cd97cb8a344 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -16,24 +16,24 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
-import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import android.view.Gravity;
-import android.view.IWindow;
import android.view.WindowManager;
import androidx.test.filters.FlakyTest;
@@ -57,29 +57,11 @@ import org.mockito.Mockito;
@RunWith(WindowTestRunner.class)
public class WindowFrameTests extends WindowTestsBase {
- private final IWindow mIWindow = new TestIWindow();
private final Rect mEmptyRect = new Rect();
private DisplayContent mTestDisplayContent;
- static class FrameTestWindowState extends WindowState {
- boolean mDockedResizingForTest = false;
- FrameTestWindowState(WindowManagerService wm, IWindow iWindow, WindowToken windowToken,
- WindowManager.LayoutParams attrs) {
- super(wm, mock(Session.class), iWindow, windowToken, null, 0, 0, attrs, 0, 0,
- false /* ownerCanAddInternalSystemWindow */);
- }
-
- @Override
- boolean isDockedResizing() {
- return mDockedResizingForTest;
- }
- }
-
- TaskStack mStubStack;
-
@Before
public void setUp() throws Exception {
- mStubStack = mock(TaskStack.class);
DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo);
testDisplayInfo.displayCutout = null;
mTestDisplayContent = createNewDisplay(testDisplayInfo);
@@ -129,8 +111,7 @@ public class WindowFrameTests extends WindowTestsBase {
expectedRect.bottom);
}
- private void assertPolicyCrop(
- FrameTestWindowState w, int left, int top, int right, int bottom) {
+ private void assertPolicyCrop(WindowState w, int left, int top, int right, int bottom) {
Rect policyCrop = new Rect();
w.calculatePolicyCrop(policyCrop);
assertRect(policyCrop, left, top, right, bottom);
@@ -139,7 +120,7 @@ public class WindowFrameTests extends WindowTestsBase {
@Test
public void testLayoutInFullscreenTaskInsets() {
// fullscreen task doesn't use bounds for computeFrame
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final int bottomContentInset = 100;
@@ -196,7 +177,7 @@ public class WindowFrameTests extends WindowTestsBase {
@Test
public void testLayoutInFullscreenTaskNoInsets() {
// fullscreen task doesn't use bounds for computeFrame
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
// With no insets or system decor all the frames incoming from PhoneWindowManager
@@ -278,16 +259,16 @@ public class WindowFrameTests extends WindowTestsBase {
final int logicalWidth = displayInfo.logicalWidth;
final int logicalHeight = displayInfo.logicalHeight;
- final int taskLeft = logicalWidth / 4;
- final int taskTop = logicalHeight / 4;
- final int taskRight = logicalWidth / 4 * 3;
- final int taskBottom = logicalHeight / 4 * 3;
- final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ final Rect taskBounds = new Rect(
+ logicalWidth / 4, logicalHeight / 4, logicalWidth / 4 * 3, logicalHeight / 4 * 3);
+ WindowState w = createWindow();
final Task task = w.getTask();
// Use split-screen because it is non-fullscreen, but also not floating
- task.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- task.setBounds(taskBounds);
+ task.mTaskRecord.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ task.mTaskRecord.setBounds(taskBounds);
+ // The bounds we are requesting might be different from what the system resolved based on
+ // other factors.
+ final Rect resolvedTaskBounds = task.getBounds();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
@@ -296,8 +277,8 @@ public class WindowFrameTests extends WindowTestsBase {
w.computeFrameLw();
// For non fullscreen tasks the containing frame is based off the
// task bounds not the parent frame.
- assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
- assertContentFrame(w, taskBounds);
+ assertEquals(resolvedTaskBounds, w.getFrameLw());
+ assertContentFrame(w, resolvedTaskBounds);
assertContentInset(w, 0, 0, 0, 0);
pf.set(0, 0, logicalWidth, logicalHeight);
@@ -307,36 +288,38 @@ public class WindowFrameTests extends WindowTestsBase {
final Rect cf = new Rect(0, 0, cfRight, cfBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw();
- assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
- int contentInsetRight = taskRight - cfRight;
- int contentInsetBottom = taskBottom - cfBottom;
+ assertEquals(resolvedTaskBounds, w.getFrameLw());
+ int contentInsetRight = resolvedTaskBounds.right - cfRight;
+ int contentInsetBottom = resolvedTaskBounds.bottom - cfBottom;
assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
- assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight,
- taskBottom - contentInsetBottom));
+ assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top,
+ resolvedTaskBounds.right - contentInsetRight,
+ resolvedTaskBounds.bottom - contentInsetBottom));
pf.set(0, 0, logicalWidth, logicalHeight);
// If we set displayed bounds, the insets will be computed with the main task bounds
// but the frame will be positioned according to the displayed bounds.
final int insetLeft = logicalWidth / 5;
final int insetTop = logicalHeight / 5;
- final int insetRight = insetLeft + (taskRight - taskLeft);
- final int insetBottom = insetTop + (taskBottom - taskTop);
- task.setOverrideDisplayedBounds(taskBounds);
- task.setBounds(insetLeft, insetTop, insetRight, insetBottom);
+ final int insetRight = insetLeft + (resolvedTaskBounds.right - resolvedTaskBounds.left);
+ final int insetBottom = insetTop + (resolvedTaskBounds.bottom - resolvedTaskBounds.top);
+ task.mTaskRecord.setDisplayedBounds(resolvedTaskBounds);
+ task.mTaskRecord.setBounds(insetLeft, insetTop, insetRight, insetBottom);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw();
- assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
+ assertEquals(resolvedTaskBounds, w.getFrameLw());
contentInsetRight = insetRight - cfRight;
contentInsetBottom = insetBottom - cfBottom;
assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
- assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight,
- taskBottom - contentInsetBottom));
+ assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top,
+ resolvedTaskBounds.right - contentInsetRight,
+ resolvedTaskBounds.bottom - contentInsetBottom));
}
@Test
@FlakyTest(bugId = 130388666)
public void testCalculatePolicyCrop() {
- final FrameTestWindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ final WindowState w = createWindow();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
@@ -381,7 +364,7 @@ public class WindowFrameTests extends WindowTestsBase {
// to the computed window frame.
assertPolicyCrop(w, 0, 0, logicalWidth / 2, logicalHeight / 2);
- w.mDockedResizingForTest = true;
+ doReturn(true).when(w).isDockedResizing();
// But if we are docked resizing it won't be, however we will still be
// shrunk to the decor frame and the display.
assertPolicyCrop(w, 0, 0,
@@ -402,7 +385,7 @@ public class WindowFrameTests extends WindowTestsBase {
final int taskRight = logicalWidth / 4 * 3;
final int taskBottom = logicalHeight / 4 * 3;
final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
final Task task = w.getTask();
// Use split-screen because it is non-fullscreen, but also not floating
task.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
@@ -440,7 +423,7 @@ public class WindowFrameTests extends WindowTestsBase {
@FlakyTest(bugId = 130388666)
public void testDisplayCutout() {
// Regular fullscreen task and window
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
final Rect pf = new Rect(0, 0, 1000, 2000);
@@ -464,7 +447,7 @@ public class WindowFrameTests extends WindowTestsBase {
@FlakyTest(bugId = 130388666)
public void testDisplayCutout_tempDisplayedBounds() {
// Regular fullscreen task and window
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
final Task task = w.getTask();
task.setBounds(new Rect(0, 0, 1000, 2000));
task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500));
@@ -489,11 +472,12 @@ public class WindowFrameTests extends WindowTestsBase {
@Test
public void testFreeformContentInsets() {
+ removeGlobalMinSizeRestriction();
// fullscreen task doesn't use bounds for computeFrame
- WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT);
+ WindowState w = createWindow();
final Task task = w.getTask();
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
- task.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.mTaskRecord.setWindowingMode(WINDOWING_MODE_FREEFORM);
DisplayContent dc = mTestDisplayContent;
dc.mInputMethodTarget = w;
@@ -515,7 +499,7 @@ public class WindowFrameTests extends WindowTestsBase {
// First check that it only gets moved up enough to show window.
final Rect winRect = new Rect(200, 200, 300, 500);
- task.setBounds(winRect);
+ task.mTaskRecord.setBounds(winRect);
w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
w.computeFrameLw();
@@ -527,7 +511,7 @@ public class WindowFrameTests extends WindowTestsBase {
// Now check that it won't get moved beyond the top and then has appropriate insets
winRect.bottom = 600;
- task.setBounds(winRect);
+ task.mTaskRecord.setBounds(winRect);
w.setBounds(winRect);
w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
w.computeFrameLw();
@@ -544,16 +528,9 @@ public class WindowFrameTests extends WindowTestsBase {
assertEquals(winRect, w.getFrameLw());
}
- private FrameTestWindowState createWindow(int width, int height) {
- final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
- attrs.width = width;
- attrs.height = height;
-
- ActivityRecord activity = createActivityRecord(mTestDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-
- FrameTestWindowState ws = new FrameTestWindowState(mWm, mIWindow, activity, attrs);
- activity.addWindow(ws);
+ private WindowState createWindow() {
+ final WindowState ws = createWindow(null, TYPE_APPLICATION, mTestDisplayContent, "WindowFrameTests");
+ spyOn(ws);
return ws;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
index 86f0f8bc1bb9..1c9eed2e5622 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
@@ -77,6 +78,20 @@ public class WindowManagerSettingsTests extends WindowTestsBase {
}
}
+ @Test
+ public void testEnableSizeCompatFreeform() {
+ try (SettingsSession enableSizeCompatFreeformSession = new
+ SettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) {
+ final boolean enableSizeCompatFreeform =
+ !enableSizeCompatFreeformSession.getSetting();
+ final Uri enableSizeCompatFreeformUri =
+ enableSizeCompatFreeformSession.setSetting(enableSizeCompatFreeform);
+ mWm.mSettingsObserver.onChange(false, enableSizeCompatFreeformUri);
+
+ assertEquals(mWm.mAtmService.mSizeCompatFreeform, enableSizeCompatFreeform);
+ }
+ }
+
private class SettingsSession implements AutoCloseable {
private static final int SETTING_VALUE_OFF = 0;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index 4fbf820e339d..51daf6567a47 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -33,16 +33,16 @@ import com.android.server.wm.ActivityTestsBase.ActivityBuilder;
* A collection of static functions that provide access to WindowManager related test functionality.
*/
class WindowTestUtils {
- private static int sNextTaskId = 0;
/** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
- static Task createTaskInStack(WindowManagerService service, TaskStack stack,
- int userId) {
+ static Task createTaskInStack(WindowManagerService service, TaskStack stack, int userId) {
synchronized (service.mGlobalLock) {
- final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
- new ActivityManager.TaskDescription(), null);
- stack.addTask(newTask, POSITION_TOP);
- return newTask;
+ final TaskRecord task = new ActivityTestsBase.TaskBuilder(
+ stack.mActivityStack.mStackSupervisor)
+ .setUserId(userId)
+ .setStack(stack.mActivityStack)
+ .build();
+ return task.mTask;
}
}
@@ -53,17 +53,33 @@ class WindowTestUtils {
return activity;
}
+ static ActivityRecord createTestActivityRecord(ActivityStack stack) {
+ synchronized (stack.mService.mGlobalLock) {
+ final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(
+ stack.mService)
+ .setStack(stack)
+ .setCreateTask(true)
+ .build();
+ postCreateActivitySetup(activity, stack.mTaskStack.getDisplayContent());
+ return activity;
+ }
+ }
+
static ActivityRecord createTestActivityRecord(DisplayContent dc) {
synchronized (dc.mWmService.mGlobalLock) {
final ActivityRecord activity = new ActivityBuilder(dc.mWmService.mAtmService).build();
- activity.onDisplayChanged(dc);
- activity.setOccludesParent(true);
- activity.setHidden(false);
- activity.hiddenRequested = false;
+ postCreateActivitySetup(activity, dc);
return activity;
}
}
+ private static void postCreateActivitySetup(ActivityRecord activity, DisplayContent dc) {
+ activity.onDisplayChanged(dc);
+ activity.setOccludesParent(true);
+ activity.setHidden(false);
+ activity.hiddenRequested = false;
+ }
+
static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
return createTestWindowToken(type, dc, false /* persistOnEmpty */);
}
@@ -92,49 +108,6 @@ class WindowTestUtils {
}
}
- /* Used so we can gain access to some protected members of the {@link Task} class */
- static class TestTask extends Task {
- boolean mShouldDeferRemoval = false;
- boolean mOnDisplayChangedCalled = false;
- private boolean mIsAnimating = false;
-
- TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
- int resizeMode, boolean supportsPictureInPicture,
- TaskRecord taskRecord) {
- super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
- new ActivityManager.TaskDescription(), taskRecord);
- stack.addTask(this, POSITION_TOP);
- }
-
- boolean shouldDeferRemoval() {
- return mShouldDeferRemoval;
- }
-
- int positionInParent() {
- return getParent().mChildren.indexOf(this);
- }
-
- @Override
- void onDisplayChanged(DisplayContent dc) {
- super.onDisplayChanged(dc);
- mOnDisplayChangedCalled = true;
- }
-
- @Override
- boolean isSelfAnimating() {
- return mIsAnimating;
- }
-
- void setLocalIsAnimating(boolean isAnimating) {
- mIsAnimating = isAnimating;
- }
- }
-
- static TestTask createTestTask(TaskStack stack) {
- return new TestTask(sNextTaskId++, stack, 0, stack.mWmService, RESIZE_MODE_UNRESIZEABLE,
- false, mock(TaskRecord.class));
- }
-
/** Used to track resize reports. */
static class TestWindowState extends WindowState {
boolean mResizeReported;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index af9c51008091..780fed9805cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -211,11 +211,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
ActivityRecord createTestActivityRecord(DisplayContent dc, int
windowingMode, int activityType) {
final TaskStack stack = createTaskStackOnDisplay(windowingMode, activityType, dc);
- final Task task = createTaskInStack(stack, 0 /* userId */);
- final ActivityRecord activity =
- WindowTestUtils.createTestActivityRecord(dc);
- task.addChild(activity, 0);
- return activity;
+ return WindowTestUtils.createTestActivityRecord(stack.mActivityStack);
}
WindowState createWindow(WindowState parent, int type, String name) {
@@ -327,14 +323,14 @@ class WindowTestsBase extends SystemServiceTestsBase {
TaskStack createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) {
synchronized (mWm.mGlobalLock) {
- final Configuration overrideConfig = new Configuration();
- overrideConfig.windowConfiguration.setWindowingMode(windowingMode);
- overrideConfig.windowConfiguration.setActivityType(activityType);
- final int stackId = ++sNextStackId;
- final TaskStack stack = new TaskStack(mWm, stackId, mock(ActivityStack.class));
- dc.setStackOnDisplay(stackId, true, stack);
- stack.onRequestedOverrideConfigurationChanged(overrideConfig);
- return stack;
+ final ActivityStack stack = new ActivityTestsBase.StackBuilder(
+ dc.mWmService.mAtmService.mRootActivityContainer)
+ .setDisplay(dc.mActivityDisplay)
+ .setWindowingMode(windowingMode)
+ .setActivityType(activityType)
+ .setCreateActivity(false)
+ .build();
+ return stack.mTaskStack;
}
}
@@ -386,4 +382,9 @@ class WindowTestsBase extends SystemServiceTestsBase {
displayInfo.ownerUid = SYSTEM_UID;
return createNewDisplay(displayInfo);
}
+
+ /** Sets the default minimum task size to 1 so that tests can use small task sizes */
+ public void removeGlobalMinSizeRestriction() {
+ mWm.mAtmService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index 5d1f730694ef..87e077e3df1f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -334,8 +334,6 @@ final class UsageStatsProto {
proto.write(IntervalStatsProto.UsageStats.PACKAGE_INDEX, packageIndex + 1);
} else {
// Package not in Stringpool for some reason, write full string instead
- Slog.w(TAG, "UsageStats package name (" + usageStats.mPackageName
- + ") not found in IntervalStats string cache");
proto.write(IntervalStatsProto.UsageStats.PACKAGE, usageStats.mPackageName);
}
// Time attributes stored as an offset of the beginTime.
@@ -430,8 +428,6 @@ final class UsageStatsProto {
proto.write(IntervalStatsProto.Event.PACKAGE_INDEX, packageIndex + 1);
} else {
// Package not in Stringpool for some reason, write full string instead
- Slog.w(TAG, "Usage event package name (" + event.mPackage
- + ") not found in IntervalStats string cache");
proto.write(IntervalStatsProto.Event.PACKAGE, event.mPackage);
}
if (event.mClass != null) {
@@ -440,8 +436,6 @@ final class UsageStatsProto {
proto.write(IntervalStatsProto.Event.CLASS_INDEX, classIndex + 1);
} else {
// Class not in Stringpool for some reason, write full string instead
- Slog.w(TAG, "Usage event class name (" + event.mClass
- + ") not found in IntervalStats string cache");
proto.write(IntervalStatsProto.Event.CLASS, event.mClass);
}
}
@@ -454,10 +448,6 @@ final class UsageStatsProto {
if (taskRootPackageIndex >= 0) {
proto.write(IntervalStatsProto.Event.TASK_ROOT_PACKAGE_INDEX,
taskRootPackageIndex + 1);
- } else {
- // Task root package not in Stringpool for some reason.
- Slog.w(TAG, "Usage event task root package name (" + event.mTaskRootPackage
- + ") not found in IntervalStats string cache");
}
}
if (event.mTaskRootClass != null) {
@@ -465,10 +455,6 @@ final class UsageStatsProto {
if (taskRootClassIndex >= 0) {
proto.write(IntervalStatsProto.Event.TASK_ROOT_CLASS_INDEX,
taskRootClassIndex + 1);
- } else {
- // Task root class not in Stringpool for some reason.
- Slog.w(TAG, "Usage event task root class name (" + event.mTaskRootClass
- + ") not found in IntervalStats string cache");
}
}
switch (event.mEventType) {
@@ -496,9 +482,6 @@ final class UsageStatsProto {
channelIndex + 1);
} else {
// Channel not in Stringpool for some reason, write full string instead
- Slog.w(TAG, "Usage event notification channel name ("
- + event.mNotificationChannelId
- + ") not found in IntervalStats string cache");
proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL,
event.mNotificationChannelId);
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 5d03e151e993..6a805683e23d 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -86,6 +86,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -180,8 +181,8 @@ public class UsageStatsService extends SystemService implements
}
}
- private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
- new UsageStatsManagerInternal.AppIdleStateChangeListener() {
+ private AppIdleStateChangeListener mStandbyChangeListener =
+ new AppIdleStateChangeListener() {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
int bucket, int reason) {
@@ -253,6 +254,7 @@ public class UsageStatsService extends SystemService implements
null, mHandler);
publishLocalService(UsageStatsManagerInternal.class, new LocalService());
+ publishLocalService(AppStandbyInternal.class, mAppStandby);
publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
}
@@ -2029,17 +2031,6 @@ public class UsageStatsService extends SystemService implements
}
@Override
- public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
- mAppStandby.addListener(listener);
- }
-
- @Override
- public void removeAppIdleStateChangeListener(
- AppIdleStateChangeListener listener) {
- mAppStandby.removeListener(listener);
- }
-
- @Override
public byte[] getBackupPayload(int user, String key) {
synchronized (mLock) {
if (!mUserUnlockedStates.get(user)) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 23df1c553de2..5783932db488 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -146,17 +146,16 @@ class UserUsageStatsService {
}
// During system reboot, add a DEVICE_SHUTDOWN event to the end of event list, the timestamp
- // is last time UsageStatsDatabase is persisted to disk.
+ // is last time UsageStatsDatabase is persisted to disk or the last event's time whichever
+ // is higher (because the file system timestamp is round down to integral seconds).
// Also add a DEVICE_STARTUP event with current system timestamp.
final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
if (currentDailyStats != null) {
- // File system timestamp only has precision of 1 second, add 1000ms to make up
- // for the loss of round up.
- final Event shutdownEvent =
- new Event(DEVICE_SHUTDOWN, currentDailyStats.lastTimeSaved + 1000);
+ final Event shutdownEvent = new Event(DEVICE_SHUTDOWN,
+ Math.max(currentDailyStats.lastTimeSaved, currentDailyStats.endTime));
shutdownEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
currentDailyStats.addEvent(shutdownEvent);
- final Event startupEvent = new Event(DEVICE_STARTUP, currentTimeMillis);
+ final Event startupEvent = new Event(DEVICE_STARTUP, System.currentTimeMillis());
startupEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
currentDailyStats.addEvent(startupEvent);
}
diff --git a/services/usb/java/com/android/server/usb/UsbPermissionManager.java b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
index ef9ee73a9b68..1e46f981f117 100644
--- a/services/usb/java/com/android/server/usb/UsbPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
@@ -20,14 +20,20 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.usb.UsbSettingsManagerProto;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+import java.util.List;
class UsbPermissionManager {
private static final String LOG_TAG = UsbPermissionManager.class.getSimpleName();
@@ -112,4 +118,18 @@ class UsbPermissionManager {
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
+ void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ synchronized (mPermissionsByUser) {
+ List<UserInfo> users = userManager.getUsers();
+ int numUsers = users.size();
+ for (int i = 0; i < numUsers; i++) {
+ getPermissionsForUser(users.get(i).id).dump(dump, "user_permissions",
+ UsbSettingsManagerProto.USER_SETTINGS);
+ }
+ }
+ dump.end(token);
+ }
+
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index ce6f592e2b0d..04936377bbfb 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -740,6 +740,8 @@ public class UsbService extends IUsbManager.Stub {
mSettingsManager.dump(dump, "settings_manager",
UsbServiceDumpProto.SETTINGS_MANAGER);
+ mPermissionManager.dump(dump, "permissions_manager",
+ UsbServiceDumpProto.PERMISSIONS_MANAGER);
dump.flush();
} else if ("set-port-roles".equals(args[0]) && args.length == 4) {
final String portId = args[1];
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 0cb64a37f9c1..e700f19adbd4 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -36,9 +36,12 @@ import android.os.Binder;
import android.os.Environment;
import android.os.Process;
import android.os.UserHandle;
-import android.service.usb.UsbSettingsAccessoryPermissionProto;
-import android.service.usb.UsbSettingsDevicePermissionProto;
-import android.service.usb.UsbUserSettingsManagerProto;
+import android.service.usb.UsbAccessoryPermissionProto;
+import android.service.usb.UsbAccessoryPersistentPermissionProto;
+import android.service.usb.UsbDevicePermissionProto;
+import android.service.usb.UsbDevicePersistentPermissionProto;
+import android.service.usb.UsbUidPermissionProto;
+import android.service.usb.UsbUserPermissionsManagerProto;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
@@ -261,7 +264,7 @@ class UsbUserPermissionManager {
}
if (isChanged) {
- scheduleWritePermissionLocked();
+ scheduleWritePermissionsLocked();
}
}
}
@@ -288,7 +291,7 @@ class UsbUserPermissionManager {
}
if (isChanged) {
- scheduleWritePermissionLocked();
+ scheduleWritePermissionsLocked();
}
}
}
@@ -370,7 +373,7 @@ class UsbUserPermissionManager {
}
@GuardedBy("mLock")
- private void scheduleWritePermissionLocked() {
+ private void scheduleWritePermissionsLocked() {
if (mIsCopyPermissionsScheduled) {
return;
}
@@ -393,15 +396,18 @@ class UsbUserPermissionManager {
devices = new DeviceFilter[numDevices];
uidsForDevices = new int[numDevices][];
grantedValuesForDevices = new boolean[numDevices][];
- for (int i = 0; i < numDevices; i++) {
- devices[i] = new DeviceFilter(mDevicePersistentPermissionMap.keyAt(i));
- SparseBooleanArray permissions = mDevicePersistentPermissionMap.valueAt(i);
+ for (int deviceIdx = 0; deviceIdx < numDevices; deviceIdx++) {
+ devices[deviceIdx] =
+ new DeviceFilter(mDevicePersistentPermissionMap.keyAt(deviceIdx));
+ SparseBooleanArray permissions =
+ mDevicePersistentPermissionMap.valueAt(deviceIdx);
int numPermissions = permissions.size();
- uidsForDevices[i] = new int[numPermissions];
- grantedValuesForDevices[i] = new boolean[numPermissions];
- for (int j = 0; j < numPermissions; j++) {
- uidsForDevices[i][j] = permissions.keyAt(j);
- grantedValuesForDevices[i][j] = permissions.valueAt(j);
+ uidsForDevices[deviceIdx] = new int[numPermissions];
+ grantedValuesForDevices[deviceIdx] = new boolean[numPermissions];
+ for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
+ uidsForDevices[deviceIdx][permissionIdx] = permissions.keyAt(permissionIdx);
+ grantedValuesForDevices[deviceIdx][permissionIdx] =
+ permissions.valueAt(permissionIdx);
}
}
@@ -409,16 +415,19 @@ class UsbUserPermissionManager {
accessories = new AccessoryFilter[numAccessories];
uidsForAccessories = new int[numAccessories][];
grantedValuesForAccessories = new boolean[numAccessories][];
- for (int i = 0; i < numAccessories; i++) {
- accessories[i] =
- new AccessoryFilter(mAccessoryPersistentPermissionMap.keyAt(i));
- SparseBooleanArray permissions = mAccessoryPersistentPermissionMap.valueAt(i);
+ for (int accessoryIdx = 0; accessoryIdx < numAccessories; accessoryIdx++) {
+ accessories[accessoryIdx] = new AccessoryFilter(
+ mAccessoryPersistentPermissionMap.keyAt(accessoryIdx));
+ SparseBooleanArray permissions =
+ mAccessoryPersistentPermissionMap.valueAt(accessoryIdx);
int numPermissions = permissions.size();
- uidsForAccessories[i] = new int[numPermissions];
- grantedValuesForAccessories[i] = new boolean[numPermissions];
- for (int j = 0; j < numPermissions; j++) {
- uidsForAccessories[i][j] = permissions.keyAt(j);
- grantedValuesForAccessories[i][j] = permissions.valueAt(j);
+ uidsForAccessories[accessoryIdx] = new int[numPermissions];
+ grantedValuesForAccessories[accessoryIdx] = new boolean[numPermissions];
+ for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
+ uidsForAccessories[accessoryIdx][permissionIdx] =
+ permissions.keyAt(permissionIdx);
+ grantedValuesForAccessories[accessoryIdx][permissionIdx] =
+ permissions.valueAt(permissionIdx);
}
}
mIsCopyPermissionsScheduled = false;
@@ -477,22 +486,22 @@ class UsbUserPermissionManager {
* Creates UI dialog to request permission for the given package to access the device
* or accessory.
*
- * @param device The USB device attached
- * @param accessory The USB accessory attached
+ * @param device The USB device attached
+ * @param accessory The USB accessory attached
* @param canBeDefault Whether the calling pacakge can set as default handler
- * of the USB device or accessory
- * @param packageName The package name of the calling package
- * @param uid The uid of the calling package
- * @param userContext The context to start the UI dialog
- * @param pi PendingIntent for returning result
+ * of the USB device or accessory
+ * @param packageName The package name of the calling package
+ * @param uid The uid of the calling package
+ * @param userContext The context to start the UI dialog
+ * @param pi PendingIntent for returning result
*/
void requestPermissionDialog(@Nullable UsbDevice device,
- @Nullable UsbAccessory accessory,
- boolean canBeDefault,
- @NonNull String packageName,
- int uid,
- @NonNull Context userContext,
- @NonNull PendingIntent pi) {
+ @Nullable UsbAccessory accessory,
+ boolean canBeDefault,
+ @NonNull String packageName,
+ int uid,
+ @NonNull Context userContext,
+ @NonNull PendingIntent pi) {
long identity = Binder.clearCallingIdentity();
Intent intent = new Intent();
if (device != null) {
@@ -517,48 +526,96 @@ class UsbUserPermissionManager {
}
}
- void dump(@NonNull DualDumpOutputStream dump) {
+ void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
synchronized (mLock) {
- for (String deviceName : mDevicePermissionMap.keySet()) {
+ dump.write("user_id", UsbUserPermissionsManagerProto.USER_ID, mUser.getIdentifier());
+ int numMappings = mDevicePermissionMap.size();
+ for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
+ String deviceName = mDevicePermissionMap.keyAt(mappingsIdx);
long devicePermissionToken = dump.start("device_permissions",
- UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
+ UsbUserPermissionsManagerProto.DEVICE_PERMISSIONS);
- dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
+ dump.write("device_name", UsbDevicePermissionProto.DEVICE_NAME, deviceName);
- SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
- int count = uidList.size();
- for (int i = 0; i < count; i++) {
- dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
+ SparseBooleanArray uidList = mDevicePermissionMap.valueAt(mappingsIdx);
+ int numUids = uidList.size();
+ for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
+ dump.write("uids", UsbDevicePermissionProto.UIDS, uidList.keyAt(uidsIdx));
}
dump.end(devicePermissionToken);
}
- for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
+ numMappings = mAccessoryPermissionMap.size();
+ for (int mappingsIdx = 0; mappingsIdx < numMappings; ++mappingsIdx) {
+ UsbAccessory accessory = mAccessoryPermissionMap.keyAt(mappingsIdx);
long accessoryPermissionToken = dump.start("accessory_permissions",
- UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
+ UsbUserPermissionsManagerProto.ACCESSORY_PERMISSIONS);
dump.write("accessory_description",
- UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
+ UsbAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
accessory.getDescription());
- SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
- int count = uidList.size();
- for (int i = 0; i < count; i++) {
- dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
+ SparseBooleanArray uidList = mAccessoryPermissionMap.valueAt(mappingsIdx);
+ int numUids = uidList.size();
+ for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
+ dump.write("uids", UsbAccessoryPermissionProto.UIDS, uidList.keyAt(uidsIdx));
}
dump.end(accessoryPermissionToken);
}
+
+ numMappings = mDevicePersistentPermissionMap.size();
+ for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
+ DeviceFilter filter = mDevicePersistentPermissionMap.keyAt(mappingsIdx);
+ long devicePermissionToken = dump.start("device_persistent_permissions",
+ UsbUserPermissionsManagerProto.DEVICE_PERSISTENT_PERMISSIONS);
+ filter.dump(dump, "device",
+ UsbDevicePersistentPermissionProto.DEVICE_FILTER);
+ SparseBooleanArray permissions =
+ mDevicePersistentPermissionMap.valueAt(mappingsIdx);
+ int numPermissions = permissions.size();
+ for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
+ long uidPermissionToken = dump.start("uid_permission",
+ UsbDevicePersistentPermissionProto.PERMISSION_VALUES);
+ dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
+ dump.write("is_granted",
+ UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
+ dump.end(uidPermissionToken);
+ }
+ dump.end(devicePermissionToken);
+ }
+
+ numMappings = mAccessoryPersistentPermissionMap.size();
+ for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
+ AccessoryFilter filter = mAccessoryPersistentPermissionMap.keyAt(mappingsIdx);
+ long accessoryPermissionToken = dump.start("accessory_persistent_permissions",
+ UsbUserPermissionsManagerProto.ACCESSORY_PERSISTENT_PERMISSIONS);
+ filter.dump(dump, "accessory",
+ UsbAccessoryPersistentPermissionProto.ACCESSORY_FILTER);
+ SparseBooleanArray permissions =
+ mAccessoryPersistentPermissionMap.valueAt(mappingsIdx);
+ int numPermissions = permissions.size();
+ for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
+ long uidPermissionToken = dump.start("uid_permission",
+ UsbAccessoryPersistentPermissionProto.PERMISSION_VALUES);
+ dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
+ dump.write("is_granted",
+ UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
+ dump.end(uidPermissionToken);
+ }
+ dump.end(accessoryPermissionToken);
+ }
}
+ dump.end(token);
}
/**
* Check for camera permission of the calling process.
*
* @param packageName Package name of the caller.
- * @param uid Linux uid of the calling process.
- *
+ * @param uid Linux uid of the calling process.
* @return True in case camera permission is available, False otherwise.
*/
private boolean isCameraPermissionGranted(String packageName, int uid) {
@@ -677,7 +734,7 @@ class UsbUserPermissionManager {
*
* @param device The device that needs to get scanned
* @return True in case a VIDEO device or interface is present,
- * False otherwise.
+ * False otherwise.
*/
private boolean isCameraDevicePresent(UsbDevice device) {
if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) {
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index 2ff26b8a5cde..3f20273a8d61 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -18,9 +18,12 @@ android_app {
name: "startop_test_app",
srcs: [
"src/ComplexLayoutInflationActivity.java",
- "src/CPUIntensive.java",
+ "src/CPUIntensiveBenchmarkActivity.java",
+ "src/CPUIntensiveBenchmarks.java",
"src/EmptyActivity.java",
"src/FrameLayoutInflationActivity.java",
+ "src/InitCheckOverheadBenchmarkActivity.java",
+ "src/InitCheckOverheadBenchmarks.java",
"src/LayoutInflationActivity.java",
"src/NonInteractiveSystemServerBenchmarkActivity.java",
"src/SystemServerBenchmarkActivity.java",
diff --git a/startop/apps/test/AndroidManifest.xml b/startop/apps/test/AndroidManifest.xml
index ebe2584c2d32..235aa0d25e86 100644
--- a/startop/apps/test/AndroidManifest.xml
+++ b/startop/apps/test/AndroidManifest.xml
@@ -37,6 +37,18 @@
</activity>
<activity
+ android:label="CPU Intensive Benchmark Test"
+ android:name=".CPUIntensiveBenchmarkActivity"
+ android:exported="true" >
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:label="Empty Activity Layout Test"
android:name=".EmptyActivity"
android:exported="true" >
@@ -61,6 +73,18 @@
</activity>
<activity
+ android:label="Initialization Check Overhead Test"
+ android:name=".InitCheckOverheadBenchmarkActivity"
+ android:exported="true" >
+
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:label="TextView Layout Test"
android:name=".TextViewInflationActivity"
android:exported="true" >
diff --git a/startop/apps/test/src/CPUIntensiveBenchmarkActivity.java b/startop/apps/test/src/CPUIntensiveBenchmarkActivity.java
new file mode 100644
index 000000000000..2ec5308afe14
--- /dev/null
+++ b/startop/apps/test/src/CPUIntensiveBenchmarkActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import android.os.Bundle;
+
+public class CPUIntensiveBenchmarkActivity extends SystemServerBenchmarkActivity {
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.system_server_benchmark_page);
+
+ mBenchmarkList = findViewById(R.id.benchmark_list);
+
+ CPUIntensiveBenchmarks.initializeBenchmarks(this, this);
+ }
+}
diff --git a/startop/apps/test/src/CPUIntensive.java b/startop/apps/test/src/CPUIntensiveBenchmarks.java
index a411e8ce8ce0..19d0b6372c1b 100644
--- a/startop/apps/test/src/CPUIntensive.java
+++ b/startop/apps/test/src/CPUIntensiveBenchmarks.java
@@ -20,10 +20,11 @@
package com.android.startop.test;
-final class CPUIntensive {
- public static final int THREAD_COUNT = 8;
+import android.app.Activity;
+
+public class CPUIntensiveBenchmarks {
public static final int ARRAY_SIZE = 30000;
- public static int[][] array = new int[THREAD_COUNT][ARRAY_SIZE];
+ public static int[][] mArray;
static class WorkerThread extends Thread {
int mThreadNumber;
@@ -31,21 +32,22 @@ final class CPUIntensive {
mThreadNumber = number;
}
public void run() {
- final int arrayLength = array[mThreadNumber].length;
+ final int arrayLength = mArray[mThreadNumber].length;
for (int i = 0; i < arrayLength; ++i) {
- array[mThreadNumber][i] = i * i;
+ mArray[mThreadNumber][i] = i * i;
}
for (int i = 0; i < arrayLength; ++i) {
for (int j = 0; j < arrayLength; ++j) {
- int swap = array[mThreadNumber][j];
- array[mThreadNumber][j] = array[mThreadNumber][(j + i) % arrayLength];
- array[mThreadNumber][(j + i) % arrayLength] = swap;
+ int swap = mArray[mThreadNumber][j];
+ mArray[mThreadNumber][j] = mArray[mThreadNumber][(j + i) % arrayLength];
+ mArray[mThreadNumber][(j + i) % arrayLength] = swap;
}
}
}
};
- public static void doSomeWork(int threadCount) {
+ static void doSomeWork(int threadCount) {
+ mArray = new int[threadCount][ARRAY_SIZE];
WorkerThread[] threads = new WorkerThread[threadCount];
// Create the threads.
for (int i = 0; i < threadCount; ++i) {
@@ -63,5 +65,25 @@ final class CPUIntensive {
}
}
}
-}
+ // Time limit to run benchmarks in seconds
+ public static final int TIME_LIMIT = 5;
+
+ static void initializeBenchmarks(Activity parent, BenchmarkRunner benchmarks) {
+ benchmarks.addBenchmark("Use 1 thread", () -> {
+ doSomeWork(1);
+ });
+ benchmarks.addBenchmark("Use 2 threads", () -> {
+ doSomeWork(2);
+ });
+ benchmarks.addBenchmark("Use 4 threads", () -> {
+ doSomeWork(4);
+ });
+ benchmarks.addBenchmark("Use 8 threads", () -> {
+ doSomeWork(8);
+ });
+ benchmarks.addBenchmark("Use 16 threads", () -> {
+ doSomeWork(16);
+ });
+ }
+}
diff --git a/startop/apps/test/src/InitCheckOverheadBenchmarkActivity.java b/startop/apps/test/src/InitCheckOverheadBenchmarkActivity.java
new file mode 100644
index 000000000000..3e0e3b113a29
--- /dev/null
+++ b/startop/apps/test/src/InitCheckOverheadBenchmarkActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import android.os.Bundle;
+
+public class InitCheckOverheadBenchmarkActivity extends SystemServerBenchmarkActivity {
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.system_server_benchmark_page);
+
+ mBenchmarkList = findViewById(R.id.benchmark_list);
+
+ InitCheckOverheadBenchmarks.initializeBenchmarks(this, this);
+ }
+}
diff --git a/startop/apps/test/src/InitCheckOverheadBenchmarks.java b/startop/apps/test/src/InitCheckOverheadBenchmarks.java
new file mode 100644
index 000000000000..79adbbce45ff
--- /dev/null
+++ b/startop/apps/test/src/InitCheckOverheadBenchmarks.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A test of initialization check costs for AOT.
+ */
+
+package com.android.startop.test;
+
+import android.app.Activity;
+
+import java.util.Random;
+
+public class InitCheckOverheadBenchmarks {
+ public static int mSum;
+ public static int mSum2;
+ public static int mStep;
+ public static int mStep2;
+ public static int mStartingValue;
+
+ static {
+ Random random = new Random();
+ mStep = random.nextInt();
+ mStep2 = random.nextInt();
+ mStartingValue = random.nextInt();
+ };
+
+ static class OtherClass {
+ public static int mStep;
+ public static int mStep2;
+ public static int mStartingValue;
+ static {
+ Random random = new Random();
+ mStep = random.nextInt();
+ mStep2 = random.nextInt();
+ mStartingValue = random.nextInt();
+ };
+ };
+
+ public static void localStaticFor(int iterationCount) {
+ for (int i = 0; i < iterationCount; ++i) {
+ mSum += mStep;
+ }
+ }
+
+ public static void nonLocalStaticFor(int iterationCount) {
+ mSum = OtherClass.mStartingValue;
+ for (int i = 0; i < iterationCount; ++i) {
+ mSum += OtherClass.mStep;
+ }
+ }
+
+ public static void localStaticForTwo(int iterationCount) {
+ for (int i = 0; i < iterationCount; ++i) {
+ mSum += mStep;
+ mSum2 += mStep2;
+ }
+ }
+
+ public static void nonLocalStaticForTwo(int iterationCount) {
+ mSum = OtherClass.mStartingValue;
+ for (int i = 0; i < iterationCount; ++i) {
+ mSum += OtherClass.mStep;
+ mSum2 += OtherClass.mStep2;
+ }
+ }
+
+ public static void localStaticDoWhile(int iterationCount) {
+ int i = 0;
+ do {
+ mSum += mStep;
+ ++i;
+ } while (i < iterationCount);
+ }
+
+ public static void nonLocalStaticDoWhile(int iterationCount) {
+ mSum = OtherClass.mStartingValue;
+ int i = 0;
+ do {
+ mSum += OtherClass.mStep;
+ ++i;
+ } while (i < iterationCount);
+ }
+
+ public static void doGC() {
+ Runtime.getRuntime().gc();
+ }
+
+ // Time limit to run benchmarks in seconds
+ public static final int TIME_LIMIT = 5;
+
+ static void initializeBenchmarks(Activity parent, BenchmarkRunner benchmarks) {
+ benchmarks.addBenchmark("GC", () -> {
+ doGC();
+ });
+
+ benchmarks.addBenchmark("InitCheckFor (local)", () -> {
+ localStaticFor(10000000);
+ });
+
+ benchmarks.addBenchmark("InitCheckFor (non-local)", () -> {
+ nonLocalStaticFor(10000000);
+ });
+
+ benchmarks.addBenchmark("InitCheckForTwo (local)", () -> {
+ localStaticForTwo(10000000);
+ });
+
+ benchmarks.addBenchmark("InitCheckForTwo (non-local)", () -> {
+ nonLocalStaticForTwo(10000000);
+ });
+
+ benchmarks.addBenchmark("InitCheckDoWhile (local)", () -> {
+ localStaticDoWhile(10000000);
+ });
+
+ benchmarks.addBenchmark("InitCheckDoWhile (non-local)", () -> {
+ nonLocalStaticDoWhile(10000000);
+ });
+ }
+}
diff --git a/startop/apps/test/src/SystemServerBenchmarkActivity.java b/startop/apps/test/src/SystemServerBenchmarkActivity.java
index 75ea69b81e02..6be8df3728af 100644
--- a/startop/apps/test/src/SystemServerBenchmarkActivity.java
+++ b/startop/apps/test/src/SystemServerBenchmarkActivity.java
@@ -17,28 +17,20 @@
package com.android.startop.test;
import android.app.Activity;
-import android.app.ActivityManager;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.view.ViewGroup;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.TextView;
public class SystemServerBenchmarkActivity extends Activity implements BenchmarkRunner {
- private GridLayout benchmarkList;
+ protected GridLayout mBenchmarkList;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.system_server_benchmark_page);
- benchmarkList = findViewById(R.id.benchmark_list);
+ mBenchmarkList = findViewById(R.id.benchmark_list);
SystemServerBenchmarks.initializeBenchmarks(this, this);
}
@@ -49,7 +41,7 @@ public class SystemServerBenchmarkActivity extends Activity implements Benchmark
* @param name A short name that shows up in the UI or benchmark results
*/
public void addBenchmark(CharSequence name, Runnable thunk) {
- Context context = benchmarkList.getContext();
+ Context context = mBenchmarkList.getContext();
Button button = new Button(context);
TextView mean = new TextView(context);
TextView stdev = new TextView(context);
@@ -68,8 +60,8 @@ public class SystemServerBenchmarkActivity extends Activity implements Benchmark
});
});
- benchmarkList.addView(button);
- benchmarkList.addView(mean);
- benchmarkList.addView(stdev);
+ mBenchmarkList.addView(button);
+ mBenchmarkList.addView(mean);
+ mBenchmarkList.addView(stdev);
}
}
diff --git a/startop/apps/test/src/SystemServerBenchmarks.java b/startop/apps/test/src/SystemServerBenchmarks.java
index 5918503c87ea..25b43f4d53f6 100644
--- a/startop/apps/test/src/SystemServerBenchmarks.java
+++ b/startop/apps/test/src/SystemServerBenchmarks.java
@@ -60,22 +60,6 @@ class SystemServerBenchmarks {
benchmarks.addBenchmark("Empty", () -> {
});
- benchmarks.addBenchmark("CPU Intensive (1 thread)", () -> {
- CPUIntensive.doSomeWork(1);
- });
-
- benchmarks.addBenchmark("CPU Intensive (2 thread)", () -> {
- CPUIntensive.doSomeWork(2);
- });
-
- benchmarks.addBenchmark("CPU Intensive (4 thread)", () -> {
- CPUIntensive.doSomeWork(4);
- });
-
- benchmarks.addBenchmark("CPU Intensive (8 thread)", () -> {
- CPUIntensive.doSomeWork(8);
- });
-
PackageManager pm = parent.getPackageManager();
benchmarks.addBenchmark("getInstalledApplications", () -> {
pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 3f348a4bda8c..60290e3b785d 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -2049,6 +2049,10 @@ public final class Call {
return "DISCONNECTING";
case STATE_SELECT_PHONE_ACCOUNT:
return "SELECT_PHONE_ACCOUNT";
+ case STATE_SIMULATED_RINGING:
+ return "SIMULATED_RINGING";
+ case STATE_AUDIO_PROCESSING:
+ return "AUDIO_PROCESSING";
default:
Log.w(Call.class, "Unknown state %d", state);
return "UNKNOWN";
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 2d35f8eae816..ee291fa0122b 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -28,17 +28,13 @@ import android.annotation.UnsupportedAppUsage;
import android.app.ActivityThread;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.CursorWindow;
import android.net.Uri;
-import android.os.Binder;
-import android.os.BaseBundle;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.provider.Telephony;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -290,12 +286,6 @@ public final class SmsManager {
*/
public static final int SMS_MESSAGE_PERIOD_NOT_SPECIFIED = -1;
- /**
- * Extra key passed into a PendingIntent when the SMS operation failed due to there being no
- * default set.
- */
- private static final String NO_DEFAULT_EXTRA = "noDefault";
-
// result of asking the user for a subscription to perform an operation.
private interface SubscriptionResolverResult {
void onSuccess(int subId);
@@ -336,9 +326,59 @@ public final class SmsManager {
* <code>RESULT_ERROR_RADIO_OFF</code><br>
* <code>RESULT_ERROR_NULL_PDU</code><br>
* <code>RESULT_ERROR_NO_SERVICE</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>RESULT_NETWORK_REJECT</code><br>
+ * <code>RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>RESULT_INVALID_STATE</code><br>
+ * <code>RESULT_NO_MEMORY</code><br>
+ * <code>RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>RESULT_SYSTEM_ERROR</code><br>
+ * <code>RESULT_MODEM_ERROR</code><br>
+ * <code>RESULT_NETWORK_ERROR</code><br>
+ * <code>RESULT_ENCODING_ERROR</code><br>
+ * <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>RESULT_INTERNAL_ERROR</code><br>
+ * <code>RESULT_NO_RESOURCES</code><br>
+ * <code>RESULT_CANCELLED</code><br>
+ * <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>RESULT_RIL_INVALID_STATE</code><br>
+ * <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>RESULT_RIL_NO_MEMORY</code><br>
+ * <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>RESULT_RIL_MODEM_ERR</code><br>
+ * <code>RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>RESULT_RIL_CANCELLED</code><br>
+ * <code>RESULT_RIL_SIM_ABSENT</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -378,9 +418,60 @@ public final class SmsManager {
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
* <code>RESULT_ERROR_RADIO_OFF</code><br>
* <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>RESULT_NETWORK_REJECT</code><br>
+ * <code>RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>RESULT_INVALID_STATE</code><br>
+ * <code>RESULT_NO_MEMORY</code><br>
+ * <code>RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>RESULT_SYSTEM_ERROR</code><br>
+ * <code>RESULT_MODEM_ERROR</code><br>
+ * <code>RESULT_NETWORK_ERROR</code><br>
+ * <code>RESULT_ENCODING_ERROR</code><br>
+ * <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>RESULT_INTERNAL_ERROR</code><br>
+ * <code>RESULT_NO_RESOURCES</code><br>
+ * <code>RESULT_CANCELLED</code><br>
+ * <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>RESULT_RIL_INVALID_STATE</code><br>
+ * <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>RESULT_RIL_NO_MEMORY</code><br>
+ * <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>RESULT_RIL_MODEM_ERR</code><br>
+ * <code>RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>RESULT_RIL_CANCELLED</code><br>
+ * <code>RESULT_RIL_SIM_ABSENT</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -451,13 +542,13 @@ public final class SmsManager {
} catch (RemoteException e) {
Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - "
+ e.getMessage());
- notifySmsGenericError(sentIntent);
+ notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
}
}
@Override
public void onFailure() {
- notifySmsErrorNoDefaultSet(context, sentIntent);
+ notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
}
});
} else {
@@ -471,7 +562,7 @@ public final class SmsManager {
} catch (RemoteException e) {
Log.e(TAG, "sendTextMessageInternal (no persist): Couldn't send SMS, exception - "
+ e.getMessage());
- notifySmsGenericError(sentIntent);
+ notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
}
}
}
@@ -564,13 +655,13 @@ public final class SmsManager {
} catch (RemoteException e) {
Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - "
+ e.getMessage());
- notifySmsGenericError(sentIntent);
+ notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
}
}
@Override
public void onFailure() {
- notifySmsErrorNoDefaultSet(context, sentIntent);
+ notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
}
});
} else {
@@ -586,7 +677,7 @@ public final class SmsManager {
} catch (RemoteException e) {
Log.e(TAG, "sendTextMessageInternal(no persist): Couldn't send SMS, exception - "
+ e.getMessage());
- notifySmsGenericError(sentIntent);
+ notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
}
}
}
@@ -668,7 +759,7 @@ public final class SmsManager {
} catch (RemoteException ex) {
try {
if (receivedIntent != null) {
- receivedIntent.send(Telephony.Sms.Intents.RESULT_SMS_GENERIC_ERROR);
+ receivedIntent.send(RESULT_REMOTE_EXCEPTION);
}
} catch (PendingIntent.CanceledException cx) {
// Don't worry about it, we do not need to notify the caller if this is the case.
@@ -724,12 +815,63 @@ public final class SmsManager {
* broadcast when the corresponding message part has been sent.
* The result code will be <code>Activity.RESULT_OK</code> for success,
* or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>RESULT_NETWORK_REJECT</code><br>
+ * <code>RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>RESULT_INVALID_STATE</code><br>
+ * <code>RESULT_NO_MEMORY</code><br>
+ * <code>RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>RESULT_SYSTEM_ERROR</code><br>
+ * <code>RESULT_MODEM_ERROR</code><br>
+ * <code>RESULT_NETWORK_ERROR</code><br>
+ * <code>RESULT_ENCODING_ERROR</code><br>
+ * <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>RESULT_INTERNAL_ERROR</code><br>
+ * <code>RESULT_NO_RESOURCES</code><br>
+ * <code>RESULT_CANCELLED</code><br>
+ * <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>RESULT_RIL_INVALID_STATE</code><br>
+ * <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>RESULT_RIL_NO_MEMORY</code><br>
+ * <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>RESULT_RIL_MODEM_ERR</code><br>
+ * <code>RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>RESULT_RIL_CANCELLED</code><br>
+ * <code>RESULT_RIL_SIM_ABSENT</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -811,13 +953,13 @@ public final class SmsManager {
} catch (RemoteException e) {
Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
+ e.getMessage());
- notifySmsGenericError(sentIntents);
+ notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
}
}
@Override
public void onFailure() {
- notifySmsErrorNoDefaultSet(context, sentIntents);
+ notifySmsError(sentIntents, RESULT_NO_DEFAULT_SMS_APP);
}
});
} else {
@@ -832,7 +974,7 @@ public final class SmsManager {
} catch (RemoteException e) {
Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
+ e.getMessage());
- notifySmsGenericError(sentIntents);
+ notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
}
}
} else {
@@ -911,12 +1053,63 @@ public final class SmsManager {
* broadcast when the corresponding message part has been sent.
* The result code will be <code>Activity.RESULT_OK</code> for success,
* or one of these errors:<br>
- * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
- * <code>RESULT_ERROR_RADIO_OFF</code><br>
- * <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>RESULT_NETWORK_REJECT</code><br>
+ * <code>RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>RESULT_INVALID_STATE</code><br>
+ * <code>RESULT_NO_MEMORY</code><br>
+ * <code>RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>RESULT_SYSTEM_ERROR</code><br>
+ * <code>RESULT_MODEM_ERROR</code><br>
+ * <code>RESULT_NETWORK_ERROR</code><br>
+ * <code>RESULT_ENCODING_ERROR</code><br>
+ * <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>RESULT_INTERNAL_ERROR</code><br>
+ * <code>RESULT_NO_RESOURCES</code><br>
+ * <code>RESULT_CANCELLED</code><br>
+ * <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>RESULT_RIL_INVALID_STATE</code><br>
+ * <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>RESULT_RIL_NO_MEMORY</code><br>
+ * <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>RESULT_RIL_MODEM_ERR</code><br>
+ * <code>RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>RESULT_RIL_CANCELLED</code><br>
+ * <code>RESULT_RIL_SIM_ABSENT</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -994,13 +1187,13 @@ public final class SmsManager {
} catch (RemoteException e) {
Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
+ e.getMessage());
- notifySmsGenericError(sentIntents);
+ notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
}
}
@Override
public void onFailure() {
- notifySmsErrorNoDefaultSet(context, sentIntents);
+ notifySmsError(sentIntents, RESULT_NO_DEFAULT_SMS_APP);
}
});
} else {
@@ -1016,7 +1209,7 @@ public final class SmsManager {
} catch (RemoteException e) {
Log.e(TAG, "sendMultipartTextMessageInternal (no persist): Couldn't send SMS - "
+ e.getMessage());
- notifySmsGenericError(sentIntents);
+ notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
}
}
} else {
@@ -1061,9 +1254,60 @@ public final class SmsManager {
* <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
* <code>RESULT_ERROR_RADIO_OFF</code><br>
* <code>RESULT_ERROR_NULL_PDU</code><br>
- * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
- * the extra "errorCode" containing a radio technology specific value,
- * generally only useful for troubleshooting.<br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>RESULT_ERROR_NO_SERVICE</code><br>
+ * <code>RESULT_ERROR_LIMIT_EXCEEDED</code><br>
+ * <code>RESULT_ERROR_FDN_CHECK_FAILURE</code><br>
+ * <code>RESULT_ERROR_SHORT_CODE_NOT_ALLOWED</code><br>
+ * <code>RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED</code><br>
+ * <code>RESULT_RADIO_NOT_AVAILABLE</code><br>
+ * <code>RESULT_NETWORK_REJECT</code><br>
+ * <code>RESULT_INVALID_ARGUMENTS</code><br>
+ * <code>RESULT_INVALID_STATE</code><br>
+ * <code>RESULT_NO_MEMORY</code><br>
+ * <code>RESULT_INVALID_SMS_FORMAT</code><br>
+ * <code>RESULT_SYSTEM_ERROR</code><br>
+ * <code>RESULT_MODEM_ERROR</code><br>
+ * <code>RESULT_NETWORK_ERROR</code><br>
+ * <code>RESULT_ENCODING_ERROR</code><br>
+ * <code>RESULT_INVALID_SMSC_ADDRESS</code><br>
+ * <code>RESULT_OPERATION_NOT_ALLOWED</code><br>
+ * <code>RESULT_INTERNAL_ERROR</code><br>
+ * <code>RESULT_NO_RESOURCES</code><br>
+ * <code>RESULT_CANCELLED</code><br>
+ * <code>RESULT_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>RESULT_NO_BLUETOOTH_SERVICE</code><br>
+ * <code>RESULT_INVALID_BLUETOOTH_ADDRESS</code><br>
+ * <code>RESULT_BLUETOOTH_DISCONNECTED</code><br>
+ * <code>RESULT_UNEXPECTED_EVENT_STOP_SENDING</code><br>
+ * <code>RESULT_SMS_BLOCKED_DURING_EMERGENCY</code><br>
+ * <code>RESULT_SMS_SEND_RETRY_FAILED</code><br>
+ * <code>RESULT_REMOTE_EXCEPTION</code><br>
+ * <code>RESULT_NO_DEFAULT_SMS_APP</code><br>
+ * <code>RESULT_RIL_RADIO_NOT_AVAILABLE</code><br>
+ * <code>RESULT_RIL_SMS_SEND_FAIL_RETRY</code><br>
+ * <code>RESULT_RIL_NETWORK_REJECT</code><br>
+ * <code>RESULT_RIL_INVALID_STATE</code><br>
+ * <code>RESULT_RIL_INVALID_ARGUMENTS</code><br>
+ * <code>RESULT_RIL_NO_MEMORY</code><br>
+ * <code>RESULT_RIL_REQUEST_RATE_LIMITED</code><br>
+ * <code>RESULT_RIL_INVALID_SMS_FORMAT</code><br>
+ * <code>RESULT_RIL_SYSTEM_ERR</code><br>
+ * <code>RESULT_RIL_ENCODING_ERR</code><br>
+ * <code>RESULT_RIL_INVALID_SMSC_ADDRESS</code><br>
+ * <code>RESULT_RIL_MODEM_ERR</code><br>
+ * <code>RESULT_RIL_NETWORK_ERR</code><br>
+ * <code>RESULT_RIL_INTERNAL_ERR</code><br>
+ * <code>RESULT_RIL_REQUEST_NOT_SUPPORTED</code><br>
+ * <code>RESULT_RIL_INVALID_MODEM_STATE</code><br>
+ * <code>RESULT_RIL_NETWORK_NOT_READY</code><br>
+ * <code>RESULT_RIL_OPERATION_NOT_ALLOWED</code><br>
+ * <code>RESULT_RIL_NO_RESOURCES</code><br>
+ * <code>RESULT_RIL_CANCELLED</code><br>
+ * <code>RESULT_RIL_SIM_ABSENT</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> or any of the RESULT_RIL errors,
+ * the sentIntent may include the extra "errorCode" containing a radio technology specific
+ * value, generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -1095,12 +1339,12 @@ public final class SmsManager {
sentIntent, deliveryIntent);
} catch (RemoteException e) {
Log.e(TAG, "sendDataMessage: Couldn't send SMS - Exception: " + e.getMessage());
- notifySmsGenericError(sentIntent);
+ notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
}
}
@Override
public void onFailure() {
- notifySmsErrorNoDefaultSet(context, sentIntent);
+ notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP);
}
});
}
@@ -1305,53 +1549,20 @@ public final class SmsManager {
return binder;
}
- private static void notifySmsErrorNoDefaultSet(Context context, PendingIntent pendingIntent) {
- if (pendingIntent != null) {
- Intent errorMessage = new Intent();
- errorMessage.putExtra(NO_DEFAULT_EXTRA, true);
- try {
- pendingIntent.send(context, RESULT_ERROR_GENERIC_FAILURE, errorMessage);
- } catch (PendingIntent.CanceledException e) {
- // Don't worry about it, we do not need to notify the caller if this is the case.
- }
- }
- }
-
- private static void notifySmsErrorNoDefaultSet(Context context,
- List<PendingIntent> pendingIntents) {
- if (pendingIntents != null) {
- for (PendingIntent pendingIntent : pendingIntents) {
- Intent errorMessage = new Intent();
- errorMessage.putExtra(NO_DEFAULT_EXTRA, true);
- try {
- pendingIntent.send(context, RESULT_ERROR_GENERIC_FAILURE, errorMessage);
- } catch (PendingIntent.CanceledException e) {
- // Don't worry about it, we do not need to notify the caller if this is the
- // case.
- }
- }
- }
- }
-
- private static void notifySmsGenericError(PendingIntent pendingIntent) {
+ private static void notifySmsError(PendingIntent pendingIntent, int error) {
if (pendingIntent != null) {
try {
- pendingIntent.send(RESULT_ERROR_GENERIC_FAILURE);
+ pendingIntent.send(error);
} catch (PendingIntent.CanceledException e) {
// Don't worry about it, we do not need to notify the caller if this is the case.
}
}
}
- private static void notifySmsGenericError(List<PendingIntent> pendingIntents) {
+ private static void notifySmsError(List<PendingIntent> pendingIntents, int error) {
if (pendingIntents != null) {
for (PendingIntent pendingIntent : pendingIntents) {
- try {
- pendingIntent.send(RESULT_ERROR_GENERIC_FAILURE);
- } catch (PendingIntent.CanceledException e) {
- // Don't worry about it, we do not need to notify the caller if this is the
- // case.
- }
+ notifySmsError(pendingIntent, error);
}
}
}
@@ -1798,19 +2009,19 @@ public final class SmsManager {
// see SmsMessage.getStatusOnIcc
/** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
- static public final int STATUS_ON_ICC_FREE = 0;
+ public static final int STATUS_ON_ICC_FREE = 0;
/** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
- static public final int STATUS_ON_ICC_READ = 1;
+ public static final int STATUS_ON_ICC_READ = 1;
/** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
- static public final int STATUS_ON_ICC_UNREAD = 3;
+ public static final int STATUS_ON_ICC_UNREAD = 3;
/** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
- static public final int STATUS_ON_ICC_SENT = 5;
+ public static final int STATUS_ON_ICC_SENT = 5;
/** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
- static public final int STATUS_ON_ICC_UNSENT = 7;
+ public static final int STATUS_ON_ICC_UNSENT = 7;
// SMS send failure result codes
@@ -1846,126 +2057,263 @@ public final class SmsManager {
/**
* No error.
- * @hide
*/
- @SystemApi
- static public final int RESULT_ERROR_NONE = 0;
+ public static final int RESULT_ERROR_NONE = 0;
+
/** Generic failure cause */
- static public final int RESULT_ERROR_GENERIC_FAILURE = 1;
+ public static final int RESULT_ERROR_GENERIC_FAILURE = 1;
+
/** Failed because radio was explicitly turned off */
- static public final int RESULT_ERROR_RADIO_OFF = 2;
+ public static final int RESULT_ERROR_RADIO_OFF = 2;
+
/** Failed because no pdu provided */
- static public final int RESULT_ERROR_NULL_PDU = 3;
+ public static final int RESULT_ERROR_NULL_PDU = 3;
+
/** Failed because service is currently unavailable */
- static public final int RESULT_ERROR_NO_SERVICE = 4;
+ public static final int RESULT_ERROR_NO_SERVICE = 4;
+
/** Failed because we reached the sending queue limit. */
- static public final int RESULT_ERROR_LIMIT_EXCEEDED = 5;
+ public static final int RESULT_ERROR_LIMIT_EXCEEDED = 5;
+
/**
* Failed because FDN is enabled.
- * @hide
*/
- @SystemApi
- static public final int RESULT_ERROR_FDN_CHECK_FAILURE = 6;
+ public static final int RESULT_ERROR_FDN_CHECK_FAILURE = 6;
+
/** Failed because user denied the sending of this short code. */
- static public final int RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7;
+ public static final int RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 7;
+
/** Failed because the user has denied this app ever send premium short codes. */
- static public final int RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8;
+ public static final int RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 8;
+
/**
* Failed because the radio was not available
- * @hide
*/
- @SystemApi
- static public final int RESULT_RADIO_NOT_AVAILABLE = 9;
+ public static final int RESULT_RADIO_NOT_AVAILABLE = 9;
+
/**
* Failed because of network rejection
- * @hide
*/
- @SystemApi
- static public final int RESULT_NETWORK_REJECT = 10;
+ public static final int RESULT_NETWORK_REJECT = 10;
+
/**
* Failed because of invalid arguments
- * @hide
*/
- @SystemApi
- static public final int RESULT_INVALID_ARGUMENTS = 11;
+ public static final int RESULT_INVALID_ARGUMENTS = 11;
+
/**
* Failed because of an invalid state
- * @hide
*/
- @SystemApi
- static public final int RESULT_INVALID_STATE = 12;
+ public static final int RESULT_INVALID_STATE = 12;
+
/**
* Failed because there is no memory
- * @hide
*/
- @SystemApi
- static public final int RESULT_NO_MEMORY = 13;
+ public static final int RESULT_NO_MEMORY = 13;
+
/**
* Failed because the sms format is not valid
- * @hide
*/
- @SystemApi
- static public final int RESULT_INVALID_SMS_FORMAT = 14;
+ public static final int RESULT_INVALID_SMS_FORMAT = 14;
+
/**
* Failed because of a system error
- * @hide
*/
- @SystemApi
- static public final int RESULT_SYSTEM_ERROR = 15;
+ public static final int RESULT_SYSTEM_ERROR = 15;
+
/**
* Failed because of a modem error
- * @hide
*/
- @SystemApi
- static public final int RESULT_MODEM_ERROR = 16;
+ public static final int RESULT_MODEM_ERROR = 16;
+
/**
* Failed because of a network error
- * @hide
*/
- @SystemApi
- static public final int RESULT_NETWORK_ERROR = 17;
+ public static final int RESULT_NETWORK_ERROR = 17;
+
/**
* Failed because of an encoding error
- * @hide
*/
- @SystemApi
- static public final int RESULT_ENCODING_ERROR = 18;
+ public static final int RESULT_ENCODING_ERROR = 18;
+
/**
* Failed because of an invalid smsc address
- * @hide
*/
- @SystemApi
- static public final int RESULT_INVALID_SMSC_ADDRESS = 19;
+ public static final int RESULT_INVALID_SMSC_ADDRESS = 19;
+
/**
* Failed because the operation is not allowed
- * @hide
*/
- @SystemApi
- static public final int RESULT_OPERATION_NOT_ALLOWED = 20;
+ public static final int RESULT_OPERATION_NOT_ALLOWED = 20;
+
/**
* Failed because of an internal error
- * @hide
*/
- @SystemApi
- static public final int RESULT_INTERNAL_ERROR = 21;
+ public static final int RESULT_INTERNAL_ERROR = 21;
+
/**
* Failed because there are no resources
- * @hide
*/
- @SystemApi
- static public final int RESULT_NO_RESOURCES = 22;
+ public static final int RESULT_NO_RESOURCES = 22;
+
/**
* Failed because the operation was cancelled
- * @hide
*/
- @SystemApi
- static public final int RESULT_CANCELLED = 23;
+ public static final int RESULT_CANCELLED = 23;
+
/**
* Failed because the request is not supported
- * @hide
*/
- @SystemApi
- static public final int RESULT_REQUEST_NOT_SUPPORTED = 24;
+ public static final int RESULT_REQUEST_NOT_SUPPORTED = 24;
+
+ /**
+ * Failed sending via bluetooth because the bluetooth service is not available
+ */
+ public static final int RESULT_NO_BLUETOOTH_SERVICE = 25;
+
+ /**
+ * Failed sending via bluetooth because the bluetooth device address is invalid
+ */
+ public static final int RESULT_INVALID_BLUETOOTH_ADDRESS = 26;
+
+ /**
+ * Failed sending via bluetooth because bluetooth disconnected
+ */
+ public static final int RESULT_BLUETOOTH_DISCONNECTED = 27;
+
+ /**
+ * Failed sending because the user denied or canceled the dialog displayed for a premium
+ * shortcode sms or rate-limited sms.
+ */
+ public static final int RESULT_UNEXPECTED_EVENT_STOP_SENDING = 28;
+
+ /**
+ * Failed sending during an emergency call
+ */
+ public static final int RESULT_SMS_BLOCKED_DURING_EMERGENCY = 29;
+
+ /**
+ * Failed to send an sms retry
+ */
+ public static final int RESULT_SMS_SEND_RETRY_FAILED = 30;
+
+ /**
+ * Set by BroadcastReceiver to indicate a remote exception while handling a message.
+ */
+ public static final int RESULT_REMOTE_EXCEPTION = 31;
+
+ /**
+ * Set by BroadcastReceiver to indicate there's no default sms app.
+ */
+ public static final int RESULT_NO_DEFAULT_SMS_APP = 32;
+
+ // Radio Error results
+
+ /**
+ * The radio did not start or is resetting.
+ */
+ public static final int RESULT_RIL_RADIO_NOT_AVAILABLE = 100;
+
+ /**
+ * The radio failed to send the sms and needs to retry.
+ */
+ public static final int RESULT_RIL_SMS_SEND_FAIL_RETRY = 101;
+
+ /**
+ * The sms request was rejected by the network.
+ */
+ public static final int RESULT_RIL_NETWORK_REJECT = 102;
+
+ /**
+ * The radio returned an unexpected request for the current state.
+ */
+ public static final int RESULT_RIL_INVALID_STATE = 103;
+
+ /**
+ * The radio received invalid arguments in the request.
+ */
+ public static final int RESULT_RIL_INVALID_ARGUMENTS = 104;
+
+ /**
+ * The radio didn't have sufficient memory to process the request.
+ */
+ public static final int RESULT_RIL_NO_MEMORY = 105;
+
+ /**
+ * The radio denied the operation due to overly-frequent requests.
+ */
+ public static final int RESULT_RIL_REQUEST_RATE_LIMITED = 106;
+
+ /**
+ * The radio returned an error indicating invalid sms format.
+ */
+ public static final int RESULT_RIL_INVALID_SMS_FORMAT = 107;
+
+ /**
+ * The radio encountered a platform or system error.
+ */
+ public static final int RESULT_RIL_SYSTEM_ERR = 108;
+
+ /**
+ * The SMS message was not encoded properly.
+ */
+ public static final int RESULT_RIL_ENCODING_ERR = 109;
+
+ /**
+ * The specified SMSC address was invalid.
+ */
+ public static final int RESULT_RIL_INVALID_SMSC_ADDRESS = 110;
+
+ /**
+ * The vendor RIL received an unexpected or incorrect response.
+ */
+ public static final int RESULT_RIL_MODEM_ERR = 111;
+
+ /**
+ * The radio received an error from the network.
+ */
+ public static final int RESULT_RIL_NETWORK_ERR = 112;
+
+ /**
+ * The modem encountered an unexpected error scenario while handling the request.
+ */
+ public static final int RESULT_RIL_INTERNAL_ERR = 113;
+
+ /**
+ * The request was not supported by the radio.
+ */
+ public static final int RESULT_RIL_REQUEST_NOT_SUPPORTED = 114;
+
+ /**
+ * The radio cannot process the request in the current modem state.
+ */
+ public static final int RESULT_RIL_INVALID_MODEM_STATE = 115;
+
+ /**
+ * The network is not ready to perform the request.
+ */
+ public static final int RESULT_RIL_NETWORK_NOT_READY = 116;
+
+ /**
+ * The radio reports the request is not allowed.
+ */
+ public static final int RESULT_RIL_OPERATION_NOT_ALLOWED = 117;
+
+ /**
+ * There are not sufficient resources to process the request.
+ */
+ public static final int RESULT_RIL_NO_RESOURCES = 118;
+
+ /**
+ * The request has been cancelled.
+ */
+ public static final int RESULT_RIL_CANCELLED = 119;
+
+ /**
+ * The radio failed to set the location where the CDMA subscription
+ * can be retrieved because the SIM or RUIM is absent.
+ */
+ public static final int RESULT_RIL_SIM_ABSENT = 120;
/**
* Send an MMS message
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 06a737041f0d..2eb4809bcb27 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -424,18 +424,8 @@ public class TelephonyManager {
* {@link #getActiveModemCount} returns 1 while this API returns 2.
*/
public @ModemCount int getSupportedModemCount() {
- // TODO: b/139642279 when turning on this feature, remove dependency of
- // PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE and always return result based on
- // PROPERTY_MAX_ACTIVE_MODEMS.
- String rebootRequired = SystemProperties.get(
- TelephonyProperties.PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE);
- if (rebootRequired.equals("false")) {
- // If no reboot is required, return max possible active modems.
- return SystemProperties.getInt(
- TelephonyProperties.PROPERTY_MAX_ACTIVE_MODEMS, getPhoneCount());
- } else {
- return getPhoneCount();
- }
+ return SystemProperties.getInt(TelephonyProperties.PROPERTY_MAX_ACTIVE_MODEMS,
+ getActiveModemCount());
}
/** {@hide} */
@@ -10237,11 +10227,13 @@ public class TelephonyManager {
/**
* Action set from carrier signalling broadcast receivers to enable/disable radio
- * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required.
* @param subId the subscription ID that this action applies to.
* @param enabled control enable or disable radio.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void carrierActionSetRadioEnabled(int subId, boolean enabled) {
try {
ITelephony service = getITelephony();
@@ -10256,11 +10248,13 @@ public class TelephonyManager {
/**
* Action set from carrier signalling broadcast receivers to start/stop reporting default
* network available events
- * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required.
* @param subId the subscription ID that this action applies to.
* @param report control start/stop reporting network status.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void carrierActionReportDefaultNetworkStatus(int subId, boolean report) {
try {
ITelephony service = getITelephony();
@@ -10274,10 +10268,12 @@ public class TelephonyManager {
/**
* Action set from carrier signalling broadcast receivers to reset all carrier actions
- * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required.
* @param subId the subscription ID that this action applies to.
* @hide
*/
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void carrierActionResetAll(int subId) {
try {
ITelephony service = getITelephony();
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 5a90cb13a548..0025c7acb18c 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -830,7 +830,7 @@ public class EuiccManager {
* @param callbackIntent a PendingIntent to launch when the operation completes.
*
* @deprecated From R, callers should specify a flag for specific set of subscriptions to erase
- * and use @link{eraseSubscriptionsWithOptions} instead
+ * and use {@link #eraseSubscriptionsWithOptions(int, PendingIntent)} instead
*
* @hide
*/
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 39e00cc50f98..f79a5c654a63 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1778,21 +1778,6 @@ interface ITelephony {
boolean isInEmergencySmsMode();
/**
- * Get a list of SMS apps on a user.
- */
- String[] getSmsApps(int userId);
-
- /**
- * Get the default SMS app on a given user.
- */
- String getDefaultSmsApp(int userId);
-
- /**
- * Set the default SMS app to a given package on a given user.
- */
- void setDefaultSmsApp(int userId, String packageName);
-
- /**
* Return the modem radio power state for slot index.
*
*/
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index ccdd452b3f1e..81937e6b7005 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -54,11 +54,15 @@ public class BootImageProfileTest implements IDeviceTest {
assertTrue("profile system server not enabled", res != null && res.equals("true"));
}
- private void forceSaveProfile(String pkg) throws Exception {
+ private boolean forceSaveProfile(String pkg) throws Exception {
String pid = mTestDevice.executeShellCommand("pidof " + pkg).trim();
- assertTrue("Invalid pid " + pid, pid.length() > 0);
+ if (pid.length() == 0) {
+ // Not yet running.
+ return false;
+ }
String res = mTestDevice.executeShellCommand("kill -s SIGUSR1 " + pid).trim();
assertTrue("kill SIGUSR1: " + res, res.length() == 0);
+ return true;
}
@Test
@@ -71,10 +75,13 @@ public class BootImageProfileTest implements IDeviceTest {
// Wait up to 20 seconds for the profile to be saved.
for (int i = 0; i < 20; ++i) {
// Force save the profile since we truncated it.
- forceSaveProfile("system_server");
- String s = mTestDevice.executeShellCommand("wc -c <" + SYSTEM_SERVER_PROFILE).trim();
- if (!"0".equals(s)) {
- break;
+ if (forceSaveProfile("system_server")) {
+ // Might fail if system server is not yet running.
+ String s = mTestDevice.executeShellCommand(
+ "wc -c <" + SYSTEM_SERVER_PROFILE).trim();
+ if (!"0".equals(s)) {
+ break;
+ }
}
Thread.sleep(1000);
}
diff --git a/tests/Compatibility/Android.bp b/tests/Compatibility/Android.bp
index 4ca406eba3cf..7dc44fa4a260 100644
--- a/tests/Compatibility/Android.bp
+++ b/tests/Compatibility/Android.bp
@@ -19,4 +19,7 @@ android_test {
srcs: ["src/**/*.java"],
platform_apis: true,
certificate: "platform",
+ test_suites: [
+ "csuite"
+ ],
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index f06b18a0605d..b51aad1d63f8 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -32,6 +32,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
+import android.os.ParcelFileDescriptor;
import android.provider.DeviceConfig;
import android.text.TextUtils;
@@ -46,12 +47,16 @@ import com.android.cts.rollback.lib.Rollback;
import com.android.cts.rollback.lib.RollbackUtils;
import com.android.internal.R;
+import libcore.io.IoUtils;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for rollback of staged installs.
* <p>
@@ -123,6 +128,13 @@ public class StagedRollbackTest {
assertThat(rollback).packagesContainsExactly(
Rollback.from(TestApp.A2).to(TestApp.A1));
assertThat(rollback.isStaged()).isTrue();
+
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
+ Integer.toString(5), false);
+ RollbackUtils.sendCrashBroadcast(TestApp.A, 4);
+ // Sleep for a while to make sure we don't trigger rollback
+ Thread.sleep(TimeUnit.SECONDS.toMillis(30));
}
/**
@@ -132,11 +144,8 @@ public class StagedRollbackTest {
*/
@Test
public void testBadApkOnly_Phase3() throws Exception {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
- Integer.toString(5), false);
-
- RollbackUtils.sendCrashBroadcast(TestApp.A, 5);
+ // One more crash to trigger rollback
+ RollbackUtils.sendCrashBroadcast(TestApp.A, 1);
// We expect the device to be rebooted automatically. Wait for that to happen.
Thread.sleep(30 * 1000);
@@ -209,7 +218,7 @@ public class StagedRollbackTest {
String networkStack = getNetworkStackPackageName();
rm.expireRollbackForPackage(networkStack);
- Uninstall.packages(networkStack);
+ uninstallNetworkStackPackage();
assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
networkStack)).isNull();
@@ -243,6 +252,12 @@ public class StagedRollbackTest {
return comp.getPackageName();
}
+ private void uninstallNetworkStackPackage() {
+ // Since the host side use shell command to install the network stack package, uninstall
+ // must be done by shell command as well. Otherwise uninstall by a different user will fail.
+ runShellCommand("pm uninstall " + getNetworkStackPackageName());
+ }
+
@Test
public void testPreviouslyAbandonedRollbacks_Phase1() throws Exception {
Uninstall.packages(TestApp.A);
@@ -285,7 +300,7 @@ public class StagedRollbackTest {
String networkStack = getNetworkStackPackageName();
rm.expireRollbackForPackage(networkStack);
- Uninstall.packages(networkStack);
+ uninstallNetworkStackPackage();
assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
networkStack)).isNull();
@@ -326,7 +341,8 @@ public class StagedRollbackTest {
}
private void runShellCommand(String cmd) {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
.executeShellCommand(cmd);
+ IoUtils.closeQuietly(pfd);
}
}
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 57f211d909ff..f7fe6c728f6a 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -94,7 +94,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
// Reboot device to activate staged package
getDevice().reboot();
- getDevice().waitForDeviceAvailable();
runPhase("testNativeWatchdogTriggersRollback_Phase2");
@@ -134,7 +133,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
Thread.sleep(5000);
// Reboot device to activate staged package
getDevice().reboot();
- getDevice().waitForDeviceAvailable();
// Verify rollback was enabled
runPhase("testNetworkFailedRollback_Phase2");
@@ -180,7 +178,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
Thread.sleep(5000);
// Reboot device to activate staged package
getDevice().reboot();
- getDevice().waitForDeviceAvailable();
// Verify rollback was enabled
runPhase("testNetworkPassedDoesNotRollback_Phase2");
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index beb5e0db6422..b9b2238572ac 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -44,7 +44,11 @@ java_defaults {
android_test {
name: "FrameworksNetTests",
defaults: ["FrameworksNetTests-jni-defaults"],
- srcs: ["java/**/*.java", "java/**/*.kt"],
+ srcs: [
+ ":tethering-tests-src",
+ "java/**/*.java",
+ "java/**/*.kt",
+ ],
platform_apis: true,
test_suites: ["device-tests"],
certificate: "platform",
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 2ca0d1a81e13..15691127cab7 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -271,7 +271,7 @@ public class NetworkCapabilitiesTest {
.addCapability(NET_CAPABILITY_NOT_METERED);
assertParcelingIsLossless(netCap);
netCap.setSSID(TEST_SSID);
- assertParcelSane(netCap, 11);
+ assertParcelSane(netCap, 12);
}
@Test
diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
index 46e27c1d3d3b..1f2bb0ac05ee 100644
--- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java
+++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
@@ -30,6 +30,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.app.Instrumentation;
import android.content.Context;
@@ -283,6 +284,107 @@ public class InetDiagSocketTest {
assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_BYTES, msg);
}
+ // Hexadecimal representation of InetDiagReqV2 request with extension, INET_DIAG_INFO.
+ private static final String INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_HEX =
+ // struct nlmsghdr
+ "48000000" + // length = 72
+ "1400" + // type = SOCK_DIAG_BY_FAMILY
+ "0100" + // flags = NLM_F_REQUEST
+ "00000000" + // seqno
+ "00000000" + // pid (0 == kernel)
+ // struct inet_diag_req_v2
+ "02" + // family = AF_INET
+ "06" + // protcol = IPPROTO_TCP
+ "02" + // idiag_ext = INET_DIAG_INFO
+ "00" + // pad
+ "ffffffff" + // idiag_states
+ // inet_diag_sockid
+ "3039" + // idiag_sport = 12345
+ "d431" + // idiag_dport = 54321
+ "01020304000000000000000000000000" + // idiag_src = 1.2.3.4
+ "08080404000000000000000000000000" + // idiag_dst = 8.8.4.4
+ "00000000" + // idiag_if
+ "ffffffffffffffff"; // idiag_cookie = INET_DIAG_NOCOOKIE
+
+ private static final byte[] INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_BYTES =
+ HexEncoding.decode(INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_HEX.toCharArray(), false);
+ private static final int TCP_ALL_STATES = 0xffffffff;
+ @Test
+ public void testInetDiagReqV2TcpInetWithExt() throws Exception {
+ InetSocketAddress local = new InetSocketAddress(
+ InetAddress.getByName("1.2.3.4"), 12345);
+ InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.4.4"),
+ 54321);
+ byte[] msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, remote, AF_INET,
+ NLM_F_REQUEST, 0 /* pad */, 2 /* idiagExt */, TCP_ALL_STATES);
+
+ assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET_INET_DIAG_BYTES, msg);
+
+ local = new InetSocketAddress(
+ InetAddress.getByName("fe80::86c9:b2ff:fe6a:ed4b"), 42462);
+ remote = new InetSocketAddress(InetAddress.getByName("8.8.8.8"),
+ 47473);
+ msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, remote, AF_INET6,
+ NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
+
+ assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_BYTES, msg);
+ }
+
+ // Hexadecimal representation of InetDiagReqV2 request with no socket specified.
+ private static final String INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFIED_HEX =
+ // struct nlmsghdr
+ "48000000" + // length = 72
+ "1400" + // type = SOCK_DIAG_BY_FAMILY
+ "0100" + // flags = NLM_F_REQUEST
+ "00000000" + // seqno
+ "00000000" + // pid (0 == kernel)
+ // struct inet_diag_req_v2
+ "0a" + // family = AF_INET6
+ "06" + // protcol = IPPROTO_TCP
+ "00" + // idiag_ext
+ "00" + // pad
+ "ffffffff" + // idiag_states
+ // inet_diag_sockid
+ "0000" + // idiag_sport
+ "0000" + // idiag_dport
+ "00000000000000000000000000000000" + // idiag_src
+ "00000000000000000000000000000000" + // idiag_dst
+ "00000000" + // idiag_if
+ "0000000000000000"; // idiag_cookie
+
+ private static final byte[] INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFED_BYTES =
+ HexEncoding.decode(INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFIED_HEX.toCharArray(), false);
+
+ @Test
+ public void testInetDiagReqV2TcpInet6NoIdSpecified() throws Exception {
+ InetSocketAddress local = new InetSocketAddress(
+ InetAddress.getByName("fe80::fe6a:ed4b"), 12345);
+ InetSocketAddress remote = new InetSocketAddress(InetAddress.getByName("8.8.4.4"),
+ 54321);
+ // Verify no socket specified if either local or remote socket address is null.
+ byte[] msgExt = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, null, null, AF_INET6,
+ NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
+ byte[] msg;
+ try {
+ msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, null, remote, AF_INET6,
+ NLM_F_REQUEST);
+ fail("Both remote and local should be null, expected UnknownHostException");
+ } catch (NullPointerException e) {
+ }
+
+ try {
+ msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, local, null, AF_INET6,
+ NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
+ fail("Both remote and local should be null, expected UnknownHostException");
+ } catch (NullPointerException e) {
+ }
+
+ msg = InetDiagMessage.InetDiagReqV2(IPPROTO_TCP, null, null, AF_INET6,
+ NLM_F_REQUEST, 0 /* pad */, 0 /* idiagExt */, TCP_ALL_STATES);
+ assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFED_BYTES, msg);
+ assertArrayEquals(INET_DIAG_REQ_V2_TCP_INET6_NO_ID_SPECIFED_BYTES, msgExt);
+ }
+
// Hexadecimal representation of InetDiagReqV2 request.
private static final String INET_DIAG_MSG_HEX =
// struct nlmsghdr
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index cf3fba8bef91..61f37fd6c7e2 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -33,6 +33,7 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
+import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
@@ -492,6 +493,8 @@ public class ConnectivityServiceTest {
private INetworkMonitor mNetworkMonitor;
private INetworkMonitorCallbacks mNmCallbacks;
private int mNmValidationResult = VALIDATION_RESULT_BASE;
+ private int mProbesCompleted;
+ private int mProbesSucceeded;
private String mNmValidationRedirectUrl = null;
private boolean mNmProvNotificationRequested = false;
@@ -559,6 +562,7 @@ public class ConnectivityServiceTest {
mNmProvNotificationRequested = false;
}
+ mNmCallbacks.notifyProbeStatusChanged(mProbesCompleted, mProbesSucceeded);
mNmCallbacks.notifyNetworkTested(
mNmValidationResult, mNmValidationRedirectUrl);
@@ -581,7 +585,7 @@ public class ConnectivityServiceTest {
* @param validated Indicate if network should pretend to be validated.
*/
public void connect(boolean validated) {
- connect(validated, true);
+ connect(validated, true, false /* isStrictMode */);
}
/**
@@ -589,13 +593,13 @@ public class ConnectivityServiceTest {
* @param validated Indicate if network should pretend to be validated.
* @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET.
*/
- public void connect(boolean validated, boolean hasInternet) {
+ public void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
assertFalse(getNetworkCapabilities().hasCapability(NET_CAPABILITY_INTERNET));
ConnectivityManager.NetworkCallback callback = null;
final ConditionVariable validatedCv = new ConditionVariable();
if (validated) {
- setNetworkValid();
+ setNetworkValid(isStrictMode);
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(getNetworkCapabilities().getTransportTypes()[0])
.clearCapabilities()
@@ -620,15 +624,15 @@ public class ConnectivityServiceTest {
if (validated) {
// Wait for network to validate.
waitFor(validatedCv);
- setNetworkInvalid();
+ setNetworkInvalid(isStrictMode);
}
if (callback != null) mCm.unregisterNetworkCallback(callback);
}
- public void connectWithCaptivePortal(String redirectUrl) {
- setNetworkPortal(redirectUrl);
- connect(false);
+ public void connectWithCaptivePortal(String redirectUrl, boolean isStrictMode) {
+ setNetworkPortal(redirectUrl, isStrictMode);
+ connect(false, true /* hasInternet */, isStrictMode);
}
public void connectWithPartialConnectivity() {
@@ -636,34 +640,75 @@ public class ConnectivityServiceTest {
connect(false);
}
- public void connectWithPartialValidConnectivity() {
- setNetworkPartialValid();
- connect(false);
+ public void connectWithPartialValidConnectivity(boolean isStrictMode) {
+ setNetworkPartialValid(isStrictMode);
+ connect(false, true /* hasInternet */, isStrictMode);
}
- void setNetworkValid() {
+ void setNetworkValid(boolean isStrictMode) {
mNmValidationResult = VALIDATION_RESULT_VALID;
mNmValidationRedirectUrl = null;
+ int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTP;
+ if (isStrictMode) {
+ probesSucceeded |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ // The probesCompleted equals to probesSucceeded for the case of valid network, so put
+ // the same value into two different parameter of the method.
+ setProbesStatus(probesSucceeded, probesSucceeded);
}
- void setNetworkInvalid() {
+ void setNetworkInvalid(boolean isStrictMode) {
mNmValidationResult = VALIDATION_RESULT_INVALID;
mNmValidationRedirectUrl = null;
+ int probesCompleted = VALIDATION_RESULT_BASE;
+ int probesSucceeded = VALIDATION_RESULT_INVALID;
+ // If the isStrictMode is true, it means the network is invalid when NetworkMonitor
+ // tried to validate the private DNS but failed.
+ if (isStrictMode) {
+ probesCompleted &= ~NETWORK_VALIDATION_PROBE_HTTP;
+ probesSucceeded = probesCompleted;
+ probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ setProbesStatus(probesCompleted, probesSucceeded);
}
- void setNetworkPortal(String redirectUrl) {
- setNetworkInvalid();
+ void setNetworkPortal(String redirectUrl, boolean isStrictMode) {
+ setNetworkInvalid(isStrictMode);
mNmValidationRedirectUrl = redirectUrl;
+ // Suppose the portal is found when NetworkMonitor probes NETWORK_VALIDATION_PROBE_HTTP
+ // in the beginning, so the NETWORK_VALIDATION_PROBE_HTTPS hasn't probed yet.
+ int probesCompleted = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+ int probesSucceeded = VALIDATION_RESULT_INVALID;
+ if (isStrictMode) {
+ probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ setProbesStatus(probesCompleted, probesSucceeded);
}
void setNetworkPartial() {
mNmValidationResult = VALIDATION_RESULT_PARTIAL;
mNmValidationRedirectUrl = null;
+ int probesCompleted = VALIDATION_RESULT_BASE;
+ int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+ setProbesStatus(probesCompleted, probesSucceeded);
}
- void setNetworkPartialValid() {
- mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID;
- mNmValidationRedirectUrl = null;
+ void setNetworkPartialValid(boolean isStrictMode) {
+ setNetworkPartial();
+ mNmValidationResult |= VALIDATION_RESULT_VALID;
+ int probesCompleted = VALIDATION_RESULT_BASE;
+ int probesSucceeded = VALIDATION_RESULT_BASE & ~NETWORK_VALIDATION_PROBE_HTTPS;
+ // Suppose the partial network cannot pass the private DNS validation as well, so only
+ // add NETWORK_VALIDATION_PROBE_DNS in probesCompleted but not probesSucceeded.
+ if (isStrictMode) {
+ probesCompleted |= NETWORK_VALIDATION_PROBE_PRIVDNS;
+ }
+ setProbesStatus(probesCompleted, probesSucceeded);
+ }
+
+ void setProbesStatus(int probesCompleted, int probesSucceeded) {
+ mProbesCompleted = probesCompleted;
+ mProbesSucceeded = probesSucceeded;
}
public String waitForRedirectUrl() {
@@ -2226,7 +2271,7 @@ public class ConnectivityServiceTest {
// With HTTPS probe disabled, NetworkMonitor should pass the network validation with http
// probe.
- mWiFiNetworkAgent.setNetworkPartialValid();
+ mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */);
// If the user chooses yes to use this partial connectivity wifi, switch the default
// network to wifi and check if wifi becomes valid or not.
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
@@ -2299,7 +2344,7 @@ public class ConnectivityServiceTest {
callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
- mWiFiNetworkAgent.setNetworkValid();
+ mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
// Need a trigger point to let NetworkMonitor tell ConnectivityService that network is
// validated.
@@ -2317,7 +2362,7 @@ public class ConnectivityServiceTest {
// NetworkMonitor will immediately (once the HTTPS probe fails...) report the network as
// valid, because ConnectivityService calls setAcceptPartialConnectivity before it calls
// notifyNetworkConnected.
- mWiFiNetworkAgent.connectWithPartialValidConnectivity();
+ mWiFiNetworkAgent.connectWithPartialValidConnectivity(false /* isStrictMode */);
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
callback.expectCallback(CallbackEntry.LOSING, mCellNetworkAgent);
@@ -2343,7 +2388,7 @@ public class ConnectivityServiceTest {
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String redirectUrl = "http://android.com/path";
- mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl, false /* isStrictMode */);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), redirectUrl);
@@ -2361,7 +2406,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent);
// Report partial connectivity is accepted.
- mWiFiNetworkAgent.setNetworkPartialValid();
+ mWiFiNetworkAgent.setNetworkPartialValid(false /* isStrictMode */);
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
false /* always */);
waitForIdle();
@@ -2392,7 +2437,7 @@ public class ConnectivityServiceTest {
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
- mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
@@ -2405,13 +2450,13 @@ public class ConnectivityServiceTest {
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String secondRedirectUrl = "http://example.com/secondPath";
- mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl, false /* isStrictMode */);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
// Make captive portal disappear then revalidate.
// Expect onLost callback because network no longer provides NET_CAPABILITY_CAPTIVE_PORTAL.
- mWiFiNetworkAgent.setNetworkValid();
+ mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -2423,7 +2468,7 @@ public class ConnectivityServiceTest {
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
- mWiFiNetworkAgent.setNetworkInvalid();
+ mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
}
@@ -2454,7 +2499,7 @@ public class ConnectivityServiceTest {
mServiceContext.expectNoStartActivityIntent(fastTimeoutMs);
// Turn into a captive portal.
- mWiFiNetworkAgent.setNetworkPortal("http://example.com");
+ mWiFiNetworkAgent.setNetworkPortal("http://example.com", false /* isStrictMode */);
mCm.reportNetworkConnectivity(wifiNetwork, false);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
validatedCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -2475,7 +2520,7 @@ public class ConnectivityServiceTest {
assertEquals(testValue, signInIntent.getStringExtra(testKey));
// Report that the captive portal is dismissed, and check that callbacks are fired
- mWiFiNetworkAgent.setNetworkValid();
+ mWiFiNetworkAgent.setNetworkValid(false /* isStrictMode */);
mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
captivePortalCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -2504,7 +2549,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
- mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
+ mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl, false /* isStrictMode */);
mWiFiNetworkAgent.expectDisconnected();
mWiFiNetworkAgent.expectPreventReconnectReceived();
@@ -3219,7 +3264,7 @@ public class ConnectivityServiceTest {
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi.
- mWiFiNetworkAgent.setNetworkInvalid();
+ mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(wifiNetwork, false);
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -3263,7 +3308,7 @@ public class ConnectivityServiceTest {
wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi and expect the dialog to appear.
- mWiFiNetworkAgent.setNetworkInvalid();
+ mWiFiNetworkAgent.setNetworkInvalid(false /* isStrictMode */);
mCm.reportNetworkConnectivity(wifiNetwork, false);
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
validatedWifiCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
@@ -4299,7 +4344,7 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(request, callback);
// Bring up wifi aware network.
- wifiAware.connect(false, false);
+ wifiAware.connect(false, false, false /* isStrictMode */);
callback.expectAvailableCallbacksUnvalidated(wifiAware);
assertNull(mCm.getActiveNetworkInfo());
@@ -4537,6 +4582,44 @@ public class ConnectivityServiceTest {
}
@Test
+ public void testPrivateDnsNotification() throws Exception {
+ NetworkRequest request = new NetworkRequest.Builder()
+ .clearCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+ // Bring up wifi.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ // Private DNS resolution failed, checking if the notification will be shown or not.
+ mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */);
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ // If network validation failed, NetworkMonitor will re-evaluate the network.
+ // ConnectivityService should filter the redundant notification. This part is trying to
+ // simulate that situation and check if ConnectivityService could filter that case.
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).notifyAsUser(anyString(),
+ eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL));
+ // If private DNS resolution successful, the PRIVATE_DNS_BROKEN notification shouldn't be
+ // shown.
+ mWiFiNetworkAgent.setNetworkValid(true /* isStrictMode */);
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ verify(mNotificationManager, timeout(TIMEOUT_MS).times(1)).cancelAsUser(anyString(),
+ eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), eq(UserHandle.ALL));
+ // If private DNS resolution failed again, the PRIVATE_DNS_BROKEN notification should be
+ // shown again.
+ mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */);
+ mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ waitForIdle();
+ verify(mNotificationManager, timeout(TIMEOUT_MS).times(2)).notifyAsUser(anyString(),
+ eq(NotificationType.PRIVATE_DNS_BROKEN.eventId), any(), eq(UserHandle.ALL));
+ }
+
+ @Test
public void testPrivateDnsSettingsChange() throws Exception {
// Clear any interactions that occur as a result of CS starting up.
reset(mMockDnsResolver);
@@ -4793,7 +4876,7 @@ public class ConnectivityServiceTest {
// by NetworkMonitor
assertFalse(NetworkMonitorUtils.isValidationRequired(
vpnNetworkAgent.getNetworkCapabilities()));
- vpnNetworkAgent.setNetworkValid();
+ vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
vpnNetworkAgent.connect(false);
mMockVpn.connect();
@@ -4882,7 +4965,8 @@ public class ConnectivityServiceTest {
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* isStrictMode */);
mMockVpn.connect();
defaultCallback.assertNoCallback();
@@ -4913,7 +4997,8 @@ public class ConnectivityServiceTest {
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */,
+ false /* isStrictMode */);
mMockVpn.connect();
defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
@@ -4945,7 +5030,8 @@ public class ConnectivityServiceTest {
ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
+ vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */,
+ false /* isStrictMode */);
mMockVpn.connect();
// Even though the VPN is unvalidated, it becomes the default network for our app.
@@ -4968,7 +5054,7 @@ public class ConnectivityServiceTest {
vpnNetworkAgent.getNetworkCapabilities()));
// Pretend that the VPN network validates.
- vpnNetworkAgent.setNetworkValid();
+ vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
// Expect to see the validated capability, but no other changes, because the VPN is already
// the default network for the app.
@@ -5000,7 +5086,8 @@ public class ConnectivityServiceTest {
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.connect();
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* isStrictMode */);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
@@ -5099,7 +5186,8 @@ public class ConnectivityServiceTest {
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.connect();
mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ false /* isStrictMode */);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp
index d80c2e742fae..fd184f50091a 100644
--- a/tools/bit/main.cpp
+++ b/tools/bit/main.cpp
@@ -708,10 +708,12 @@ run_phases(vector<Target*> targets, const Options& options)
}
}
+
// Figure out whether we need to sync the system and which apks to install
string deviceTargetPath = buildOut + "/target/product/" + buildDevice;
string systemPath = deviceTargetPath + "/system/";
string dataPath = deviceTargetPath + "/data/";
+ string testPath = deviceTargetPath + "/testcases/";
bool syncSystem = false;
bool alwaysSyncSystem = false;
vector<string> systemFiles;
@@ -734,7 +736,8 @@ run_phases(vector<Target*> targets, const Options& options)
continue;
}
// Apk in the data partition
- if (starts_with(file, dataPath) && ends_with(file, ".apk")) {
+ if (ends_with(file, ".apk")
+ && (starts_with(file, dataPath) || starts_with(file, testPath))) {
// Always install it if we didn't build it because otherwise
// it will never have changed.
installApks.push_back(InstallApk(file, !target->build));
@@ -966,8 +969,9 @@ run_phases(vector<Target*> targets, const Options& options)
for (size_t j=0; j<target->module.installed.size(); j++) {
string filename = target->module.installed[j];
- // Apk in the data partition
- if (!starts_with(filename, dataPath) || !ends_with(filename, ".apk")) {
+ // Skip of not apk in the data partition or test
+ if (!(ends_with(filename, ".apk")
+ && (starts_with(filename, dataPath) || starts_with(filename, testPath)))) {
continue;
}
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 4ce4406211ce..c08f9b04f0df 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -95,7 +95,7 @@ genrule {
],
}
-cc_library_shared {
+cc_library {
name: "libstatslog",
host_supported: true,
generated_sources: ["statslog.cpp"],
diff --git a/wifi/java/android/net/wifi/ISoftApCallback.aidl b/wifi/java/android/net/wifi/ISoftApCallback.aidl
index b8d2971e74bb..8a252dd1e447 100644
--- a/wifi/java/android/net/wifi/ISoftApCallback.aidl
+++ b/wifi/java/android/net/wifi/ISoftApCallback.aidl
@@ -16,6 +16,8 @@
package android.net.wifi;
+import android.net.wifi.WifiClient;
+
/**
* Interface for Soft AP callback.
*
@@ -36,9 +38,9 @@ oneway interface ISoftApCallback
void onStateChanged(int state, int failureReason);
/**
- * Service to manager callback providing number of connected clients.
+ * Service to manager callback providing connected client's information.
*
- * @param numClients number of connected clients
+ * @param clients the currently connected clients
*/
- void onNumClientsChanged(int numClients);
+ void onConnectedClientsChanged(in List<WifiClient> clients);
}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 13377390fc04..19be13268d27 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -94,6 +94,8 @@ interface IWifiManager
boolean disableNetwork(int netId, String packageName);
+ void allowAutojoin(int netId, boolean choice);
+
boolean startScan(String packageName);
List<ScanResult> getScanResults(String callingPackage);
diff --git a/wifi/java/android/net/wifi/IWifiScanner.aidl b/wifi/java/android/net/wifi/IWifiScanner.aidl
index 398493439bbe..114c7320bc3a 100644
--- a/wifi/java/android/net/wifi/IWifiScanner.aidl
+++ b/wifi/java/android/net/wifi/IWifiScanner.aidl
@@ -26,5 +26,5 @@ interface IWifiScanner
{
Messenger getMessenger();
- Bundle getAvailableChannels(int band);
+ Bundle getAvailableChannels(int band, String packageName);
}
diff --git a/wifi/java/android/net/wifi/WifiClient.aidl b/wifi/java/android/net/wifi/WifiClient.aidl
new file mode 100644
index 000000000000..accdaddfc848
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiClient.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+@JavaOnlyStableParcelable parcelable WifiClient; \ No newline at end of file
diff --git a/wifi/java/android/net/wifi/WifiClient.java b/wifi/java/android/net/wifi/WifiClient.java
new file mode 100644
index 000000000000..3e09580802ce
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiClient.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.MacAddress;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/** @hide */
+@SystemApi
+public final class WifiClient implements Parcelable {
+
+ private final MacAddress mMacAddress;
+
+ /**
+ * The mac address of this client.
+ */
+ @NonNull
+ public MacAddress getMacAddress() {
+ return mMacAddress;
+ }
+
+ private WifiClient(Parcel in) {
+ mMacAddress = in.readParcelable(null);
+ }
+
+ /** @hide */
+ public WifiClient(@NonNull MacAddress macAddress) {
+ Preconditions.checkNotNull(macAddress, "mMacAddress must not be null.");
+
+ this.mMacAddress = macAddress;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mMacAddress, flags);
+ }
+
+ @NonNull
+ public static final Creator<WifiClient> CREATOR = new Creator<WifiClient>() {
+ public WifiClient createFromParcel(Parcel in) {
+ return new WifiClient(in);
+ }
+
+ public WifiClient[] newArray(int size) {
+ return new WifiClient[size];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "WifiClient{"
+ + "mMacAddress=" + mMacAddress
+ + '}';
+ }
+
+ @Override
+ public boolean equals(@NonNull Object o) {
+ if (this == o) return true;
+ if (!(o instanceof WifiClient)) return false;
+ WifiClient client = (WifiClient) o;
+ return mMacAddress.equals(client.mMacAddress);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mMacAddress);
+ }
+}
+
+
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 2afb14a12110..3bedddc8ec7d 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -735,6 +735,14 @@ public class WifiConfiguration implements Parcelable {
*/
public int userApproved = USER_UNSPECIFIED;
+ /**
+ * @hide
+ * Auto-join is allowed by user for this network.
+ * Default true.
+ */
+ @SystemApi
+ public boolean allowAutojoin = true;
+
/** The Below RSSI thresholds are used to configure AutoJoin
* - GOOD/LOW/BAD thresholds are used so as to calculate link score
* - UNWANTED_SOFT are used by the blacklisting logic so as to handle
@@ -2022,6 +2030,7 @@ public class WifiConfiguration implements Parcelable {
if (updateIdentifier != null) sbuf.append(" updateIdentifier=" + updateIdentifier);
sbuf.append(" lcuid=" + lastConnectUid);
sbuf.append(" userApproved=" + userApprovedAsString(userApproved));
+ sbuf.append(" allowAutojoin=" + allowAutojoin);
sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected);
sbuf.append(" ");
@@ -2421,6 +2430,7 @@ public class WifiConfiguration implements Parcelable {
numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
numAssociation = source.numAssociation;
userApproved = source.userApproved;
+ allowAutojoin = source.allowAutojoin;
numNoInternetAccessReports = source.numNoInternetAccessReports;
noInternetAccessExpected = source.noInternetAccessExpected;
creationTime = source.creationTime;
@@ -2496,6 +2506,7 @@ public class WifiConfiguration implements Parcelable {
dest.writeInt(numScorerOverrideAndSwitchedNetwork);
dest.writeInt(numAssociation);
dest.writeInt(userApproved);
+ dest.writeBoolean(allowAutojoin);
dest.writeInt(numNoInternetAccessReports);
dest.writeInt(noInternetAccessExpected ? 1 : 0);
dest.writeInt(shared ? 1 : 0);
@@ -2571,6 +2582,7 @@ public class WifiConfiguration implements Parcelable {
config.numScorerOverrideAndSwitchedNetwork = in.readInt();
config.numAssociation = in.readInt();
config.userApproved = in.readInt();
+ config.allowAutojoin = in.readBoolean();
config.numNoInternetAccessReports = in.readInt();
config.noInternetAccessExpected = in.readInt() != 0;
config.shared = in.readInt() != 0;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 04b073be28e2..3b7690b1e8fa 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -530,7 +530,9 @@ public class WifiManager {
@SystemApi
public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
/**
- * The interface used for the softap.
+ * The lookup key for a String extra that stores the interface name used for the Soft AP.
+ * This extra is included in the broadcast {@link #WIFI_AP_STATE_CHANGED_ACTION}.
+ * Retrieve its value with {@link android.content.Intent#getStringExtra(String)}.
*
* @hide
*/
@@ -538,9 +540,10 @@ public class WifiManager {
public static final String EXTRA_WIFI_AP_INTERFACE_NAME =
"android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
/**
- * The intended ip mode for this softap.
- * @see #IFACE_IP_MODE_TETHERED
- * @see #IFACE_IP_MODE_LOCAL_ONLY
+ * The lookup key for an int extra that stores the intended IP mode for this Soft AP.
+ * One of {@link #IFACE_IP_MODE_TETHERED} or {@link #IFACE_IP_MODE_LOCAL_ONLY}.
+ * This extra is included in the broadcast {@link #WIFI_AP_STATE_CHANGED_ACTION}.
+ * Retrieve its value with {@link android.content.Intent#getIntExtra(String, int)}.
*
* @hide
*/
@@ -2697,12 +2700,13 @@ public class WifiManager {
}
/**
- * Start SoftAp mode with the specified configuration.
- * Note that starting in access point mode disables station
- * mode operation
- * @param wifiConfig SSID, security and channel details as
- * part of WifiConfiguration
- * @return {@code true} if the operation succeeds, {@code false} otherwise
+ * Start Soft AP (hotspot) mode with the specified configuration.
+ * Note that starting Soft AP mode may disable station mode operation if the device does not
+ * support concurrency.
+ * @param wifiConfig SSID, security and channel details as part of WifiConfiguration, or null to
+ * use the persisted Soft AP configuration that was previously set using
+ * {@link #setWifiApConfiguration(WifiConfiguration)}.
+ * @return {@code true} if the operation succeeded, {@code false} otherwise
*
* @hide
*/
@@ -3250,21 +3254,22 @@ public class WifiManager {
/**
* Called when soft AP state changes.
*
- * @param state new new AP state. One of {@link #WIFI_AP_STATE_DISABLED},
- * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
- * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
+ * @param state new new AP state. One of {@link #WIFI_AP_STATE_DISABLED},
+ * {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
+ * {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
* @param failureReason reason when in failed state. One of
- * {@link #SAP_START_FAILURE_GENERAL}, {@link #SAP_START_FAILURE_NO_CHANNEL}
+ * {@link #SAP_START_FAILURE_GENERAL},
+ * {@link #SAP_START_FAILURE_NO_CHANNEL}
*/
- public abstract void onStateChanged(@WifiApState int state,
+ void onStateChanged(@WifiApState int state,
@SapStartFailure int failureReason);
/**
- * Called when number of connected clients to soft AP changes.
+ * Called when the connected clients to soft AP changes.
*
- * @param numClients number of connected clients
+ * @param clients the currently connected clients
*/
- public abstract void onNumClientsChanged(int numClients);
+ void onConnectedClientsChanged(@NonNull List<WifiClient> clients);
}
/**
@@ -3287,18 +3292,21 @@ public class WifiManager {
Log.v(TAG, "SoftApCallbackProxy: onStateChanged: state=" + state
+ ", failureReason=" + failureReason);
}
+
mHandler.post(() -> {
mCallback.onStateChanged(state, failureReason);
});
}
@Override
- public void onNumClientsChanged(int numClients) {
+ public void onConnectedClientsChanged(List<WifiClient> clients) {
if (mVerboseLoggingEnabled) {
- Log.v(TAG, "SoftApCallbackProxy: onNumClientsChanged: numClients=" + numClients);
+ Log.v(TAG, "SoftApCallbackProxy: onConnectedClientsChanged: clients="
+ + clients.size() + " clients");
}
+
mHandler.post(() -> {
- mCallback.onNumClientsChanged(numClients);
+ mCallback.onConnectedClientsChanged(clients);
});
}
}
@@ -3865,6 +3873,29 @@ public class WifiManager {
}
/**
+ * Sets the user choice for allowing auto-join to a network.
+ * The updated choice will be made available through the updated config supplied by the
+ * CONFIGURED_NETWORKS_CHANGED broadcast.
+ *
+ * @param netId the id of the network to allow/disallow autojoin for.
+ * @param choice true to allow autojoin, false to disallow autojoin
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void allowAutojoin(int netId, boolean choice) {
+ try {
+ IWifiManager iWifiManager = getIWifiManager();
+ if (iWifiManager == null) {
+ throw new RemoteException("Wifi service is not running");
+ }
+ iWifiManager.allowAutojoin(netId, choice);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Disable ephemeral Network
*
* @param SSID, in the format of WifiConfiguration's SSID.
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 68948cbbe7a9..21189a411d94 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -17,7 +17,9 @@
package android.net.wifi;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -39,6 +41,8 @@ import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Protocol;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -51,26 +55,38 @@ import java.util.List;
@SystemService(Context.WIFI_SCANNING_SERVICE)
public class WifiScanner {
- /** no band specified; use channel list instead */
- public static final int WIFI_BAND_UNSPECIFIED = 0; /* not specified */
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"WIFI_BAND_"}, value = {
+ WIFI_BAND_UNSPECIFIED,
+ WIFI_BAND_24_GHZ,
+ WIFI_BAND_5_GHZ,
+ WIFI_BAND_BOTH,
+ WIFI_BAND_5_GHZ_DFS_ONLY,
+ WIFI_BAND_24_GHZ_WITH_5GHZ_DFS,
+ WIFI_BAND_5_GHZ_WITH_DFS,
+ WIFI_BAND_BOTH_WITH_DFS})
+ public @interface WifiBand {}
+ /** no band specified; use channel list instead */
+ public static final int WIFI_BAND_UNSPECIFIED = 0;
/** 2.4 GHz band */
- public static final int WIFI_BAND_24_GHZ = 1; /* 2.4 GHz band */
+ public static final int WIFI_BAND_24_GHZ = 1;
/** 5 GHz band excluding DFS channels */
- public static final int WIFI_BAND_5_GHZ = 2; /* 5 GHz band without DFS channels */
+ public static final int WIFI_BAND_5_GHZ = 2;
+ /** Both 2.4 GHz band and 5 GHz band; no DFS channels */
+ public static final int WIFI_BAND_BOTH = 3;
/** DFS channels from 5 GHz band only */
- public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; /* 5 GHz band DFS channels */
+ public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4;
/**
* 2.4Ghz band + DFS channels from 5 GHz band only
* @hide
*/
public static final int WIFI_BAND_24_GHZ_WITH_5GHZ_DFS = 5;
/** 5 GHz band including DFS channels */
- public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; /* 5 GHz band with DFS channels */
- /** Both 2.4 GHz band and 5 GHz band; no DFS channels */
- public static final int WIFI_BAND_BOTH = 3; /* both bands without DFS channels */
+ public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6;
/** Both 2.4 GHz band and 5 GHz band; with DFS channels */
- public static final int WIFI_BAND_BOTH_WITH_DFS = 7; /* both bands with DFS channels */
+ public static final int WIFI_BAND_BOTH_WITH_DFS = 7;
/**
* Max band value
* @hide
@@ -78,9 +94,9 @@ public class WifiScanner {
public static final int WIFI_BAND_MAX = 8;
/** Minimum supported scanning period */
- public static final int MIN_SCAN_PERIOD_MS = 1000; /* minimum supported period */
+ public static final int MIN_SCAN_PERIOD_MS = 1000;
/** Maximum supported scanning period */
- public static final int MAX_SCAN_PERIOD_MS = 1024000; /* maximum supported period */
+ public static final int MAX_SCAN_PERIOD_MS = 1024000;
/** No Error */
public static final int REASON_SUCCEEDED = 0;
@@ -109,13 +125,20 @@ public class WifiScanner {
}
/**
- * gives you all the possible channels; channel is specified as an
- * integer with frequency in MHz i.e. channel 1 is 2412
+ * Returns a list of all the possible channels for the given band(s).
+ *
+ * @param band one of the WifiScanner#WIFI_BAND_* constants, e.g. {@link #WIFI_BAND_24_GHZ}
+ * @return a list of all the frequencies, in MHz, for the given band(s) e.g. channel 1 is
+ * 2412, or null if an error occurred.
+ *
* @hide
*/
- public List<Integer> getAvailableChannels(int band) {
+ @SystemApi
+ @Nullable
+ @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+ public List<Integer> getAvailableChannels(@WifiBand int band) {
try {
- Bundle bundle = mService.getAvailableChannels(band);
+ Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName());
return bundle.getIntegerArrayList(GET_AVAILABLE_CHANNELS_EXTRA);
} catch (RemoteException e) {
return null;
diff --git a/wifi/java/android/net/wifi/hotspot2/ConfigParser.java b/wifi/java/android/net/wifi/hotspot2/ConfigParser.java
index e8e873199e17..bb01365d6722 100644
--- a/wifi/java/android/net/wifi/hotspot2/ConfigParser.java
+++ b/wifi/java/android/net/wifi/hotspot2/ConfigParser.java
@@ -182,6 +182,12 @@ public final class ConfigParser {
throw new IOException("Passpoint profile missing credential");
}
+ // Don't allow the installer to make changes to the update identifier. This is an
+ // indicator of an R2 (or newer) network.
+ if (config.getUpdateIdentifier() != Integer.MIN_VALUE) {
+ config.setUpdateIdentifier(Integer.MIN_VALUE);
+ }
+
// Parse CA (Certificate Authority) certificate.
byte[] caCertData = mimeParts.get(TYPE_CA_CERT);
if (caCertData != null) {
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 557b7c9b05e9..e9aa076798e1 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -65,6 +65,7 @@ public final class PasspointConfiguration implements Parcelable {
* Configurations under HomeSp subtree.
*/
private HomeSp mHomeSp = null;
+
/**
* Set the Home SP (Service Provider) information.
*
@@ -248,7 +249,10 @@ public final class PasspointConfiguration implements Parcelable {
mSubscriptionExpirationTimeInMillis = subscriptionExpirationTimeInMillis;
}
/**
- * @hide
+ * Utility method to get the time this subscription will expire. It is in the format of number
+ * of milliseconds since January 1, 1970, 00:00:00 GMT.
+ *
+ * @return The time this subscription will expire, or Long.MIN_VALUE to indicate unset value
*/
public long getSubscriptionExpirationTimeInMillis() {
return mSubscriptionExpirationTimeInMillis;
@@ -521,6 +525,8 @@ public final class PasspointConfiguration implements Parcelable {
.append("\n");
builder.append("UsageLimitDataLimit: ").append(mUsageLimitDataLimit).append("\n");
builder.append("UsageLimitTimeLimit: ").append(mUsageLimitTimeLimitInMinutes).append("\n");
+ builder.append("Provisioned by a subscription server: ")
+ .append(isOsuProvisioned() ? "Yes" : "No").append("\n");
if (mHomeSp != null) {
builder.append("HomeSP Begin ---\n");
builder.append(mHomeSp);
@@ -728,4 +734,14 @@ public final class PasspointConfiguration implements Parcelable {
}
return true;
}
+
+ /**
+ * Indicates if the Passpoint Configuration was provisioned by a subscription (OSU) server,
+ * which means that it's an R2 (or R3) profile.
+ *
+ * @return true if the Passpoint Configuration was provisioned by a subscription server.
+ */
+ public boolean isOsuProvisioned() {
+ return getUpdateIdentifier() != Integer.MIN_VALUE;
+ }
}
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 94fb5ae4d61c..5e6c107dc187 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -168,6 +168,11 @@ public class BaseWifiService extends IWifiManager.Stub {
}
@Override
+ public void allowAutojoin(int netId, boolean choice) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean startScan(String packageName) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index 401b652289cd..3453d6ec827f 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -49,6 +49,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
core-test-rules \
guava \
mockito-target-minus-junit4 \
+ net-tests-utils \
frameworks-base-testutils \
truth-prebuilt \
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
index 56919c25de46..760c8395e659 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
@@ -12,75 +12,75 @@ VnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSthVEF3TVR3dlRt
OWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVJYjIx
bFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1
dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNB
-OFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklDQWdQQzlPYjJS
-bFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrWlJSRTQ4
-TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1MWF6d3ZWbUZz
-ZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0Fn
-SUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2ClpHVk9ZVzFs
-UGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpoYkhWbFBnb2dJ
-Q0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4VG05a1pUNEtJ
-Q0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVoYldVK0NpQWdJ
-Q0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhiRzA4CkwwNXZa
-R1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHljbVZrTG1OdmJU
-d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lD
-QWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThMMDV2WkdWT1lX
-MWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0ClpU
-NVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lXeDFaVDVxWVcx
-bGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05
-a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BDOU9iMlJsVG1G
-dFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3dlZtRnNkV1Ur
-CkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0Fn
-SUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0Fn
-SUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1GdFpUNUZRVkJV
-ZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1UrTWpFOEwxWmhi
-SFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxQ
-Z29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2WkR3dlRtOWta
-VTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0VmpJOEwxWmhi
-SFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0Np
-QWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BF
-NXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
-QWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQa05sY25ScFpt
-bGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lXeDFaVDU0TlRB
-NWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1
-dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1qVTJSbWx1WjJW
-eWNISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdVKwpNV1l4WmpG
+OFZtRnNkV1UrUlhoaGJYQnNaU0JPWlhSM2IzSnJQQzlXWVd4MVpUNEtJQ0FnCklDQWdJQ0E4TDA1
+dlpHVStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStSbEZF
+VGp3dlRtOWsKWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVSthRzkwYzNCdmRDNWxlR0Z0
+Y0d4bExtNWxkRHd2Vm1Gc2RXVStDaUFnSUNBZwpJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhP
+YjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERiMjV6CmIzSjBhWFZ0
+VDBrOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhNakl6TXl3ME5EVTFO
+alk4TDFaaGJIVmwKUGdvZ0lDQWdJQ0FnSUR3dlRtOWtaVDRLSUNBZ0lDQWdQQzlPYjJSbFBnb2dJ
+Q0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0E4VG05awpaVTVoYldVK1EzSmxaR1Z1ZEdsaGJEd3ZU
+bTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrClpVNWhi
+V1UrVW1WaGJHMDhMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbVY0WVcxd2JH
+VXVZMjl0UEM5V1lXeDEKWlQ0S0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEU1dlpH
+VStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrVlhObApjbTVoYldWUVlYTnpkMjl5WkR3dlRt
+OWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2ClpH
+Vk9ZVzFsUGxWelpYSnVZVzFsUEM5T2IyUmxUbUZ0WlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRlpoYkhW
+bFBuVnpaWEk4TDFaaGJIVmwKUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNB
+Z1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsVG1GdApaVDVRWVhOemQyOXlaRHd2VG05
+a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1alIwWjZZek5rZG1OdFVUMDhMMVpo
+CmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFn
+SUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNUZRVkJOWlhSb2IyUThMMDV2WkdWT1lXMWxQZ29n
+SUNBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZwpJQ0E4VG05a1pVNWhiV1Ur
+UlVGUVZIbHdaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqSXhQ
+QzlXCllXeDFaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThU
+bTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWcKSUNBOFRtOWtaVTVoYldVK1NXNXVaWEpOWlhSb2IyUThM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1TgpVeTFEU0VGUUxWWXlQ
+QzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2Iy
+UmxQZ29nCklDQWdJQ0FnSUR3dlRtOWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lD
+QWdJRHhPYjJSbFRtRnRaVDVFYVdkcGRHRnMKUTJWeWRHbG1hV05oZEdVOEwwNXZaR1ZPWVcxbFBn
+b2dJQ0FnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbApUbUZ0WlQ1RFpY
+SjBhV1pwWTJGMFpWUjVjR1U4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+K2VEVXdPWFl6ClBDOVdZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNB
+Z0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQTgKVG05a1pVNWhiV1UrUTJWeWRGTklRVEkxTmta
+cGJtZGxjbkJ5YVc1MFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaApiSFZsUGpG
bU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZt
-TVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNBOEwwNXZaR1Ur
-Q2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0Fn
-UEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4VG05awpaVDRL
-SUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJ
-Q0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNBOEwwNXZa
-R1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2WkdWT1lXMWxQ
-a1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1UrTWpROApM
-MVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lD
-QWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQQzlOWjIxMFZI
-SmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXg1MDktY2Et
-Y2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFDUlVkSlRpQkRS
-VkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtRVWxNYkVaa2Qz
-cE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5UbFlLUWtGTlZF
-SXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpUazFxV1hkTlZF
-RTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5VVEJGZUUxSlNV
-Skpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMwTkJVVVZCQ25w
-dVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZYTldkVzFFWWxs
-SWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01TdHZSMWhhZGto
-M2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJVRkRaV1pXYW1v
-d2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJoU1FqZzFNVEpR
-UWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdKck1IVjVhM1Jr
-WW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtzM2FFUTRjRkIy
-WmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxhCmFYQllOREY0
-UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVXTTJreGRIRXdO
-R3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZHU1hkWU5IWnpP
-RUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFFYlVGR1NYZFlO
-SFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJkMFJuCldVUldV
-VkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldVakJVUWtGVmQw
-RjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZMDVCVVVWTVFs
-RkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZDamxIUlZBdmRX
-OW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNYZEpWV00zCmQy
-azNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1EwOTBhWE5rUW5F
-eWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JXMVdUQW94Y1VK
-S2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVFMXVWR3c0ZUVW
-WFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhwaFNFb3hkVlk0
-Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpGS1VDdHNlRllL
-YlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNSa1RrNTJRMWw2
-YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmtsRFFWUkZMUzB0
-TFMwSwotLXtib3VuZGFyeX0tLQo=
+TVdZeFpqRm1NV1l4ClpqRm1NV1l4WmpGbU1XWThMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlP
+YjJSbFBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWcKSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0Fn
+SUNBZ0lEeE9iMlJsVG1GdFpUNVRTVTA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZwpQRTV2
+WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVKVFZOSlBDOU9iMlJsVG1GdFpUNEtJ
+Q0FnSUNBZ0lDQWdJQ0FnClBGWmhiSFZsUGpFeU16UTFOaW84TDFaaGJIVmxQZ29nSUNBZ0lDQWdJ
+Q0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVSsKQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9i
+MlJsVG1GdFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaApi
+SFZsUGpJelBDOVdZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOEww
+NXZaR1UrQ2lBZ0lDQWdJRHd2ClRtOWtaVDRLSUNBZ0lEd3ZUbTlrWlQ0S0lDQThMMDV2WkdVK0Nq
+d3ZUV2R0ZEZSeVpXVSsKCi0te2JvdW5kYXJ5fQpDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL3gt
+eDUwOS1jYS1jZXJ0CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoKTFMwdExTMUNS
+VWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUkxSRU5EUVdoRFowRjNTVUpCWjBsS1FV
+bE1iRVprZDNwTQpWblZ5VFVFd1IwTlRjVWRUU1dJelJGRkZRa04zVlVGTlFrbDRSVVJCVDBKblRs
+WUtRa0ZOVkVJd1ZrSlZRMEpFVVZSRmQwaG9ZMDVOClZGbDNUVlJGZVUxVVJURk5SRVV4VjJoalRr
+MXFXWGROVkVFMVRWUkZNVTFFUlRGWGFrRlRUVkpCZHdwRVoxbEVWbEZSUkVWM1pFWlIKVmtGblVU
+QkZlRTFKU1VKSmFrRk9RbWRyY1docmFVYzVkekJDUVZGRlJrRkJUME5CVVRoQlRVbEpRa05uUzBO
+QlVVVkJDbnB1UVZCVgplakkyVFhOaFpUUjNjelF6WTNwU05ERXZTakpSZEhKVFNWcFZTMjFXVlhO
+V2RXMUVZbGxJY2xCT2RsUllTMU5OV0VGalpYZFBVa1JSCldWZ0tVbkYyU0had2JqaERjMk5DTVN0
+dlIxaGFka2gzZUdvMGVsWXdWMHR2U3pKNlpWaHJZWFV6ZG1ONWJETklTVXQxY0VwbWNUSlUKUlVG
+RFpXWldhbW93ZEFwS1Z5dFlNelZRUjFkd09TOUlOWHBKVlU1V1RsWnFVemRWYlhNNE5FbDJTMmhT
+UWpnMU1USlFRamxWZVVoaApaMWhaVmxnMVIxZHdRV05XY0hsbWNteFNDa1pKT1ZGa2FHZ3JVR0py
+TUhWNWEzUmtZbVl2UTJSbVowaFBiMlZpY2xSMGQxSnNhazB3CmIwUjBXQ3N5UTNZMmFqQjNRa3Mz
+YUVRNGNGQjJaakVyZFhrS1IzcGplbWxuUVZVdk5FdDNOMlZhY1hsa1pqbENLelZTZFhCU0swbGEK
+YVhCWU5ERjRSV2xKY2t0U2QzRnBOVEUzVjFkNldHTnFZVWN5WTA1aVpqUTFNUXA0Y0VnMVVHNVdN
+Mmt4ZEhFd05HcE5SMUZWZWtaMwpTVVJCVVVGQ2J6UkhRVTFJTkhkSVVWbEVWbEl3VDBKQ1dVVkdT
+WGRZTkhaek9FSnBRbU5UWTI5a0NqVnViMXBJVWswNFJUUXJhVTFGClNVZEJNVlZrU1hkUk4wMUVi
+VUZHU1hkWU5IWnpPRUpwUW1OVFkyOWtOVzV2V2toU1RUaEZOQ3RwYjFKaGEwWkVRVk1LVFZKQmQw
+Um4KV1VSV1VWRkVSWGRrUmxGV1FXZFJNRVY0WjJkclFXZDFWVll6UkUxMFZ6WnpkMFJCV1VSV1Vq
+QlVRa0ZWZDBGM1JVSXZla0ZNUW1kTwpWZ3BJVVRoRlFrRk5RMEZSV1hkRVVWbEtTMjlhU1doMlkw
+NUJVVVZNUWxGQlJHZG5SVUpCUm1aUmNVOVVRVGRTZGpkTEsyeDFVVGR3CmJtRnpORUpaZDBoRkNq
+bEhSVkF2ZFc5b2RqWkxUM2t3VkVkUlJtSnlVbFJxUm05TVZrNUNPVUphTVhsdFRVUmFNQzlVU1hk
+SlZXTTMKZDJrM1lUaDBOVzFGY1ZsSU1UVXpkMWNLWVZkdmIybFRhbmxNVEdoMVNUUnpUbkpPUTA5
+MGFYTmtRbkV5Y2pKTlJsaDBObWd3YlVGUgpXVTlRZGpoU09FczNMMlpuVTNoSFJuRjZhSGxPYlcx
+V1RBb3hjVUpLYkdSNE16UlRjSGR6VkVGTVVWWlFZalJvUjNkS2VscG1jakZRClkzQkZVWGcyZUUx
+dVZHdzRlRVZYV2tVelRYTTVPWFZoVlhoaVVYRkpkMUoxQ2t4blFVOXJUa050V1RKdE9EbFdhSHBo
+U0VveGRWWTQKTlVGa1RTOTBSQ3RaYzIxc2JtNXFkRGxNVWtObGFtSkNhWEJxU1VkcVQxaHlaekZL
+VUN0c2VGWUtiWFZOTkhaSUsxQXZiV3h0ZUhOUQpVSG93WkRZMVlpdEZSMjFLV25CdlRHdFBMM1Jr
+VGs1MlExbDZha3B3VkVWWGNFVnpUelpPVFdoTFdXODlDaTB0TFMwdFJVNUVJRU5GClVsUkpSa2xE
+UVZSRkxTMHRMUzBLCi0te2JvdW5kYXJ5fS0tCg== \ No newline at end of file
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
index a44b54222589..5b4e4cb947cd 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
@@ -13,38 +13,38 @@ YTptbzpob3RzcG90MmRvdDAtcGVycHJvdmlkZXJzdWJzY3JpcHRpb246MS4wPC9EREZOYW1lPgog
ICAgICA8L1R5cGU+CiAgICA8L1JUUHJvcGVydGllcz4KICAgIDxOb2RlPgogICAgICA8Tm9kZU5h
bWU+aTAwMTwvTm9kZU5hbWU+CiAgICAgIDxOb2RlPgogICAgICAgIDxOb2RlTmFtZT5Ib21lU1A8
L05vZGVOYW1lPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkZyaWVuZGx5TmFt
-ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8VmFsdWU+Q2VudHVyeSBIb3VzZTwvVmFsdWU+CiAgICAg
-ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkZRRE48L05vZGVO
-YW1lPgogICAgICAgICAgPFZhbHVlPm1pNi5jby51azwvVmFsdWU+CiAgICAgICAgPC9Ob2RlPgog
-ICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlJvYW1pbmdDb25zb3J0aXVtT0k8L05v
-ZGVOYW1lPgogICAgICAgICAgPFZhbHVlPjExMjIzMyw0NDU1NjY8L1ZhbHVlPgogICAgICAgIDwv
-Tm9kZT4KICAgICAgPC9Ob2RlPgogICAgICA8Tm9kZT4KICAgICAgICA8Tm9kZU5hbWU+Q3JlZGVu
-dGlhbDwvTm9kZU5hbWU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9kZU5hbWU+UmVhbG08
-L05vZGVOYW1lPgogICAgICAgICAgPFZhbHVlPnNoYWtlbi5zdGlycmVkLmNvbTwvVmFsdWU+CiAg
-ICAgICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlVzZXJuYW1l
-UGFzc3dvcmQ8L05vZGVOYW1lPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2RlTmFt
-ZT5Vc2VybmFtZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT5qYW1lczwvVmFsdWU+CiAg
-ICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1lPlBh
-c3N3b3JkPC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZhbHVlPlltOXVaREF3Tnc9PTwvVmFsdWU+
-CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l
-PkVBUE1ldGhvZDwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICAgIDxO
-b2RlTmFtZT5FQVBUeXBlPC9Ob2RlTmFtZT4KICAgICAgICAgICAgICA8VmFsdWU+MjE8L1ZhbHVl
-PgogICAgICAgICAgICA8L05vZGU+CiAgICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICAgIDxO
-b2RlTmFtZT5Jbm5lck1ldGhvZDwvTm9kZU5hbWU+CiAgICAgICAgICAgICAgPFZhbHVlPk1TLUNI
-QVAtVjI8L1ZhbHVlPgogICAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8L05vZGU+CiAgICAg
-ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkRpZ2l0YWxDZXJ0
-aWZpY2F0ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l
-PkNlcnRpZmljYXRlVHlwZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT54NTA5djM8L1Zh
+ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8VmFsdWU+RXhhbXBsZSBOZXR3b3JrPC9WYWx1ZT4KICAg
+ICAgICA8L05vZGU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9kZU5hbWU+RlFETjwvTm9k
+ZU5hbWU+CiAgICAgICAgICA8VmFsdWU+aG90c3BvdC5leGFtcGxlLm5ldDwvVmFsdWU+CiAgICAg
+ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlJvYW1pbmdDb25z
+b3J0aXVtT0k8L05vZGVOYW1lPgogICAgICAgICAgPFZhbHVlPjExMjIzMyw0NDU1NjY8L1ZhbHVl
+PgogICAgICAgIDwvTm9kZT4KICAgICAgPC9Ob2RlPgogICAgICA8Tm9kZT4KICAgICAgICA8Tm9k
+ZU5hbWU+Q3JlZGVudGlhbDwvTm9kZU5hbWU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9k
+ZU5hbWU+UmVhbG08L05vZGVOYW1lPgogICAgICAgICAgPFZhbHVlPmV4YW1wbGUuY29tPC9WYWx1
+ZT4KICAgICAgICA8L05vZGU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9kZU5hbWU+VXNl
+cm5hbWVQYXNzd29yZDwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5v
+ZGVOYW1lPlVzZXJuYW1lPC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZhbHVlPnVzZXI8L1ZhbHVl
+PgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2RlTmFt
+ZT5QYXNzd29yZDwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT5jR0Z6YzNkdmNtUT08L1Zh
bHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2Rl
-TmFtZT5DZXJ0U0hBMjU2RmluZ2VycHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+
-MWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYx
-ZjFmMWYxZjwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgPC9Ob2RlPgogICAgICAg
-IDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlNJTTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9k
-ZT4KICAgICAgICAgICAgPE5vZGVOYW1lPklNU0k8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFs
-dWU+aW1zaTwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAg
-ICAgICAgPE5vZGVOYW1lPkVBUFR5cGU8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+MjQ8
-L1ZhbHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgIDwvTm9kZT4KICAgICAgPC9Ob2RlPgog
-ICAgPC9Ob2RlPgogIDwvTm9kZT4KPC9NZ210VHJlZT4K
+TmFtZT5FQVBNZXRob2Q8L05vZGVOYW1lPgogICAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAg
+ICA8Tm9kZU5hbWU+RUFQVHlwZTwvTm9kZU5hbWU+CiAgICAgICAgICAgICAgPFZhbHVlPjIxPC9W
+YWx1ZT4KICAgICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAg
+ICA8Tm9kZU5hbWU+SW5uZXJNZXRob2Q8L05vZGVOYW1lPgogICAgICAgICAgICAgIDxWYWx1ZT5N
+Uy1DSEFQLVYyPC9WYWx1ZT4KICAgICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPC9Ob2RlPgog
+ICAgICAgIDwvTm9kZT4KICAgICAgICA8Tm9kZT4KICAgICAgICAgIDxOb2RlTmFtZT5EaWdpdGFs
+Q2VydGlmaWNhdGU8L05vZGVOYW1lPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2Rl
+TmFtZT5DZXJ0aWZpY2F0ZVR5cGU8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+eDUwOXYz
+PC9WYWx1ZT4KICAgICAgICAgIDwvTm9kZT4KICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICA8
+Tm9kZU5hbWU+Q2VydFNIQTI1NkZpbmdlcnByaW50PC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZh
+bHVlPjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYx
+ZjFmMWYxZjFmMWY8L1ZhbHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgIDwvTm9kZT4KICAg
+ICAgICA8Tm9kZT4KICAgICAgICAgIDxOb2RlTmFtZT5TSU08L05vZGVOYW1lPgogICAgICAgICAg
+PE5vZGU+CiAgICAgICAgICAgIDxOb2RlTmFtZT5JTVNJPC9Ob2RlTmFtZT4KICAgICAgICAgICAg
+PFZhbHVlPjEyMzQ1Nio8L1ZhbHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+
+CiAgICAgICAgICAgIDxOb2RlTmFtZT5FQVBUeXBlPC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZh
+bHVlPjIzPC9WYWx1ZT4KICAgICAgICAgIDwvTm9kZT4KICAgICAgICA8L05vZGU+CiAgICAgIDwv
+Tm9kZT4KICAgIDwvTm9kZT4KICA8L05vZGU+CjwvTWdtdFRyZWU+
--{boundary}
Content-Type: application/x-x509-ca-cert
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64
index 906bfb397464..2775a9f419f9 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64
@@ -1,85 +1,86 @@
-Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
-dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
-cHBsaWNhdGlvbi9wYXNzcG9pbnQtcHJvZmlsZQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBi
-YXNlNjQKClBFMW5iWFJVY21WbElIaHRiRzV6UFNKemVXNWpiV3c2Wkcxa1pHWXhMaklpUGdvZ0lE
-eFdaWEpFVkVRK01TNHlQQzlXWlhKRVZFUSsKQ2lBZ1BFNXZaR1UrQ2lBZ0lDQThUbTlrWlU1aGJX
-VStVR1Z5VUhKdmRtbGtaWEpUZFdKelkzSnBjSFJwYjI0OEwwNXZaR1ZPWVcxbApQZ29nSUNBZ1BG
-SlVVSEp2Y0dWeWRHbGxjejRLSUNBZ0lDQWdQRlI1Y0dVK0NpQWdJQ0FnSUNBZ1BFUkVSazVoYldV
-K2RYSnVPbmRtCllUcHRienBvYjNSemNHOTBNbVJ2ZERBdGNHVnljSEp2ZG1sa1pYSnpkV0p6WTNK
-cGNIUnBiMjQ2TVM0d1BDOUVSRVpPWVcxbFBnb2cKSUNBZ0lDQThMMVI1Y0dVK0NpQWdJQ0E4TDFK
-VVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQThUbTlrWlU1aApiV1UrYVRB
-d01Ud3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0
-WlQ1SWIyMWxVMUE4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0Fn
-SUNBZ1BFNXZaR1ZPWVcxbFBrWnlhV1Z1Wkd4NVRtRnQKWlR3dlRtOWtaVTVoYldVK0NpQWdJQ0Fn
-SUNBZ0lDQThWbUZzZFdVK1EyVnVkSFZ5ZVNCSWIzVnpaVHd2Vm1Gc2RXVStDaUFnSUNBZwpJQ0Fn
-UEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQ
-a1pSUkU0OEwwNXZaR1ZPCllXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbTFwTmk1amJ5NTFh
-end2Vm1Gc2RXVStDaUFnSUNBZ0lDQWdQQzlPYjJSbFBnb2cKSUNBZ0lDQWdJRHhPYjJSbFBnb2dJ
-Q0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERiMjV6YjNKMGFYVnRUMGs4TDA1dgpa
-R1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhNakl6TXl3ME5EVTFOalk4TDFaaGJI
-VmxQZ29nSUNBZ0lDQWdJRHd2ClRtOWtaVDRLSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBOFRt
-OWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUTNKbFpHVnUKZEdsaGJEd3ZUbTlrWlU1aGJX
-VStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStVbVZoYkcw
-OApMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbk5vWVd0bGJpNXpkR2x5Y21W
-a0xtTnZiVHd2Vm1Gc2RXVStDaUFnCklDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJS
-bFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbFZ6WlhKdVlXMWwKVUdGemMzZHZjbVE4TDA1
-dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJs
-VG1GdApaVDVWYzJWeWJtRnRaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gx
-WlQ1cVlXMWxjend2Vm1Gc2RXVStDaUFnCklDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0Fn
-SUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsQmgKYzNOM2IzSmtQQzlP
-YjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGxsdE9YVmFSRUYzVG5jOVBUd3ZW
-bUZzZFdVKwpDaUFnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pUNEtJ
-Q0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsClBrVkJVRTFsZEdodlpEd3ZUbTlrWlU1aGJXVStD
-aUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRa
-VDVGUVZCVWVYQmxQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdVK01q
-RThMMVpoYkhWbApQZ29nSUNBZ0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lE
-eE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJRHhPCmIyUmxUbUZ0WlQ1SmJtNWxjazFsZEdodlpE
-d3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGsxVExVTkkKUVZBdFZq
-SThMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4TDA1
-dlpHVStDaUFnSUNBZwpJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNB
-Z0lDQWdQRTV2WkdWT1lXMWxQa1JwWjJsMFlXeERaWEowCmFXWnBZMkYwWlR3dlRtOWtaVTVoYldV
-K0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtO
-bGNuUnBabWxqWVhSbFZIbHdaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gx
-WlQ1NE5UQTVkak04TDFaaApiSFZsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0Fn
-SUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsClRtRnRaVDVEWlhKMFUwaEJNalUy
-Um1sdVoyVnlVSEpwYm5ROEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVSsK
-TVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1N
-V1l4WmpGbU1XWXhaakZtTVdZeApaakZtTVdZeFpqd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lDQThM
-MDV2WkdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnCklEeE9iMlJsUGdvZ0lDQWdJ
-Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBsTkpUVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBOFRt
-OWsKWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQa2xOVTBrOEwwNXZaR1ZPWVcxbFBn
-b2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1GcwpkV1UrYVcxemFUd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lD
-QThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnCklDQWdJQ0FnUEU1dlpH
-Vk9ZVzFsUGtWQlVGUjVjR1U4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
-K01qUTgKTDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHd2VG05
-a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnUEM5T2IyUmxQZ29nSUR3dlRtOWtaVDRLUEM5
-TloyMTBWSEpsWlQ0SwoKLS17Ym91bmRhcnl9CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24veC14
-NTA5LWNhLWNlcnQKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgpMUzB0TFMxQ1JV
-ZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VSTFJFTkRRV2hEWjBGM1NVSkJaMGxLUVVs
-TWJFWmtkM3BNClZuVnlUVUV3UjBOVGNVZFRTV0l6UkZGRlFrTjNWVUZOUWtsNFJVUkJUMEpuVGxZ
-S1FrRk5WRUl3VmtKVlEwSkVVVlJGZDBob1kwNU4KVkZsM1RWUkZlVTFVUlRGTlJFVXhWMmhqVGsx
-cVdYZE5WRUUxVFZSRk1VMUVSVEZYYWtGVFRWSkJkd3BFWjFsRVZsRlJSRVYzWkVaUgpWa0ZuVVRC
-RmVFMUpTVUpKYWtGT1FtZHJjV2hyYVVjNWR6QkNRVkZGUmtGQlQwTkJVVGhCVFVsSlFrTm5TME5C
-VVVWQkNucHVRVkJWCmVqSTJUWE5oWlRSM2N6UXpZM3BTTkRFdlNqSlJkSEpUU1ZwVlMyMVdWWE5X
-ZFcxRVlsbEljbEJPZGxSWVMxTk5XRUZqWlhkUFVrUlIKV1ZnS1VuRjJTSFp3YmpoRGMyTkNNU3R2
-UjFoYWRraDNlR28wZWxZd1YwdHZTeko2WlZocllYVXpkbU41YkROSVNVdDFjRXBtY1RKVQpSVUZE
-WldaV2Ftb3dkQXBLVnl0WU16VlFSMWR3T1M5SU5YcEpWVTVXVGxacVV6ZFZiWE00TkVsMlMyaFNR
-amcxTVRKUVFqbFZlVWhoCloxaFpWbGcxUjFkd1FXTldjSGxtY214U0NrWkpPVkZrYUdnclVHSnJN
-SFY1YTNSa1ltWXZRMlJtWjBoUGIyVmljbFIwZDFKc2FrMHcKYjBSMFdDc3lRM1kyYWpCM1FrczNh
-RVE0Y0ZCMlpqRXJkWGtLUjNwamVtbG5RVlV2TkV0M04yVmFjWGxrWmpsQ0t6VlNkWEJTSzBsYQph
-WEJZTkRGNFJXbEpja3RTZDNGcE5URTNWMWQ2V0dOcVlVY3lZMDVpWmpRMU1RcDRjRWcxVUc1V00y
-a3hkSEV3TkdwTlIxRlZla1ozClNVUkJVVUZDYnpSSFFVMUlOSGRJVVZsRVZsSXdUMEpDV1VWR1NY
-ZFlOSFp6T0VKcFFtTlRZMjlrQ2pWdWIxcElVazA0UlRRcmFVMUYKU1VkQk1WVmtTWGRSTjAxRWJV
-RkdTWGRZTkhaek9FSnBRbU5UWTI5a05XNXZXa2hTVFRoRk5DdHBiMUpoYTBaRVFWTUtUVkpCZDBS
-bgpXVVJXVVZGRVJYZGtSbEZXUVdkUk1FVjRaMmRyUVdkMVZWWXpSRTEwVnpaemQwUkJXVVJXVWpC
-VVFrRlZkMEYzUlVJdmVrRk1RbWRPClZncElVVGhGUWtGTlEwRlJXWGRFVVZsS1MyOWFTV2gyWTA1
-QlVVVk1RbEZCUkdkblJVSkJSbVpSY1U5VVFUZFNkamRMSzJ4MVVUZHcKYm1Gek5FSlpkMGhGQ2ps
-SFJWQXZkVzlvZGpaTFQza3dWRWRSUm1KeVVsUnFSbTlNVms1Q09VSmFNWGx0VFVSYU1DOVVTWGRK
-VldNMwpkMmszWVRoME5XMUZjVmxJTVRVemQxY0tZVmR2YjJsVGFubE1UR2gxU1RSelRuSk9RMDkw
-YVhOa1FuRXljakpOUmxoME5tZ3diVUZSCldVOVFkamhTT0VzM0wyWm5VM2hIUm5GNmFIbE9iVzFX
-VEFveGNVSktiR1I0TXpSVGNIZHpWRUZNVVZaUVlqUm9SM2RLZWxwbWNqRlEKWTNCRlVYZzJlRTF1
-Vkd3NGVFVlhXa1V6VFhNNU9YVmhWWGhpVVhGSmQxSjFDa3huUVU5clRrTnRXVEp0T0RsV2FIcGhT
-RW94ZFZZNApOVUZrVFM5MFJDdFpjMjFzYm01cWREbE1Va05sYW1KQ2FYQnFTVWRxVDFoeVp6RktV
-Q3RzZUZZS2JYVk5OSFpJSzFBdmJXeHRlSE5RClVIb3daRFkxWWl0RlIyMUtXbkJ2VEd0UEwzUmtU
-azUyUTFsNmFrcHdWRVZYY0VWelR6Wk9UV2hMV1c4OUNpMHRMUzB0UlU1RUlFTkYKVWxSSlJrbERR
-VlJGTFMwdExTMEsKLS17Ym91bmRhcnl9LS0K
+TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5
+PXtib3VuZGFyeX07IGNoYXJzZXQ9VVRGLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFz
+ZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9wYXNzcG9pbnQtcHJv
+ZmlsZTsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKClBF
+MW5iWFJVY21WbElIaHRiRzV6UFNKemVXNWpiV3c2Wkcxa1pHWXhMaklpUGdvZ0lEeFdaWEpFVkVR
+K01TNHlQQzlXWlhKRVZFUSsKQ2lBZ1BFNXZaR1UrQ2lBZ0lDQThUbTlrWlU1aGJXVStVR1Z5VUhK
+dmRtbGtaWEpUZFdKelkzSnBjSFJwYjI0OEwwNXZaR1ZPWVcxbApQZ29nSUNBZ1BGSlVVSEp2Y0dW
+eWRHbGxjejRLSUNBZ0lDQWdQRlI1Y0dVK0NpQWdJQ0FnSUNBZ1BFUkVSazVoYldVK2RYSnVPbmRt
+CllUcHRienBvYjNSemNHOTBNbVJ2ZERBdGNHVnljSEp2ZG1sa1pYSnpkV0p6WTNKcGNIUnBiMjQ2
+TVM0d1BDOUVSRVpPWVcxbFBnb2cKSUNBZ0lDQThMMVI1Y0dVK0NpQWdJQ0E4TDFKVVVISnZjR1Z5
+ZEdsbGN6NEtJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQThUbTlrWlU1aApiV1UrYVRBd01Ud3ZUbTlr
+WlU1aGJXVStDaUFnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1SWIyMWxV
+MUE4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZa
+R1ZPWVcxbFBrWnlhV1Z1Wkd4NVRtRnQKWlR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQThW
+bUZzZFdVK1JYaGhiWEJzWlNCT1pYUjNiM0pyUEM5V1lXeDFaVDRLSUNBZwpJQ0FnSUNBOEwwNXZa
+R1UrQ2lBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUmxGRVRq
+d3ZUbTlrClpVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBOFZtRnNkV1UrYUc5MGMzQnZkQzVsZUdGdGNH
+eGxMbTVsZER3dlZtRnNkV1UrQ2lBZ0lDQWcKSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2Iy
+UmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxKdllXMXBibWREYjI1egpiM0owYVhWdFQw
+azhMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQakV4TWpJek15dzBORFUxTmpZ
+OEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNB
+Z0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBOFRtOWsKWlU1aGJXVStRM0psWkdWdWRHbGhiRHd2VG05
+a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05awpaVTVoYldV
+K1VtVmhiRzA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUG1WNFlXMXdiR1V1
+WTI5dFBDOVdZV3gxClpUNEtJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ1BFNXZaR1Ur
+Q2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1ZYTmwKY201aGJXVlFZWE56ZDI5eVpEd3ZUbTlr
+WlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dgpaR1ZP
+WVcxbFBsVnpaWEp1WVcxbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQ
+blZ6WlhJOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQ
+RTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFRtRnQKWlQ1UVlYTnpkMjl5WkR3dlRtOWta
+VTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhXWVd4MVpUNWpSMFo2WXpOa2RtTnRVVDA4TDFaaApi
+SFZsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lD
+QWdJQ0FnSUNBZ0lEeE9iMlJsClRtRnRaVDVGUVZCTlpYUm9iMlE4TDA1dlpHVk9ZVzFsUGdvZ0lD
+QWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWcKSUNBOFRtOWtaVTVoYldVK1JV
+RlFWSGx3WlR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQakl4UEM5
+VwpZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4VG05
+a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnCklDQThUbTlrWlU1aGJXVStTVzV1WlhKTlpYUm9iMlE4TDA1
+dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJRHhXWVd4MVpUNU4KVXkxRFNFRlFMVll5UEM5
+V1lXeDFaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJs
+UGdvZwpJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0Fn
+SUR4T2IyUmxUbUZ0WlQ1RWFXZHBkR0ZzClEyVnlkR2xtYVdOaGRHVThMMDV2WkdWT1lXMWxQZ29n
+SUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEow
+YVdacFkyRjBaVlI1Y0dVOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVStl
+RFV3T1hZegpQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZ0lDQWdJ
+RHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4ClRtOWtaVTVoYldVK1EyVnlkRk5JUVRJMU5rWnBi
+bWRsY25CeWFXNTBQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmgKYkhWbFBqRm1N
+V1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1X
+WXhaakZtTVdZeApaakZtTVdZeFpqRm1NV1k4TDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2Iy
+UmxQZ29nSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnCklDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lD
+QWdJRHhPYjJSbFRtRnRaVDVUU1UwOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWcKUEU1dlpH
+VStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1SlRWTkpQQzlPYjJSbFRtRnRaVDRLSUNB
+Z0lDQWdJQ0FnSUNBZwpQRlpoYkhWbFBqRXlNelExTmlvOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNB
+Z1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrCkNpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJS
+bFRtRnRaVDVGUVZCVWVYQmxQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmgKYkhW
+bFBqSXpQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZ0lDQThMMDV2
+WkdVK0NpQWdJQ0FnSUR3dgpUbTlrWlQ0S0lDQWdJRHd2VG05a1pUNEtJQ0E4TDA1dlpHVStDand2
+VFdkdGRGUnlaV1UrCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXg1
+MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFDUlVk
+SlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtRVWxN
+YkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5UbFlL
+UWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpUazFx
+V1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5VVEJG
+ZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMwTkJV
+VVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZYTldk
+VzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01TdHZS
+MWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJVRkRa
+V1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJoU1Fq
+ZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdKck1I
+VjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtzM2FF
+UTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxhCmFY
+QllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVXTTJr
+eGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZHU1hk
+WU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFFYlVG
+R1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJkMFJu
+CldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldVakJV
+UWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZMDVC
+VVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZDamxI
+UlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNYZEpW
+V00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1EwOTBh
+WE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JXMVdU
+QW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVFMXVW
+R3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhwaFNF
+b3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpGS1VD
+dHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNSa1Rr
+NTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmtsRFFW
+UkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo= \ No newline at end of file
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64
index 3fa97d1cdd76..7023453b3992 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64
@@ -1,85 +1,86 @@
-Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
-dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
-cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
-IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
-SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
-YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
-UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
-V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
-M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
-MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
-VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
-RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
-QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
-QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
-QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
-bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
-MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
-Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
-ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
-YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
-VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
-YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
-RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
-bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
-MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
-MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
-UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
-eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
-QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
-OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
-dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
-S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
-K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
-dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
-TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
-SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
-WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
-VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
-MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
-Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
-V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
-a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
-eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
-QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
-VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
-KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
-bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
-OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
-Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
-VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
-UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
-SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
-WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
-V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
-bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
-QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
-LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
-UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
-VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
-bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
-azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
-VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
-TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
-TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
-dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
-RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
-U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
-ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
-M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
-CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
-TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
-U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
-YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
-MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
-akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
-MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
-amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
-ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
-OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
-MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
-MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
-aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
-S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
-a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
-RFFWUkZMUzB0TFMwSwo=
+TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5
+PXtib3VuZGFyeX07IGNoYXJzZXQ9VVRGLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFz
+ZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXBhc3Nwb2ludC1w
+cm9maWxlOyBjaGFyc2V0PVVURi04CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoK
+UEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29nSUR4V1pYSkVW
+RVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVoYldVK1VHVnlV
+SEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0FnUEZKVVVISnZj
+R1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhiV1UrZFhKdU9u
+ZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZM0pwY0hScGIy
+NDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThMMUpVVUhKdmNH
+VnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSthVEF3TVR3dlRt
+OWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVJYjIx
+bFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1
+dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNB
+OFZtRnNkV1UrUlhoaGJYQnNaU0JPWlhSM2IzSnJQQzlXWVd4MVpUNEtJQ0FnCklDQWdJQ0E4TDA1
+dlpHVStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStSbEZF
+VGp3dlRtOWsKWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVSthRzkwYzNCdmRDNWxlR0Z0
+Y0d4bExtNWxkRHd2Vm1Gc2RXVStDaUFnSUNBZwpJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhP
+YjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERiMjV6CmIzSjBhWFZ0
+VDBrOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhNakl6TXl3ME5EVTFO
+alk4TDFaaGJIVmwKUGdvZ0lDQWdJQ0FnSUR3dlRtOWtaVDRLSUNBZ0lDQWdQQzlPYjJSbFBnb2dJ
+Q0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0E4VG05awpaVTVoYldVK1EzSmxaR1Z1ZEdsaGJEd3ZU
+bTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrClpVNWhi
+V1UrVW1WaGJHMDhMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbVY0WVcxd2JH
+VXVZMjl0UEM5V1lXeDEKWlQ0S0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEU1dlpH
+VStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrVlhObApjbTVoYldWUVlYTnpkMjl5WkR3dlRt
+OWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2ClpH
+Vk9ZVzFsUGxWelpYSnVZVzFsUEM5T2IyUmxUbUZ0WlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRlpoYkhW
+bFBuVnpaWEk4TDFaaGJIVmwKUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNB
+Z1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsVG1GdApaVDVRWVhOemQyOXlaRHd2VG05
+a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1alIwWjZZek5rZG1OdFVUMDhMMVpo
+CmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFn
+SUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNUZRVkJOWlhSb2IyUThMMDV2WkdWT1lXMWxQZ29n
+SUNBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZwpJQ0E4VG05a1pVNWhiV1Ur
+UlVGUVZIbHdaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqSXhQ
+QzlXCllXeDFaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThU
+bTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWcKSUNBOFRtOWtaVTVoYldVK1NXNXVaWEpOWlhSb2IyUThM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1TgpVeTFEU0VGUUxWWXlQ
+QzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2Iy
+UmxQZ29nCklDQWdJQ0FnSUR3dlRtOWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lD
+QWdJRHhPYjJSbFRtRnRaVDVFYVdkcGRHRnMKUTJWeWRHbG1hV05oZEdVOEwwNXZaR1ZPWVcxbFBn
+b2dJQ0FnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbApUbUZ0WlQ1RFpY
+SjBhV1pwWTJGMFpWUjVjR1U4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+K2VEVXdPWFl6ClBDOVdZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNB
+Z0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQTgKVG05a1pVNWhiV1UrUTJWeWRGTklRVEkxTmta
+cGJtZGxjbkJ5YVc1MFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaApiSFZsUGpG
+bU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZt
+TVdZeFpqRm1NV1l4ClpqRm1NV1l4WmpGbU1XWThMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlP
+YjJSbFBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWcKSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0Fn
+SUNBZ0lEeE9iMlJsVG1GdFpUNVRTVTA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZwpQRTV2
+WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVKVFZOSlBDOU9iMlJsVG1GdFpUNEtJ
+Q0FnSUNBZ0lDQWdJQ0FnClBGWmhiSFZsUGpFeU16UTFOaW84TDFaaGJIVmxQZ29nSUNBZ0lDQWdJ
+Q0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVSsKQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9i
+MlJsVG1GdFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaApi
+SFZsUGpJelBDOVdZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOEww
+NXZaR1UrQ2lBZ0lDQWdJRHd2ClRtOWtaVDRLSUNBZ0lEd3ZUbTlrWlQ0S0lDQThMMDV2WkdVK0Nq
+d3ZUV2R0ZEZSeVpXVSsKCi0te2JvdW5kYXJ5fQpDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL3gt
+eDUwOS1jYS1jZXJ0CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoKTFMwdExTMUNS
+VWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUkxSRU5EUVdoRFowRjNTVUpCWjBsS1FV
+bE1iRVprZDNwTQpWblZ5VFVFd1IwTlRjVWRUU1dJelJGRkZRa04zVlVGTlFrbDRSVVJCVDBKblRs
+WUtRa0ZOVkVJd1ZrSlZRMEpFVVZSRmQwaG9ZMDVOClZGbDNUVlJGZVUxVVJURk5SRVV4VjJoalRr
+MXFXWGROVkVFMVRWUkZNVTFFUlRGWGFrRlRUVkpCZHdwRVoxbEVWbEZSUkVWM1pFWlIKVmtGblVU
+QkZlRTFKU1VKSmFrRk9RbWRyY1docmFVYzVkekJDUVZGRlJrRkJUME5CVVRoQlRVbEpRa05uUzBO
+QlVVVkJDbnB1UVZCVgplakkyVFhOaFpUUjNjelF6WTNwU05ERXZTakpSZEhKVFNWcFZTMjFXVlhO
+V2RXMUVZbGxJY2xCT2RsUllTMU5OV0VGalpYZFBVa1JSCldWZ0tVbkYyU0had2JqaERjMk5DTVN0
+dlIxaGFka2gzZUdvMGVsWXdWMHR2U3pKNlpWaHJZWFV6ZG1ONWJETklTVXQxY0VwbWNUSlUKUlVG
+RFpXWldhbW93ZEFwS1Z5dFlNelZRUjFkd09TOUlOWHBKVlU1V1RsWnFVemRWYlhNNE5FbDJTMmhT
+UWpnMU1USlFRamxWZVVoaApaMWhaVmxnMVIxZHdRV05XY0hsbWNteFNDa1pKT1ZGa2FHZ3JVR0py
+TUhWNWEzUmtZbVl2UTJSbVowaFBiMlZpY2xSMGQxSnNhazB3CmIwUjBXQ3N5UTNZMmFqQjNRa3Mz
+YUVRNGNGQjJaakVyZFhrS1IzcGplbWxuUVZVdk5FdDNOMlZhY1hsa1pqbENLelZTZFhCU0swbGEK
+YVhCWU5ERjRSV2xKY2t0U2QzRnBOVEUzVjFkNldHTnFZVWN5WTA1aVpqUTFNUXA0Y0VnMVVHNVdN
+Mmt4ZEhFd05HcE5SMUZWZWtaMwpTVVJCVVVGQ2J6UkhRVTFJTkhkSVVWbEVWbEl3VDBKQ1dVVkdT
+WGRZTkhaek9FSnBRbU5UWTI5a0NqVnViMXBJVWswNFJUUXJhVTFGClNVZEJNVlZrU1hkUk4wMUVi
+VUZHU1hkWU5IWnpPRUpwUW1OVFkyOWtOVzV2V2toU1RUaEZOQ3RwYjFKaGEwWkVRVk1LVFZKQmQw
+Um4KV1VSV1VWRkVSWGRrUmxGV1FXZFJNRVY0WjJkclFXZDFWVll6UkUxMFZ6WnpkMFJCV1VSV1Vq
+QlVRa0ZWZDBGM1JVSXZla0ZNUW1kTwpWZ3BJVVRoRlFrRk5RMEZSV1hkRVVWbEtTMjlhU1doMlkw
+NUJVVVZNUWxGQlJHZG5SVUpCUm1aUmNVOVVRVGRTZGpkTEsyeDFVVGR3CmJtRnpORUpaZDBoRkNq
+bEhSVkF2ZFc5b2RqWkxUM2t3VkVkUlJtSnlVbFJxUm05TVZrNUNPVUphTVhsdFRVUmFNQzlVU1hk
+SlZXTTMKZDJrM1lUaDBOVzFGY1ZsSU1UVXpkMWNLWVZkdmIybFRhbmxNVEdoMVNUUnpUbkpPUTA5
+MGFYTmtRbkV5Y2pKTlJsaDBObWd3YlVGUgpXVTlRZGpoU09FczNMMlpuVTNoSFJuRjZhSGxPYlcx
+V1RBb3hjVUpLYkdSNE16UlRjSGR6VkVGTVVWWlFZalJvUjNkS2VscG1jakZRClkzQkZVWGcyZUUx
+dVZHdzRlRVZYV2tVelRYTTVPWFZoVlhoaVVYRkpkMUoxQ2t4blFVOXJUa050V1RKdE9EbFdhSHBo
+U0VveGRWWTQKTlVGa1RTOTBSQ3RaYzIxc2JtNXFkRGxNVWtObGFtSkNhWEJxU1VkcVQxaHlaekZL
+VUN0c2VGWUtiWFZOTkhaSUsxQXZiV3h0ZUhOUQpVSG93WkRZMVlpdEZSMjFLV25CdlRHdFBMM1Jr
+VGs1MlExbDZha3B3VkVWWGNFVnpUelpPVFdoTFdXODlDaTB0TFMwdFJVNUVJRU5GClVsUkpSa2xE
+UVZSRkxTMHRMUzBLCg== \ No newline at end of file
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64
index 975f8e539cc3..5c23f61b1711 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64
@@ -1,85 +1,86 @@
-Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
-dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTMyCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
-cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
-IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
-SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
-YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
-UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
-V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
-M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
-MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
-VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
-RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
-QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
-QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
-QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
-bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
-MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
-Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
-ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
-YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
-VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
-YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
-RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
-bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
-MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
-MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
-UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
-eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
-QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
-OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
-dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
-S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
-K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
-dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
-TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
-SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
-WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
-VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
-MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
-Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
-V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
-a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
-eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
-QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
-VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
-KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
-bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
-OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
-Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
-VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
-UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
-SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
-WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
-V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
-bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
-QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
-LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
-UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
-VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
-bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
-azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
-VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
-TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
-TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
-dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
-RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
-U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
-ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
-M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
-CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
-TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
-U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
-YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
-MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
-akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
-MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
-amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
-ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
-OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
-MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
-MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
-aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
-S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
-a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
-RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
+TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5
+PXtib3VuZGFyeX07IGNoYXJzZXQ9VVRGLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogOGJp
+dAoKLS17Ym91bmRhcnl9CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24veC1wYXNzcG9pbnQtcHJv
+ZmlsZTsgY2hhcnNldD1VVEYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKClBF
+MW5iWFJVY21WbElIaHRiRzV6UFNKemVXNWpiV3c2Wkcxa1pHWXhMaklpUGdvZ0lEeFdaWEpFVkVR
+K01TNHlQQzlXWlhKRVZFUSsKQ2lBZ1BFNXZaR1UrQ2lBZ0lDQThUbTlrWlU1aGJXVStVR1Z5VUhK
+dmRtbGtaWEpUZFdKelkzSnBjSFJwYjI0OEwwNXZaR1ZPWVcxbApQZ29nSUNBZ1BGSlVVSEp2Y0dW
+eWRHbGxjejRLSUNBZ0lDQWdQRlI1Y0dVK0NpQWdJQ0FnSUNBZ1BFUkVSazVoYldVK2RYSnVPbmRt
+CllUcHRienBvYjNSemNHOTBNbVJ2ZERBdGNHVnljSEp2ZG1sa1pYSnpkV0p6WTNKcGNIUnBiMjQ2
+TVM0d1BDOUVSRVpPWVcxbFBnb2cKSUNBZ0lDQThMMVI1Y0dVK0NpQWdJQ0E4TDFKVVVISnZjR1Z5
+ZEdsbGN6NEtJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQThUbTlrWlU1aApiV1UrYVRBd01Ud3ZUbTlr
+WlU1aGJXVStDaUFnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1SWIyMWxV
+MUE4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZa
+R1ZPWVcxbFBrWnlhV1Z1Wkd4NVRtRnQKWlR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQThW
+bUZzZFdVK1JYaGhiWEJzWlNCT1pYUjNiM0pyUEM5V1lXeDFaVDRLSUNBZwpJQ0FnSUNBOEwwNXZa
+R1UrQ2lBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUmxGRVRq
+d3ZUbTlrClpVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBOFZtRnNkV1UrYUc5MGMzQnZkQzVsZUdGdGNH
+eGxMbTVsZER3dlZtRnNkV1UrQ2lBZ0lDQWcKSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2Iy
+UmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxKdllXMXBibWREYjI1egpiM0owYVhWdFQw
+azhMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQakV4TWpJek15dzBORFUxTmpZ
+OEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNB
+Z0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBOFRtOWsKWlU1aGJXVStRM0psWkdWdWRHbGhiRHd2VG05
+a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05awpaVTVoYldV
+K1VtVmhiRzA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUG1WNFlXMXdiR1V1
+WTI5dFBDOVdZV3gxClpUNEtJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ1BFNXZaR1Ur
+Q2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1ZYTmwKY201aGJXVlFZWE56ZDI5eVpEd3ZUbTlr
+WlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dgpaR1ZP
+WVcxbFBsVnpaWEp1WVcxbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQ
+blZ6WlhJOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQ
+RTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFRtRnQKWlQ1UVlYTnpkMjl5WkR3dlRtOWta
+VTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhXWVd4MVpUNWpSMFo2WXpOa2RtTnRVVDA4TDFaaApi
+SFZsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lD
+QWdJQ0FnSUNBZ0lEeE9iMlJsClRtRnRaVDVGUVZCTlpYUm9iMlE4TDA1dlpHVk9ZVzFsUGdvZ0lD
+QWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWcKSUNBOFRtOWtaVTVoYldVK1JV
+RlFWSGx3WlR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQakl4UEM5
+VwpZV3gxWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4VG05
+a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnCklDQThUbTlrWlU1aGJXVStTVzV1WlhKTlpYUm9iMlE4TDA1
+dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJRHhXWVd4MVpUNU4KVXkxRFNFRlFMVll5UEM5
+V1lXeDFaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJs
+UGdvZwpJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0Fn
+SUR4T2IyUmxUbUZ0WlQ1RWFXZHBkR0ZzClEyVnlkR2xtYVdOaGRHVThMMDV2WkdWT1lXMWxQZ29n
+SUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEow
+YVdacFkyRjBaVlI1Y0dVOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVStl
+RFV3T1hZegpQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZ0lDQWdJ
+RHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4ClRtOWtaVTVoYldVK1EyVnlkRk5JUVRJMU5rWnBi
+bWRsY25CeWFXNTBQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmgKYkhWbFBqRm1N
+V1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1X
+WXhaakZtTVdZeApaakZtTVdZeFpqRm1NV1k4TDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2Iy
+UmxQZ29nSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnCklDQWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lD
+QWdJRHhPYjJSbFRtRnRaVDVUU1UwOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWcKUEU1dlpH
+VStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1SlRWTkpQQzlPYjJSbFRtRnRaVDRLSUNB
+Z0lDQWdJQ0FnSUNBZwpQRlpoYkhWbFBqRXlNelExTmlvOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNB
+Z1BDOU9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrCkNpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJS
+bFRtRnRaVDVGUVZCVWVYQmxQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmgKYkhW
+bFBqSXpQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZ0lDQThMMDV2
+WkdVK0NpQWdJQ0FnSUR3dgpUbTlrWlQ0S0lDQWdJRHd2VG05a1pUNEtJQ0E4TDA1dlpHVStDand2
+VFdkdGRGUnlaV1UrCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXg1
+MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFDUlVk
+SlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtRVWxN
+YkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5UbFlL
+UWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpUazFx
+V1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5VVEJG
+ZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMwTkJV
+VVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZYTldk
+VzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01TdHZS
+MWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJVRkRa
+V1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJoU1Fq
+ZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdKck1I
+VjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtzM2FF
+UTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxhCmFY
+QllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVXTTJr
+eGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZHU1hk
+WU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFFYlVG
+R1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJkMFJu
+CldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldVakJV
+UWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZMDVC
+VVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZDamxI
+UlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNYZEpW
+V00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1EwOTBh
+WE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JXMVdU
+QW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVFMXVW
+R3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhwaFNF
+b3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpGS1VD
+dHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNSa1Rr
+NTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmtsRFFW
+UkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo= \ No newline at end of file
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64
new file mode 100644
index 000000000000..bab7607512c8
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64
@@ -0,0 +1,88 @@
+TUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5
+PXtib3VuZGFyeX07IGNoYXJzZXQ9VVRGLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFz
+ZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXBhc3Nwb2ludC1w
+cm9maWxlOyBjaGFyc2V0PVVURi04CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoK
+UEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29nSUR4V1pYSkVW
+RVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVoYldVK1VHVnlV
+SEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0FnUEZKVVVISnZj
+R1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhiV1UrZFhKdU9u
+ZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZM0pwY0hScGIy
+NDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThMMUpVVUhKdmNH
+VnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVStWWEJrWVhSbFNX
+UmxiblJwWm1sbGNqd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lEeFdZV3gxWlQ0eE1qTTBQQzlXWVd4
+MVpUNEsKSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoYldV
+K2FUQXdNVHd2VG05a1pVNWhiV1UrQ2lBZwpJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJS
+bFRtRnRaVDVJYjIxbFUxQThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJRHhPCmIyUmxQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdFpUd3ZUbTlrWlU1aGJXVStDaUFn
+SUNBZ0lDQWcKSUNBOFZtRnNkV1UrUlhoaGJYQnNaU0JPWlhSM2IzSnJQQzlXWVd4MVpUNEtJQ0Fn
+SUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZwpQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlr
+WlU1aGJXVStSbEZFVGp3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthRzkw
+YzNCdmRDNWxlR0Z0Y0d4bExtNWxkRHd2Vm1Gc2RXVStDaUFnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJ
+Q0FnSUNBZ0lEeE8KYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERi
+MjV6YjNKMGFYVnRUMGs4TDA1dlpHVk9ZVzFsUGdvZwpJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhN
+akl6TXl3ME5EVTFOalk4TDFaaGJIVmxQZ29nSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnCklDQWdQ
+QzlPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUTNKbFpH
+VnVkR2xoYkR3dlRtOWsKWlU1aGJXVStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lD
+QThUbTlrWlU1aGJXVStVbVZoYkcwOEwwNXZaR1ZPWVcxbApQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJI
+VmxQbVY0WVcxd2JHVXVZMjl0UEM5V1lXeDFaVDRLSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnCklD
+QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrVlhObGNtNWhiV1ZRWVhO
+emQyOXlaRHd2VG05a1pVNWgKYldVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNB
+Z0lDQWdQRTV2WkdWT1lXMWxQbFZ6WlhKdVlXMWxQQzlPYjJSbApUbUZ0WlQ0S0lDQWdJQ0FnSUNB
+Z0lDQWdQRlpoYkhWbFBuVnpaWEk4TDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29n
+CklDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsVG1GdFpUNVFZWE56
+ZDI5eVpEd3ZUbTlrWlU1aGJXVSsKQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1alIwWjZZek5r
+ZG1OdFVUMDhMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbApQZ29nSUNBZ0lDQWdJQ0Fn
+UEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1RlFWQk5aWFJvYjJROEwwNXZa
+R1ZPCllXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQThU
+bTlrWlU1aGJXVStSVUZRVkhsd1pUd3YKVG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lDQWdQ
+RlpoYkhWbFBqSXhQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEM5TwpiMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrU1c1dVpY
+Sk5aWFJvCmIyUThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gxWlQ1TlV5
+MURTRUZRTFZZeVBDOVdZV3gxWlQ0S0lDQWcKSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZwpJQ0E4VG05a1pU
+NEtJQ0FnSUNBZ0lDQWdJRHhPYjJSbFRtRnRaVDVFYVdkcGRHRnNRMlZ5ZEdsbWFXTmhkR1U4TDA1
+dlpHVk9ZVzFsClBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJS
+bFRtRnRaVDVEWlhKMGFXWnBZMkYwWlZSNWNHVTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNB
+Z0lDQThWbUZzZFdVK2VEVXdPWFl6UEM5V1lXeDFaVDRLSUNBZ0lDQWdJQ0FnSUR3dgpUbTlrWlQ0
+S0lDQWdJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRMlZ5
+ZEZOSVFUSTFOa1pwCmJtZGxjbkJ5YVc1MFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0Fn
+UEZaaGJIVmxQakZtTVdZeFpqRm1NV1l4WmpGbU1XWXgKWmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4
+WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZOEwxWmhiSFZsUGdvZwpJQ0Fn
+SUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOFRtOWta
+VDRLSUNBZ0lDQWdJQ0FnCklEeE9iMlJsVG1GdFpUNVRTVTA4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKVFZOSlBDOU9i
+MlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQakV5TXpRMU5pbzhMMVpoYkhWbApQ
+Z29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lD
+QWdJQ0FnSUR4T2IyUmxUbUZ0ClpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lD
+QWdJQ0FnUEZaaGJIVmxQakl6UEM5V1lXeDFaVDRLSUNBZ0lDQWcKSUNBZ0lEd3ZUbTlrWlQ0S0lD
+QWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUR3dlRtOWtaVDRLSUNB
+OApMMDV2WkdVK0Nqd3ZUV2R0ZEZSeVpXVSsKCi0te2JvdW5kYXJ5fQpDb250ZW50LVR5cGU6IGFw
+cGxpY2F0aW9uL3gteDUwOS1jYS1jZXJ0CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2
+NAoKTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVUkxSRU5EUVdoRFow
+RjNTVUpCWjBsS1FVbE1iRVprZDNwTQpWblZ5VFVFd1IwTlRjVWRUU1dJelJGRkZRa04zVlVGTlFr
+bDRSVVJCVDBKblRsWUtRa0ZOVkVJd1ZrSlZRMEpFVVZSRmQwaG9ZMDVOClZGbDNUVlJGZVUxVVJU
+Rk5SRVV4VjJoalRrMXFXWGROVkVFMVRWUkZNVTFFUlRGWGFrRlRUVkpCZHdwRVoxbEVWbEZSUkVW
+M1pFWlIKVmtGblVUQkZlRTFKU1VKSmFrRk9RbWRyY1docmFVYzVkekJDUVZGRlJrRkJUME5CVVRo
+QlRVbEpRa05uUzBOQlVVVkJDbnB1UVZCVgplakkyVFhOaFpUUjNjelF6WTNwU05ERXZTakpSZEhK
+VFNWcFZTMjFXVlhOV2RXMUVZbGxJY2xCT2RsUllTMU5OV0VGalpYZFBVa1JSCldWZ0tVbkYyU0ha
+d2JqaERjMk5DTVN0dlIxaGFka2gzZUdvMGVsWXdWMHR2U3pKNlpWaHJZWFV6ZG1ONWJETklTVXQx
+Y0VwbWNUSlUKUlVGRFpXWldhbW93ZEFwS1Z5dFlNelZRUjFkd09TOUlOWHBKVlU1V1RsWnFVemRW
+YlhNNE5FbDJTMmhTUWpnMU1USlFRamxWZVVoaApaMWhaVmxnMVIxZHdRV05XY0hsbWNteFNDa1pK
+T1ZGa2FHZ3JVR0pyTUhWNWEzUmtZbVl2UTJSbVowaFBiMlZpY2xSMGQxSnNhazB3CmIwUjBXQ3N5
+UTNZMmFqQjNRa3MzYUVRNGNGQjJaakVyZFhrS1IzcGplbWxuUVZVdk5FdDNOMlZhY1hsa1pqbENL
+elZTZFhCU0swbGEKYVhCWU5ERjRSV2xKY2t0U2QzRnBOVEUzVjFkNldHTnFZVWN5WTA1aVpqUTFN
+UXA0Y0VnMVVHNVdNMmt4ZEhFd05HcE5SMUZWZWtaMwpTVVJCVVVGQ2J6UkhRVTFJTkhkSVVWbEVW
+bEl3VDBKQ1dVVkdTWGRZTkhaek9FSnBRbU5UWTI5a0NqVnViMXBJVWswNFJUUXJhVTFGClNVZEJN
+VlZrU1hkUk4wMUViVUZHU1hkWU5IWnpPRUpwUW1OVFkyOWtOVzV2V2toU1RUaEZOQ3RwYjFKaGEw
+WkVRVk1LVFZKQmQwUm4KV1VSV1VWRkVSWGRrUmxGV1FXZFJNRVY0WjJkclFXZDFWVll6UkUxMFZ6
+WnpkMFJCV1VSV1VqQlVRa0ZWZDBGM1JVSXZla0ZNUW1kTwpWZ3BJVVRoRlFrRk5RMEZSV1hkRVVW
+bEtTMjlhU1doMlkwNUJVVVZNUWxGQlJHZG5SVUpCUm1aUmNVOVVRVGRTZGpkTEsyeDFVVGR3CmJt
+RnpORUpaZDBoRkNqbEhSVkF2ZFc5b2RqWkxUM2t3VkVkUlJtSnlVbFJxUm05TVZrNUNPVUphTVhs
+dFRVUmFNQzlVU1hkSlZXTTMKZDJrM1lUaDBOVzFGY1ZsSU1UVXpkMWNLWVZkdmIybFRhbmxNVEdo
+MVNUUnpUbkpPUTA5MGFYTmtRbkV5Y2pKTlJsaDBObWd3YlVGUgpXVTlRZGpoU09FczNMMlpuVTNo
+SFJuRjZhSGxPYlcxV1RBb3hjVUpLYkdSNE16UlRjSGR6VkVGTVVWWlFZalJvUjNkS2VscG1jakZR
+ClkzQkZVWGcyZUUxdVZHdzRlRVZYV2tVelRYTTVPWFZoVlhoaVVYRkpkMUoxQ2t4blFVOXJUa050
+V1RKdE9EbFdhSHBoU0VveGRWWTQKTlVGa1RTOTBSQ3RaYzIxc2JtNXFkRGxNVWtObGFtSkNhWEJx
+U1VkcVQxaHlaekZLVUN0c2VGWUtiWFZOTkhaSUsxQXZiV3h0ZUhOUQpVSG93WkRZMVlpdEZSMjFL
+V25CdlRHdFBMM1JrVGs1MlExbDZha3B3VkVWWGNFVnpUelpPVFdoTFdXODlDaTB0TFMwdFJVNUVJ
+RU5GClVsUkpSa2xEUVZSRkxTMHRMUzBLCi0te2JvdW5kYXJ5fS0tCg== \ No newline at end of file
diff --git a/wifi/tests/assets/hsr1/README.txt b/wifi/tests/assets/hsr1/README.txt
index d1f8384fc979..9f3cdc274ee1 100644
--- a/wifi/tests/assets/hsr1/README.txt
+++ b/wifi/tests/assets/hsr1/README.txt
@@ -2,4 +2,5 @@ HSR1ProfileWithCACert.conf - unencoded installation file that contains a Passpoi
HSR1ProfileWithCACert.base64 - base64 encoded of the data contained in HSR1ProfileWithCAWith.conf
HSR1ProfileWithNonBase64Part.base64 - base64 encoded installation file that contains a part of non-base64 encoding type
HSR1ProfileWithMissingBoundary.base64 - base64 encoded installation file with missing end-boundary in the MIME data
-HSR1ProfileWithInvalidContentType.base64 - base64 encoded installation file with that contains a MIME part with an invalid content type.
+HSR1ProfileWithInvalidContentType.base64 - base64 encoded installation file with that contains a MIME part with an invalid content type
+HSR1ProfileWithUpdateIdentifier.base64 - base64 encoded installation file with that contains an R2 update identifier
diff --git a/wifi/tests/src/android/net/wifi/WifiClientTest.java b/wifi/tests/src/android/net/wifi/WifiClientTest.java
new file mode 100644
index 000000000000..42cab55305b9
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/WifiClientTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals;
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.net.MacAddress;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.WifiClient}.
+ */
+@SmallTest
+public class WifiClientTest {
+ private static final String INTERFACE_NAME = "wlan0";
+ private static final String MAC_ADDRESS_STRING = "00:0a:95:9d:68:16";
+ private static final MacAddress MAC_ADDRESS = MacAddress.fromString(MAC_ADDRESS_STRING);
+
+ /**
+ * Verify parcel write/read with WifiClient.
+ */
+ @Test
+ public void testWifiClientParcelWriteRead() throws Exception {
+ WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS);
+
+ assertParcelSane(writeWifiClient, 1);
+ }
+
+ /**
+ * Verify equals with WifiClient.
+ */
+ @Test
+ public void testWifiClientEquals() throws Exception {
+ WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS);
+ WifiClient writeWifiClientEquals = new WifiClient(MAC_ADDRESS);
+
+ assertEquals(writeWifiClient, writeWifiClientEquals);
+ assertEquals(writeWifiClient.hashCode(), writeWifiClientEquals.hashCode());
+ assertFieldCountEquals(1, WifiClient.class);
+ }
+
+ /**
+ * Verify not-equals with WifiClient.
+ */
+ @Test
+ public void testWifiClientNotEquals() throws Exception {
+ final MacAddress macAddressNotEquals = MacAddress.fromString("00:00:00:00:00:00");
+ WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS);
+ WifiClient writeWifiClientNotEquals = new WifiClient(macAddressNotEquals);
+
+ assertNotEquals(writeWifiClient, writeWifiClientNotEquals);
+ assertNotEquals(writeWifiClient.hashCode(), writeWifiClientNotEquals.hashCode());
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index f8a0c8f5fab8..8d0579bde92c 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -99,8 +99,7 @@ public class WifiManagerTest {
private static final String[] TEST_MAC_ADDRESSES = {"da:a1:19:0:0:0"};
@Mock Context mContext;
- @Mock
- android.net.wifi.IWifiManager mWifiService;
+ @Mock android.net.wifi.IWifiManager mWifiService;
@Mock ApplicationInfo mApplicationInfo;
@Mock WifiConfiguration mApConfig;
@Mock SoftApCallback mSoftApCallback;
@@ -115,7 +114,8 @@ public class WifiManagerTest {
private TestLooper mLooper;
private WifiManager mWifiManager;
- @Before public void setUp() throws Exception {
+ @Before
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mLooper = new TestLooper();
mHandler = spy(new Handler(mLooper.getLooper()));
@@ -210,7 +210,7 @@ public class WifiManagerTest {
@Test
public void testCreationOfLocalOnlyHotspotSubscription() throws Exception {
try (WifiManager.LocalOnlyHotspotSubscription sub =
- mWifiManager.new LocalOnlyHotspotSubscription()) {
+ mWifiManager.new LocalOnlyHotspotSubscription()) {
sub.close();
}
}
@@ -752,17 +752,17 @@ public class WifiManagerTest {
* Verify client-provided callback is being called through callback proxy
*/
@Test
- public void softApCallbackProxyCallsOnNumClientsChanged() throws Exception {
+ public void softApCallbackProxyCallsOnConnectedClientsChanged() throws Exception {
ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor =
ArgumentCaptor.forClass(ISoftApCallback.Stub.class);
mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
anyInt());
- final int testNumClients = 3;
- callbackCaptor.getValue().onNumClientsChanged(testNumClients);
+ final List<WifiClient> testClients = new ArrayList();
+ callbackCaptor.getValue().onConnectedClientsChanged(testClients);
mLooper.dispatchAll();
- verify(mSoftApCallback).onNumClientsChanged(testNumClients);
+ verify(mSoftApCallback).onConnectedClientsChanged(testClients);
}
/*
@@ -776,14 +776,14 @@ public class WifiManagerTest {
verify(mWifiService).registerSoftApCallback(any(IBinder.class), callbackCaptor.capture(),
anyInt());
- final int testNumClients = 5;
+ final List<WifiClient> testClients = new ArrayList();
callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_ENABLING, 0);
- callbackCaptor.getValue().onNumClientsChanged(testNumClients);
+ callbackCaptor.getValue().onConnectedClientsChanged(testClients);
callbackCaptor.getValue().onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL);
mLooper.dispatchAll();
verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_ENABLING, 0);
- verify(mSoftApCallback).onNumClientsChanged(testNumClients);
+ verify(mSoftApCallback).onConnectedClientsChanged(testClients);
verify(mSoftApCallback).onStateChanged(WIFI_AP_STATE_FAILED, SAP_START_FAILURE_GENERAL);
}
@@ -1020,8 +1020,8 @@ public class WifiManagerTest {
verifyNoMoreInteractions(mWifiService);
}
- /**
-i * Verify that a call to cancel WPS immediately returns a failure.
+ /**
+ * Verify that a call to cancel WPS immediately returns a failure.
*/
@Test
public void testCancelWpsImmediatelyFailsWithCallback() {
@@ -1324,7 +1324,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Verify getting the factory MAC address.
- * @throws Exception
*/
@Test
public void testGetFactoryMacAddress() throws Exception {
@@ -1371,7 +1370,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of isEnhancedOpenSupported
- * @throws Exception
*/
@Test
public void testIsEnhancedOpenSupported() throws Exception {
@@ -1385,7 +1383,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of isWpa3SaeSupported
- * @throws Exception
*/
@Test
public void testIsWpa3SaeSupported() throws Exception {
@@ -1399,7 +1396,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of isWpa3SuiteBSupported
- * @throws Exception
*/
@Test
public void testIsWpa3SuiteBSupported() throws Exception {
@@ -1413,7 +1409,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of isEasyConnectSupported
- * @throws Exception
*/
@Test
public void testIsEasyConnectSupported() throws Exception {
@@ -1427,7 +1422,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#addNetwork(WifiConfiguration)}
- * @throws Exception
*/
@Test
public void testAddNetwork() throws Exception {
@@ -1444,7 +1438,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#addNetwork(WifiConfiguration)}
- * @throws Exception
*/
@Test
public void testUpdateNetwork() throws Exception {
@@ -1466,7 +1459,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#enableNetwork(int, boolean)}
- * @throws Exception
*/
@Test
public void testEnableNetwork() throws Exception {
@@ -1478,7 +1470,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#disableNetwork(int)}
- * @throws Exception
*/
@Test
public void testDisableNetwork() throws Exception {
@@ -1489,10 +1480,19 @@ i * Verify that a call to cancel WPS immediately returns a failure.
}
/**
- * Test behavior of {@link WifiManager#disconnect()}
+ * Test behavior of {@link WifiManager#allowAutojoin(int, boolean)}
* @throws Exception
*/
@Test
+ public void testAllowAutojoin() throws Exception {
+ mWifiManager.allowAutojoin(1, true);
+ verify(mWifiService).allowAutojoin(eq(1), eq(true));
+ }
+
+ /**
+ * Test behavior of {@link WifiManager#disconnect()}
+ */
+ @Test
public void testDisconnect() throws Exception {
when(mWifiService.disconnect(anyString())).thenReturn(true);
assertTrue(mWifiManager.disconnect());
@@ -1501,7 +1501,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#reconnect()}
- * @throws Exception
*/
@Test
public void testReconnect() throws Exception {
@@ -1512,7 +1511,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#reassociate()}
- * @throws Exception
*/
@Test
public void testReassociate() throws Exception {
@@ -1523,7 +1521,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#getSupportedFeatures()}
- * @throws Exception
*/
@Test
public void testGetSupportedFeatures() throws Exception {
@@ -1550,7 +1547,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#getControllerActivityEnergyInfo()}
- * @throws Exception
*/
@Test
public void testGetControllerActivityEnergyInfo() throws Exception {
@@ -1563,7 +1559,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#getConnectionInfo()}
- * @throws Exception
*/
@Test
public void testGetConnectionInfo() throws Exception {
@@ -1575,7 +1570,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#isDualModeSupported()} ()}
- * @throws Exception
*/
@Test
public void testIsDualModeSupported() throws Exception {
@@ -1586,7 +1580,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#isDualBandSupported()}
- * @throws Exception
*/
@Test
public void testIsDualBandSupported() throws Exception {
@@ -1597,7 +1590,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#getDhcpInfo()}
- * @throws Exception
*/
@Test
public void testGetDhcpInfo() throws Exception {
@@ -1610,7 +1602,6 @@ i * Verify that a call to cancel WPS immediately returns a failure.
/**
* Test behavior of {@link WifiManager#setWifiEnabled(boolean)}
- * @throws Exception
*/
@Test
public void testSetWifiEnabled() throws Exception {
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
index d9a1d9afff61..439e67259cb9 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
@@ -54,6 +54,8 @@ public class ConfigParserTest {
"assets/hsr1/HSR1ProfileWithInvalidContentType.base64";
private static final String PASSPOINT_INSTALLATION_FILE_WITHOUT_PROFILE =
"assets/hsr1/HSR1ProfileWithoutProfile.base64";
+ private static final String PASSPOINT_INSTALLATION_FILE_WITH_UPDATE_ID =
+ "assets/hsr1/HSR1ProfileWithUpdateIdentifier.base64";
/**
* Read the content of the given resource file into a String.
@@ -85,17 +87,17 @@ public class ConfigParserTest {
// HomeSP configuration.
HomeSp homeSp = new HomeSp();
- homeSp.setFriendlyName("Century House");
- homeSp.setFqdn("mi6.co.uk");
+ homeSp.setFriendlyName("Example Network");
+ homeSp.setFqdn("hotspot.example.net");
homeSp.setRoamingConsortiumOis(new long[] {0x112233L, 0x445566L});
config.setHomeSp(homeSp);
// Credential configuration.
Credential credential = new Credential();
- credential.setRealm("shaken.stirred.com");
+ credential.setRealm("example.com");
Credential.UserCredential userCredential = new Credential.UserCredential();
- userCredential.setUsername("james");
- userCredential.setPassword("Ym9uZDAwNw==");
+ userCredential.setUsername("user");
+ userCredential.setPassword("cGFzc3dvcmQ=");
userCredential.setEapType(21);
userCredential.setNonEapInnerMethod("MS-CHAP-V2");
credential.setUserCredential(userCredential);
@@ -106,8 +108,8 @@ public class ConfigParserTest {
certCredential.setCertSha256Fingerprint(certSha256Fingerprint);
credential.setCertCredential(certCredential);
Credential.SimCredential simCredential = new Credential.SimCredential();
- simCredential.setImsi("imsi");
- simCredential.setEapType(24);
+ simCredential.setImsi("123456*");
+ simCredential.setEapType(23);
credential.setSimCredential(simCredential);
credential.setCaCertificate(FakeKeys.CA_CERT0);
config.setCredential(credential);
@@ -201,4 +203,21 @@ public class ConfigParserTest {
assertNull(ConfigParser.parsePasspointConfig(
"application/x-wifi-config", configStr.getBytes()));
}
+
+ /**
+ * Verify a valid installation file is parsed successfully with the matching contents, and that
+ * Update identifier is cleared.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseConfigFileWithUpdateIdentifier() throws Exception {
+ String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_UPDATE_ID);
+ PasspointConfiguration expectedConfig = generateConfigurationFromProfile();
+ PasspointConfiguration actualConfig =
+ ConfigParser.parsePasspointConfig(
+ "application/x-wifi-config", configStr.getBytes());
+ // Expected configuration does not contain an update identifier
+ assertTrue(actualConfig.equals(expectedConfig));
+ }
} \ No newline at end of file
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index c3b074eaee5a..f501b16d2c79 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -183,7 +183,7 @@ public class PasspointConfigurationTest {
PasspointConfiguration config = PasspointTestUtils.createConfig();
assertTrue(config.validate());
- assertTrue(config.validateForR2());
+ assertFalse(config.isOsuProvisioned());
}
/**
@@ -241,7 +241,6 @@ public class PasspointConfigurationTest {
config.setPolicy(null);
assertTrue(config.validate());
- assertTrue(config.validateForR2());
}
/**
@@ -271,7 +270,6 @@ public class PasspointConfigurationTest {
config.setAaaServerTrustedNames(null);
assertTrue(config.validate());
- assertTrue(config.validateForR2());
}
/**
@@ -348,4 +346,17 @@ public class PasspointConfigurationTest {
PasspointConfiguration copyConfig = new PasspointConfiguration(sourceConfig);
assertTrue(copyConfig.equals(sourceConfig));
}
+
+ /**
+ * Verify that a configuration containing all fields is valid for R2.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateFullR2Config() throws Exception {
+ PasspointConfiguration config = PasspointTestUtils.createR2Config();
+ assertTrue(config.validate());
+ assertTrue(config.validateForR2());
+ assertTrue(config.isOsuProvisioned());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java
index adf74ebe36e5..8d55acb87f15 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointTestUtils.java
@@ -132,7 +132,6 @@ public class PasspointTestUtils {
*/
public static PasspointConfiguration createConfig() {
PasspointConfiguration config = new PasspointConfiguration();
- config.setUpdateIdentifier(1234);
config.setHomeSp(createHomeSp());
config.setAaaServerTrustedNames(
new String[] {"trusted.fqdn.com", "another-trusted.fqdn.com"});
@@ -145,7 +144,6 @@ public class PasspointTestUtils {
trustRootCertList.put("trustRoot.cert2.com",
new byte[CERTIFICATE_FINGERPRINT_BYTES]);
config.setTrustRootCertList(trustRootCertList);
- config.setUpdateIdentifier(1);
config.setCredentialPriority(120);
config.setSubscriptionCreationTimeInMillis(231200);
config.setSubscriptionExpirationTimeInMillis(2134232);
@@ -160,4 +158,15 @@ public class PasspointTestUtils {
config.setServiceFriendlyNames(friendlyNames);
return config;
}
+
+ /**
+ * Helper function for creating an R2 {@link PasspointConfiguration} for testing.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ public static PasspointConfiguration createR2Config() {
+ PasspointConfiguration config = createConfig();
+ config.setUpdateIdentifier(1234);
+ return config;
+ }
}