summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp8
-rw-r--r--api/current.txt162
-rw-r--r--api/removed.txt2
-rw-r--r--api/system-current.txt440
-rw-r--r--api/test-current.txt28
-rw-r--r--cmds/statsd/src/atoms.proto188
-rw-r--r--cmds/statsd/src/external/ResourceHealthManagerPuller.cpp32
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.cpp5
-rw-r--r--cmds/telecom/src/com/android/commands/telecom/Telecom.java29
-rw-r--r--config/boot-image-profile.txt19
-rw-r--r--config/hiddenapi-greylist.txt7
-rw-r--r--config/preloaded-classes3
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java97
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl2
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl14
-rw-r--r--core/java/android/app/Activity.java66
-rw-r--r--core/java/android/app/ActivityManager.java20
-rw-r--r--core/java/android/app/ActivityThread.java6
-rw-r--r--core/java/android/app/ActivityView.java4
-rw-r--r--core/java/android/app/DownloadManager.java6
-rw-r--r--core/java/android/app/KeyguardManager.java5
-rw-r--r--core/java/android/app/UiAutomation.java2
-rw-r--r--core/java/android/app/VrManager.java13
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java154
-rw-r--r--core/java/android/app/admin/PasswordMetrics.java148
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java175
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java188
-rw-r--r--core/java/android/content/Context.java16
-rw-r--r--core/java/android/content/Intent.java25
-rw-r--r--core/java/android/content/om/OverlayManager.java21
-rw-r--r--core/java/android/content/pm/PackageManager.java8
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java12
-rw-r--r--core/java/android/content/rollback/IRollbackManager.aidl6
-rw-r--r--core/java/android/hardware/display/ColorDisplayManager.java61
-rw-r--r--core/java/android/hardware/display/IColorDisplayManager.aidl2
-rw-r--r--core/java/android/hardware/hdmi/HdmiUtils.java172
-rw-r--r--core/java/android/hardware/location/ActivityChangedEvent.aidl19
-rw-r--r--core/java/android/hardware/location/ActivityChangedEvent.java92
-rw-r--r--core/java/android/hardware/location/ActivityRecognitionEvent.java87
-rw-r--r--core/java/android/hardware/location/ActivityRecognitionHardware.java279
-rw-r--r--core/java/android/hardware/location/IActivityRecognitionHardware.aidl62
-rw-r--r--core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl36
-rw-r--r--core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl32
-rw-r--r--core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl34
-rw-r--r--core/java/android/net/CaptivePortal.java4
-rw-r--r--core/java/android/net/ConnectivityManager.java13
-rw-r--r--core/java/android/net/DnsPacket.java235
-rw-r--r--core/java/android/net/DnsResolver.java289
-rw-r--r--core/java/android/net/ICaptivePortal.aidl5
-rw-r--r--core/java/android/net/IConnectivityManager.aidl3
-rw-r--r--core/java/android/net/LinkProperties.java44
-rw-r--r--core/java/android/net/LinkPropertiesParcelable.aidl1
-rw-r--r--core/java/android/net/NetworkCapabilities.java3
-rw-r--r--core/java/android/net/NetworkUtils.java28
-rw-r--r--core/java/android/net/SSLCertificateSocketFactory.java9
-rw-r--r--core/java/android/net/VpnService.java23
-rw-r--r--core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl2
-rw-r--r--core/java/android/net/ipmemorystore/NetworkAttributes.java66
-rw-r--r--core/java/android/net/ipmemorystore/SameL3NetworkResponse.java3
-rw-r--r--core/java/android/net/metrics/IpConnectivityLog.java13
-rw-r--r--core/java/android/net/util/SocketUtils.java74
-rw-r--r--core/java/android/os/FileUtils.java11
-rw-r--r--core/java/android/os/HandlerThread.java6
-rw-r--r--core/java/android/os/IDeviceIdleController.aidl2
-rw-r--r--core/java/android/os/PowerManager.java19
-rw-r--r--core/java/android/os/Process.java3
-rw-r--r--core/java/android/os/ServiceSpecificException.java3
-rw-r--r--core/java/android/os/WorkSource.java22
-rw-r--r--core/java/android/os/storage/StorageVolume.java6
-rw-r--r--core/java/android/os/storage/VolumeInfo.java5
-rw-r--r--core/java/android/provider/DeviceConfig.java9
-rw-r--r--core/java/android/provider/DocumentsContract.java55
-rw-r--r--core/java/android/provider/DocumentsProvider.java12
-rw-r--r--core/java/android/provider/MediaStore.java49
-rw-r--r--core/java/android/provider/Settings.java22
-rw-r--r--core/java/android/service/euicc/EuiccService.java13
-rw-r--r--core/java/android/service/vr/IVrManager.aidl8
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java7
-rw-r--r--core/java/android/util/DocumentsStatsLog.java168
-rw-r--r--core/java/android/view/GestureDetector.java85
-rw-r--r--core/java/android/view/IWindowManager.aidl5
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java5
-rw-r--r--core/java/android/view/InsetsController.java10
-rw-r--r--core/java/android/view/InsetsState.java7
-rw-r--r--core/java/android/view/Surface.java13
-rw-r--r--core/java/android/view/SurfaceControl.java218
-rw-r--r--core/java/android/view/SurfaceView.java219
-rw-r--r--core/java/android/view/View.java25
-rw-r--r--core/java/android/view/ViewRootImpl.java36
-rw-r--r--core/java/android/view/WindowInsets.java5
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java13
-rw-r--r--core/java/android/webkit/WebViewRenderer.java3
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java10
-rw-r--r--core/java/com/android/internal/logging/MetricsLogger.java98
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java105
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java7
-rw-r--r--core/java/com/android/internal/os/KernelCpuProcReader.java162
-rw-r--r--core/java/com/android/internal/os/KernelCpuUidTimeReader.java3
-rw-r--r--core/java/com/android/internal/os/KernelSingleUidTimeReader.java10
-rw-r--r--core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java179
-rw-r--r--core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java239
-rw-r--r--core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java296
-rw-r--r--core/java/com/android/internal/os/KernelUidCpuTimeReader.java213
-rw-r--r--core/java/com/android/internal/os/KernelUidCpuTimeReaderBase.java60
-rw-r--r--core/java/com/android/internal/os/LooperStats.java3
-rw-r--r--core/java/com/android/internal/os/Zygote.java47
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBarService.aidl2
-rw-r--r--core/java/com/android/internal/util/CollectionUtils.java4
-rw-r--r--core/java/com/android/internal/util/Protocol.java2
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl1
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java29
-rw-r--r--core/jni/Android.bp6
-rw-r--r--core/jni/AndroidRuntime.cpp6
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp42
-rw-r--r--core/jni/android/graphics/BitmapFactory.cpp39
-rw-r--r--core/jni/android/graphics/BitmapFactory.h2
-rw-r--r--core/jni/android/graphics/BitmapRegionDecoder.cpp8
-rw-r--r--core/jni/android/graphics/Graphics.cpp86
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h21
-rw-r--r--core/jni/android/graphics/ImageDecoder.cpp6
-rw-r--r--core/jni/android/graphics/Paint.cpp63
-rw-r--r--core/jni/android_app_ActivityThread.cpp9
-rw-r--r--core/jni/android_graphics_ColorSpace.cpp110
-rw-r--r--core/jni/android_hardware_location_ActivityRecognitionHardware.cpp132
-rw-r--r--core/jni/android_net_NetUtils.cpp88
-rw-r--r--core/jni/android_os_Debug.cpp359
-rw-r--r--core/jni/android_view_SurfaceControl.cpp32
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp1111
-rw-r--r--core/jni/fd_utils.cpp275
-rw-r--r--core/jni/fd_utils.h11
-rw-r--r--core/proto/android/server/jobscheduler.proto19
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/values/config.xml30
-rw-r--r--core/res/res/values/strings.xml4
-rw-r--r--core/res/res/values/symbols.xml7
-rw-r--r--core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java205
-rw-r--r--core/tests/coretests/src/android/os/WorkSourceTest.java37
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java8
-rw-r--r--core/tests/coretests/src/android/view/WindowInsetsTest.java13
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java16
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java316
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java199
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java3
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java260
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java326
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java331
-rw-r--r--core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java32
-rw-r--r--core/tests/coretests/src/com/android/server/wm/test/filters/CoreTestsFilter.java48
-rw-r--r--core/tests/hdmitests/Android.mk2
-rw-r--r--core/tests/hdmitests/src/android/hardware/hdmi/HdmiUtilsTest.java145
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java2
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--graphics/java/android/graphics/Bitmap.java65
-rw-r--r--graphics/java/android/graphics/BitmapFactory.java35
-rw-r--r--graphics/java/android/graphics/BitmapRegionDecoder.java5
-rw-r--r--graphics/java/android/graphics/ColorSpace.java141
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java17
-rw-r--r--graphics/java/android/graphics/Paint.java15
-rw-r--r--location/java/android/location/LocationRequest.java30
-rw-r--r--location/lib/java/com/android/location/provider/ActivityChangedEvent.java57
-rw-r--r--location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java71
-rw-r--r--location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java133
-rw-r--r--location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java76
-rw-r--r--location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java90
-rw-r--r--lowpan/tests/Android.mk2
-rw-r--r--lowpan/tests/AndroidManifest.xml2
-rw-r--r--lowpan/tests/AndroidTest.xml2
-rw-r--r--lowpan/tests/README.md2
-rwxr-xr-xlowpan/tests/runtests.sh2
-rw-r--r--lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java7
-rw-r--r--lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java4
-rw-r--r--media/java/android/media/AudioRecordingConfiguration.java14
-rw-r--r--media/java/android/media/AudioSystem.java3
-rw-r--r--media/java/android/media/IRemoteVolumeController.aidl7
-rw-r--r--media/java/android/media/MediaController2.java20
-rw-r--r--media/java/android/media/MediaSession2.java51
-rw-r--r--media/java/android/media/MediaSession2Service.java171
-rw-r--r--media/java/android/media/RemoteControlClient.java161
-rw-r--r--media/java/android/media/RemoteController.java6
-rw-r--r--media/java/android/media/Session2Token.java28
-rw-r--r--media/java/android/media/audiofx/AudioEffect.java32
-rw-r--r--media/java/android/media/session/ControllerCallbackLink.java20
-rw-r--r--media/java/android/media/session/ISessionControllerCallback.aidl4
-rw-r--r--media/java/android/media/session/MediaController.java2
-rw-r--r--media/java/android/media/session/MediaSession.java904
-rw-r--r--media/java/android/media/session/MediaSessionEngine.java1479
-rw-r--r--media/java/android/media/session/MediaSessionManager.java62
-rw-r--r--media/java/android/media/session/PlaybackState.java156
-rw-r--r--media/java/android/media/session/SessionCallbackLink.java48
-rw-r--r--media/java/android/media/tv/TvInputInfo.java45
-rw-r--r--media/java/android/service/media/MediaBrowserService.java3
-rw-r--r--native/android/Android.bp3
-rw-r--r--native/android/libandroid.map.txt15
-rw-r--r--native/android/sensor.cpp15
-rw-r--r--native/android/surface_control.cpp225
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java3
-rw-r--r--packages/NetworkStack/src/android/net/dhcp/DhcpServer.java8
-rw-r--r--packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java10
-rw-r--r--packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java49
-rw-r--r--packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml10
-rw-r--r--packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java37
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java29
-rw-r--r--packages/Shell/AndroidManifest.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml5
-rw-r--r--packages/SystemUI/res/values/strings.xml3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java118
-rw-r--r--packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java202
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/dock/DockManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java130
-rw-r--r--packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java90
-rw-r--r--proto/src/metrics_constants/metrics_constants.proto42
-rw-r--r--proto/src/system_messages.proto4
-rw-r--r--proto/src/wifi.proto34
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java102
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java89
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/MagnificationController.java902
-rw-r--r--services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java81
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java54
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java169
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java49
-rw-r--r--services/core/java/com/android/server/DeviceIdleController.java293
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java178
-rw-r--r--services/core/java/com/android/server/LooperStatsService.java4
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java124
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockManagerInternal.java13
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java137
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/AppCompactor.java2
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java17
-rw-r--r--services/core/java/com/android/server/am/UserController.java3
-rw-r--r--services/core/java/com/android/server/attention/AttentionManagerService.java33
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java33
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java15
-rw-r--r--services/core/java/com/android/server/display/ColorDisplayService.java39
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java8
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java10
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java10
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java145
-rw-r--r--services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java12
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java89
-rw-r--r--services/core/java/com/android/server/job/controllers/QuotaController.java228
-rw-r--r--services/core/java/com/android/server/location/ActivityRecognitionProxy.java118
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java12
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java13
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java11
-rw-r--r--services/core/java/com/android/server/media/MediaSessionServiceImpl.java15
-rw-r--r--services/core/java/com/android/server/notification/NotificationDelegate.java3
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java2
-rw-r--r--services/core/java/com/android/server/pm/DynamicCodeLoggingService.java219
-rw-r--r--services/core/java/com/android/server/pm/Installer.java25
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java168
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java42
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java130
-rw-r--r--services/core/java/com/android/server/pm/dex/DexLogger.java80
-rw-r--r--services/core/java/com/android/server/pm/dex/DexManager.java2
-rw-r--r--services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java11
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java1
-rw-r--r--services/core/java/com/android/server/role/RoleManagerService.java38
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java361
-rw-r--r--services/core/java/com/android/server/rollback/RollbackStore.java256
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java39
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java6
-rw-r--r--services/core/java/com/android/server/testharness/TestHarnessModeService.java328
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java11
-rw-r--r--services/core/java/com/android/server/vr/VrManagerService.java9
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java132
-rw-r--r--services/core/java/com/android/server/wm/ActivityDisplay.java16
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java90
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java39
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java2
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java11
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java23
-rw-r--r--services/core/java/com/android/server/wm/DragState.java23
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java4
-rw-r--r--services/core/java/com/android/server/wm/InsetsStateController.java5
-rw-r--r--services/core/java/com/android/server/wm/RootActivityContainer.java37
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskRecord.java8
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotLoader.java3
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotPersister.java28
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java11
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java46
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java5
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_SystemServer.cpp9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java115
-rw-r--r--services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java235
-rw-r--r--services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java72
-rw-r--r--services/java/com/android/server/SystemServer.java15
-rw-r--r--services/net/java/android/net/apf/ApfFilter.java5
-rw-r--r--services/net/java/android/net/dhcp/DhcpClient.java32
-rw-r--r--services/net/java/android/net/ip/ConnectivityPacketTracker.java4
-rw-r--r--services/net/java/android/net/ip/InterfaceController.java105
-rw-r--r--services/net/java/android/net/ip/IpClient.java79
-rw-r--r--services/net/java/android/net/ip/IpNeighborMonitor.java13
-rw-r--r--services/net/java/android/net/ip/IpReachabilityMonitor.java34
-rw-r--r--services/net/java/android/net/ip/IpServer.java2
-rw-r--r--services/net/java/android/net/netlink/NetlinkSocket.java10
-rw-r--r--services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java119
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java240
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java758
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java75
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java39
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java28
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java32
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java305
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java120
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java22
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java2
-rw-r--r--startop/OWNERS1
-rw-r--r--telecomm/java/android/telecom/Conference.java61
-rw-r--r--telecomm/java/android/telecom/ConnectionService.java25
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapter.java18
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapterServant.java19
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java6
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java16
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl2
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl2
-rw-r--r--telephony/java/android/telephony/DataFailCause.java1337
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java20
-rw-r--r--telephony/java/android/telephony/data/DataCallResponse.java5
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java91
-rw-r--r--test-mock/api/current.txt3
-rw-r--r--tests/DexLoggerIntegrationTests/Android.mk39
-rw-r--r--tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java288
-rw-r--r--tests/DexLoggerIntegrationTests/src/cpp/com_android_dcl_Jni.cpp (renamed from tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/Alarm.aidl)13
-rw-r--r--tests/DexLoggerIntegrationTests/src/cpp/test_executable.cpp20
-rw-r--r--tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java7
-rw-r--r--tests/net/java/android/net/DnsPacketTest.java159
-rw-r--r--tests/net/java/android/net/ip/IpClientTest.java18
-rw-r--r--tests/net/java/android/net/ip/IpReachabilityMonitorTest.java7
-rw-r--r--tests/net/java/android/net/ip/IpServerTest.java32
-rw-r--r--tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java49
-rw-r--r--tests/net/java/com/android/server/connectivity/TetheringTest.java7
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java26
-rw-r--r--tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java236
-rw-r--r--tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java65
-rw-r--r--tests/utils/SleepUtils/AlarmService/Android.mk26
-rw-r--r--tests/utils/SleepUtils/AlarmService/AndroidManifest.xml31
-rw-r--r--tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmImpl.java77
-rw-r--r--tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmService.java52
-rw-r--r--tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpCall.java42
-rw-r--r--tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpController.java59
-rw-r--r--tests/utils/SleepUtils/Android.mk2
-rw-r--r--tests/utils/SleepUtils/README23
-rw-r--r--tests/utils/SleepUtils/SleepHelper/Android.mk29
-rw-r--r--tests/utils/SleepUtils/SleepHelper/AndroidManifest.xml21
-rw-r--r--tests/utils/SleepUtils/SleepHelper/src/com/android/testing/sleephelper/SetAlarm.java152
-rw-r--r--tests/utils/SleepUtils/WakeLoopService/Android.mk24
-rw-r--r--tests/utils/SleepUtils/WakeLoopService/AndroidManifest.xml32
-rw-r--r--tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/FileUtil.java53
-rw-r--r--tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/WakeLoopService.java98
-rw-r--r--tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/WakeUpCall.java112
-rw-r--r--tests/utils/testutils/java/com/android/test/filters/SelectTest.java338
-rw-r--r--tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java219
-rw-r--r--tools/aapt2/cmd/Compile.cpp6
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java4
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java31
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java7
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkSpecifier.java20
-rw-r--r--wifi/java/android/net/wifi/WifiNetworkSuggestion.java24
-rw-r--r--wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java4
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java39
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java50
-rw-r--r--wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java55
416 files changed, 19186 insertions, 8909 deletions
diff --git a/Android.bp b/Android.bp
index 286be8212694..e63463c07313 100644
--- a/Android.bp
+++ b/Android.bp
@@ -191,6 +191,10 @@ java_defaults {
"core/java/android/hardware/input/IInputDevicesChangedListener.aidl",
"core/java/android/hardware/input/ITabletModeChangedListener.aidl",
"core/java/android/hardware/iris/IIrisService.aidl",
+ "core/java/android/hardware/location/IActivityRecognitionHardware.aidl",
+ "core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl",
+ "core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl",
+ "core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl",
"core/java/android/hardware/location/IGeofenceHardware.aidl",
"core/java/android/hardware/location/IGeofenceHardwareCallback.aidl",
"core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl",
@@ -207,7 +211,6 @@ java_defaults {
"core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl",
"core/java/android/hardware/usb/IUsbManager.aidl",
"core/java/android/hardware/usb/IUsbSerialReader.aidl",
- "core/java/android/net/ICaptivePortal.aidl",
"core/java/android/net/IConnectivityManager.aidl",
"core/java/android/hardware/ISensorPrivacyListener.aidl",
"core/java/android/hardware/ISensorPrivacyManager.aidl",
@@ -762,8 +765,6 @@ java_defaults {
required: [
// TODO: remove gps_debug when the build system propagates "required" properly.
"gps_debug.conf",
- // Loaded with System.loadLibrary by android.view.textclassifier
- "libmedia2_jni",
],
dxflags: [
@@ -884,6 +885,7 @@ aidl_interface {
srcs: [
"core/java/android/net/ApfCapabilitiesParcelable.aidl",
"core/java/android/net/DhcpResultsParcelable.aidl",
+ "core/java/android/net/ICaptivePortal.aidl",
"core/java/android/net/INetworkMonitor.aidl",
"core/java/android/net/INetworkMonitorCallbacks.aidl",
"core/java/android/net/IIpMemoryStore.aidl",
diff --git a/api/current.txt b/api/current.txt
index b8283ddac9eb..520d5bf93790 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3941,7 +3941,8 @@ package android.app {
method public boolean isBackgroundRestricted();
method @Deprecated public boolean isInLockTaskMode();
method public boolean isLowRamDevice();
- method public static boolean isRunningInTestHarness();
+ method @Deprecated public static boolean isRunningInTestHarness();
+ method public static boolean isRunningInUserTestHarness();
method public static boolean isUserAMonkey();
method @RequiresPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) public void killBackgroundProcesses(String);
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int);
@@ -6822,6 +6823,7 @@ package android.app.admin {
field public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
field public static final String EXTRA_DELEGATION_SCOPES = "android.app.extra.DELEGATION_SCOPES";
field public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
+ field @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY) public static final String EXTRA_PASSWORD_COMPLEXITY = "android.app.extra.PASSWORD_COMPLEXITY";
field public static final String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
field public static final String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
field public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";
@@ -9625,7 +9627,7 @@ package android.content {
public abstract class Context {
ctor public Context();
- method public abstract boolean bindIsolatedService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull String);
+ method public boolean bindIsolatedService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull String);
method public abstract boolean bindService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int);
method @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") public abstract int checkCallingOrSelfPermission(@NonNull String);
method @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") public abstract int checkCallingOrSelfUriPermission(android.net.Uri, int);
@@ -9678,7 +9680,7 @@ package android.content {
method public abstract java.io.File getNoBackupFilesDir();
method public abstract java.io.File getObbDir();
method public abstract java.io.File[] getObbDirs();
- method public abstract String getOpPackageName();
+ method public String getOpPackageName();
method public abstract String getPackageCodePath();
method public abstract android.content.pm.PackageManager getPackageManager();
method public abstract String getPackageName();
@@ -9745,7 +9747,7 @@ package android.content {
method public abstract void unbindService(@NonNull android.content.ServiceConnection);
method public void unregisterComponentCallbacks(android.content.ComponentCallbacks);
method public abstract void unregisterReceiver(android.content.BroadcastReceiver);
- method public abstract void updateServiceGroup(@NonNull android.content.ServiceConnection, int, int);
+ method public void updateServiceGroup(@NonNull android.content.ServiceConnection, int, int);
field public static final String ACCESSIBILITY_SERVICE = "accessibility";
field public static final String ACCOUNT_SERVICE = "account";
field public static final String ACTIVITY_SERVICE = "activity";
@@ -9839,7 +9841,6 @@ package android.content {
public class ContextWrapper extends android.content.Context {
ctor public ContextWrapper(android.content.Context);
method protected void attachBaseContext(android.content.Context);
- method public boolean bindIsolatedService(android.content.Intent, android.content.ServiceConnection, int, String);
method public boolean bindService(android.content.Intent, android.content.ServiceConnection, int);
method public int checkCallingOrSelfPermission(String);
method public int checkCallingOrSelfUriPermission(android.net.Uri, int);
@@ -9889,7 +9890,6 @@ package android.content {
method public java.io.File getNoBackupFilesDir();
method public java.io.File getObbDir();
method public java.io.File[] getObbDirs();
- method public String getOpPackageName();
method public String getPackageCodePath();
method public android.content.pm.PackageManager getPackageManager();
method public String getPackageName();
@@ -9945,7 +9945,6 @@ package android.content {
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
- method public void updateServiceGroup(android.content.ServiceConnection, int, int);
}
@Deprecated public class CursorLoader extends android.content.AsyncTaskLoader<android.database.Cursor> {
@@ -10221,7 +10220,7 @@ package android.content {
field public static final String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED";
field public static final String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED";
field public static final String ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED";
- field public static final String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
+ field @Deprecated public static final String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
field public static final String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
field public static final String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
field public static final String ACTION_PACKAGES_SUSPENDED = "android.intent.action.PACKAGES_SUSPENDED";
@@ -10349,6 +10348,7 @@ package android.content {
field public static final int EXTRA_DOCK_STATE_LE_DESK = 3; // 0x3
field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0
field public static final String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
+ field public static final String EXTRA_DURATION_MILLIS = "android.intent.extra.DURATION_MILLIS";
field public static final String EXTRA_EMAIL = "android.intent.extra.EMAIL";
field public static final String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
field public static final String EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE";
@@ -11655,6 +11655,7 @@ package android.content.pm {
field public static final String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
field public static final String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
field public static final String FEATURE_SECURELY_REMOVES_USERS = "android.software.securely_removes_users";
+ field public static final String FEATURE_SECURE_LOCK_SCREEN = "android.software.secure_lock_screen";
field public static final String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
field public static final String FEATURE_SENSOR_AMBIENT_TEMPERATURE = "android.hardware.sensor.ambient_temperature";
field public static final String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer";
@@ -21650,7 +21651,7 @@ package android.icu.util {
ctor public JapaneseCalendar(int, int, int, int);
ctor public JapaneseCalendar(int, int, int);
ctor public JapaneseCalendar(int, int, int, int, int, int);
- field public static final int CURRENT_ERA;
+ field @Deprecated public static final int CURRENT_ERA;
field public static final int HEISEI;
field public static final int MEIJI;
field public static final int SHOWA;
@@ -25915,6 +25916,23 @@ package android.media {
method @Nullable public android.media.Session2Command.Result onSessionCommand(@NonNull android.media.MediaSession2, @NonNull android.media.MediaSession2.ControllerInfo, @NonNull android.media.Session2Command, @Nullable android.os.Bundle);
}
+ public abstract class MediaSession2Service extends android.app.Service {
+ ctor public MediaSession2Service();
+ method public final void addSession(@NonNull android.media.MediaSession2);
+ method @NonNull public final java.util.List<android.media.MediaSession2> getSessions();
+ method @CallSuper @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @NonNull public abstract android.media.MediaSession2 onGetPrimarySession();
+ method @Nullable public abstract android.media.MediaSession2Service.MediaNotification onUpdateNotification(@NonNull android.media.MediaSession2);
+ method public final void removeSession(@NonNull android.media.MediaSession2);
+ field public static final String SERVICE_INTERFACE = "android.media.MediaSession2Service";
+ }
+
+ public static class MediaSession2Service.MediaNotification {
+ ctor public MediaSession2Service.MediaNotification(int, @NonNull android.app.Notification);
+ method @NonNull public android.app.Notification getNotification();
+ method public int getNotificationId();
+ }
+
public final class MediaSync {
ctor public MediaSync();
method @NonNull public android.view.Surface createInputSurface();
@@ -26577,12 +26595,9 @@ package android.media.audiofx {
field public static final int SUCCESS = 0; // 0x0
}
- public static final class AudioEffect.Descriptor implements android.os.Parcelable {
+ public static class AudioEffect.Descriptor {
ctor public AudioEffect.Descriptor();
ctor public AudioEffect.Descriptor(String, String, String, String, String);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.media.audiofx.AudioEffect.Descriptor> CREATOR;
field public String connectMode;
field public String implementor;
field public String name;
@@ -27426,18 +27441,26 @@ package android.media.session {
public final class MediaSessionManager {
method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName);
method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, @Nullable android.os.Handler);
+ method public void addOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener);
+ method public void addOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener, @NonNull android.os.Handler);
method @NonNull public java.util.List<android.media.session.MediaController> getActiveSessions(@Nullable android.content.ComponentName);
+ method @NonNull public java.util.List<android.media.Session2Token> getSession2Tokens();
method public boolean isTrustedForMediaControl(@NonNull android.media.session.MediaSessionManager.RemoteUserInfo);
+ method public void notifySession2Created(@NonNull android.media.Session2Token);
method public void removeOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
+ method public void removeOnSession2TokensChangedListener(@NonNull android.media.session.MediaSessionManager.OnSession2TokensChangedListener);
}
public static interface MediaSessionManager.OnActiveSessionsChangedListener {
method public void onActiveSessionsChanged(@Nullable java.util.List<android.media.session.MediaController>);
}
+ public static interface MediaSessionManager.OnSession2TokensChangedListener {
+ method public void onSession2TokensChanged(@NonNull java.util.List<android.media.Session2Token>);
+ }
+
public static final class MediaSessionManager.RemoteUserInfo {
ctor public MediaSessionManager.RemoteUserInfo(@NonNull String, int, int);
- ctor public MediaSessionManager.RemoteUserInfo(String, int, int, android.os.IBinder);
method public String getPackageName();
method public int getPid();
method public int getUid();
@@ -28555,6 +28578,28 @@ package android.net {
field public int serverAddress;
}
+ public final class DnsResolver {
+ method public static android.net.DnsResolver getInstance();
+ method public void query(@Nullable android.net.Network, @NonNull byte[], int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.RawAnswerListener) throws android.system.ErrnoException;
+ method public void query(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.RawAnswerListener) throws android.system.ErrnoException;
+ method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull android.os.Handler, @NonNull android.net.DnsResolver.InetAddressAnswerListener) throws android.system.ErrnoException;
+ field public static final int CLASS_IN = 1; // 0x1
+ field public static final int FLAG_EMPTY = 0; // 0x0
+ field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4
+ field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2
+ field public static final int FLAG_NO_RETRY = 1; // 0x1
+ field public static final int TYPE_A = 1; // 0x1
+ field public static final int TYPE_AAAA = 28; // 0x1c
+ }
+
+ public static interface DnsResolver.InetAddressAnswerListener {
+ method public void onAnswer(@NonNull java.util.List<java.net.InetAddress>);
+ }
+
+ public static interface DnsResolver.RawAnswerListener {
+ method public void onAnswer(@Nullable byte[]);
+ }
+
public class InetAddresses {
method public static boolean isNumericAddress(String);
method public static java.net.InetAddress parseNumericAddress(String);
@@ -28903,24 +28948,24 @@ package android.net {
field public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR;
}
- public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
+ @Deprecated public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
ctor @Deprecated public SSLCertificateSocketFactory(int);
- method public java.net.Socket createSocket(java.net.Socket, String, int, boolean) throws java.io.IOException;
- method public java.net.Socket createSocket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
- method public java.net.Socket createSocket(java.net.InetAddress, int) throws java.io.IOException;
- method public java.net.Socket createSocket(String, int, java.net.InetAddress, int) throws java.io.IOException;
- method public java.net.Socket createSocket(String, int) throws java.io.IOException;
- method public static javax.net.SocketFactory getDefault(int);
- method public static javax.net.ssl.SSLSocketFactory getDefault(int, android.net.SSLSessionCache);
- method public String[] getDefaultCipherSuites();
- method public static javax.net.ssl.SSLSocketFactory getInsecure(int, android.net.SSLSessionCache);
- method public byte[] getNpnSelectedProtocol(java.net.Socket);
- method public String[] getSupportedCipherSuites();
- method public void setHostname(java.net.Socket, String);
- method public void setKeyManagers(javax.net.ssl.KeyManager[]);
- method public void setNpnProtocols(byte[][]);
- method public void setTrustManagers(javax.net.ssl.TrustManager[]);
- method public void setUseSessionTickets(java.net.Socket, boolean);
+ method @Deprecated public java.net.Socket createSocket(java.net.Socket, String, int, boolean) throws java.io.IOException;
+ method @Deprecated public java.net.Socket createSocket(java.net.InetAddress, int, java.net.InetAddress, int) throws java.io.IOException;
+ method @Deprecated public java.net.Socket createSocket(java.net.InetAddress, int) throws java.io.IOException;
+ method @Deprecated public java.net.Socket createSocket(String, int, java.net.InetAddress, int) throws java.io.IOException;
+ method @Deprecated public java.net.Socket createSocket(String, int) throws java.io.IOException;
+ method @Deprecated public static javax.net.SocketFactory getDefault(int);
+ method @Deprecated public static javax.net.ssl.SSLSocketFactory getDefault(int, android.net.SSLSessionCache);
+ method @Deprecated public String[] getDefaultCipherSuites();
+ method @Deprecated public static javax.net.ssl.SSLSocketFactory getInsecure(int, android.net.SSLSessionCache);
+ method @Deprecated public byte[] getNpnSelectedProtocol(java.net.Socket);
+ method @Deprecated public String[] getSupportedCipherSuites();
+ method @Deprecated public void setHostname(java.net.Socket, String);
+ method @Deprecated public void setKeyManagers(javax.net.ssl.KeyManager[]);
+ method @Deprecated public void setNpnProtocols(byte[][]);
+ method @Deprecated public void setTrustManagers(javax.net.ssl.TrustManager[]);
+ method @Deprecated public void setUseSessionTickets(java.net.Socket, boolean);
}
public final class SSLSessionCache {
@@ -29109,6 +29154,8 @@ package android.net {
public class VpnService extends android.app.Service {
ctor public VpnService();
+ method public final boolean isAlwaysOn();
+ method public final boolean isLockdownEnabled();
method public android.os.IBinder onBind(android.content.Intent);
method public void onRevoke();
method public static android.content.Intent prepare(android.content.Context);
@@ -34864,10 +34911,13 @@ package android.os {
method public static final void setThreadPriority(int, int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
method public static final void setThreadPriority(int) throws java.lang.IllegalArgumentException, java.lang.SecurityException;
method @Deprecated public static final boolean supportsProcesses();
+ field public static final int BLUETOOTH_UID = 1002; // 0x3ea
field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
field public static final int INVALID_UID = -1; // 0xffffffff
field public static final int LAST_APPLICATION_UID = 19999; // 0x4e1f
field public static final int PHONE_UID = 1001; // 0x3e9
+ field public static final int ROOT_UID = 0; // 0x0
+ field public static final int SHELL_UID = 2000; // 0x7d0
field public static final int SIGNAL_KILL = 9; // 0x9
field public static final int SIGNAL_QUIT = 3; // 0x3
field public static final int SIGNAL_USR1 = 10; // 0xa
@@ -37882,18 +37932,18 @@ package android.provider {
method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
method @Deprecated public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static String getDocumentId(android.net.Uri);
- method public static android.os.Bundle getDocumentMetadata(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+ method @Nullable public static android.os.Bundle getDocumentMetadata(@NonNull android.content.ContentInterface, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
method @Deprecated public static android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentInterface, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method @Deprecated public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public static String getRootId(android.net.Uri);
method public static String getSearchDocumentsQuery(android.net.Uri);
method public static String getTreeDocumentId(android.net.Uri);
- method public static boolean isChildDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+ method public static boolean isChildDocument(@NonNull android.content.ContentInterface, @NonNull android.net.Uri, @NonNull android.net.Uri) throws java.io.FileNotFoundException;
method @Deprecated public static boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
method public static boolean isDocumentUri(android.content.Context, @Nullable android.net.Uri);
- method public static boolean isRootUri(android.content.Context, @Nullable android.net.Uri);
- method public static boolean isRootsUri(android.content.Context, @Nullable android.net.Uri);
+ method public static boolean isRootUri(@NonNull android.content.Context, @Nullable android.net.Uri);
+ method public static boolean isRootsUri(@NonNull android.content.Context, @Nullable android.net.Uri);
method public static boolean isTreeUri(android.net.Uri);
method public static android.net.Uri moveDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
method @Deprecated public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
@@ -37964,6 +38014,7 @@ package android.provider {
field public static final String COLUMN_FLAGS = "flags";
field public static final String COLUMN_ICON = "icon";
field public static final String COLUMN_MIME_TYPES = "mime_types";
+ field public static final String COLUMN_QUERY_ARGS = "query_args";
field public static final String COLUMN_ROOT_ID = "root_id";
field public static final String COLUMN_SUMMARY = "summary";
field public static final String COLUMN_TITLE = "title";
@@ -37986,7 +38037,7 @@ package android.provider {
method public void deleteDocument(String) throws java.io.FileNotFoundException;
method public void ejectRoot(String);
method public android.provider.DocumentsContract.Path findDocumentPath(@Nullable String, String) throws java.io.FileNotFoundException;
- method @Nullable public android.os.Bundle getDocumentMetadata(String) throws java.io.FileNotFoundException;
+ method @Nullable public android.os.Bundle getDocumentMetadata(@NonNull String) throws java.io.FileNotFoundException;
method public String[] getDocumentStreamTypes(String, String);
method public String getDocumentType(String) throws java.io.FileNotFoundException;
method public final String getType(android.net.Uri);
@@ -38011,7 +38062,7 @@ package android.provider {
method public android.database.Cursor queryRecentDocuments(String, String[], @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public abstract android.database.Cursor queryRoots(String[]) throws java.io.FileNotFoundException;
method public android.database.Cursor querySearchDocuments(String, String, String[]) throws java.io.FileNotFoundException;
- method public android.database.Cursor querySearchDocuments(String, String[], android.os.Bundle) throws java.io.FileNotFoundException;
+ method public android.database.Cursor querySearchDocuments(@NonNull String, @Nullable String[], @NonNull android.os.Bundle) throws java.io.FileNotFoundException;
method public void removeDocument(String, String) throws java.io.FileNotFoundException;
method public String renameDocument(String, String) throws java.io.FileNotFoundException;
method public final void revokeDocumentPermission(String);
@@ -43584,6 +43635,7 @@ package android.telecom {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccountHandle getSimCallManager();
method public String getSystemDialerPackage();
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telecom.PhoneAccountHandle getUserSelectedOutgoingPhoneAccount();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle);
@@ -45011,7 +45063,9 @@ package android.telephony.data {
field public static final int PROTOCOL_IP = 0; // 0x0
field public static final int PROTOCOL_IPV4V6 = 2; // 0x2
field public static final int PROTOCOL_IPV6 = 1; // 0x1
+ field public static final int PROTOCOL_NON_IP = 4; // 0x4
field public static final int PROTOCOL_PPP = 3; // 0x3
+ field public static final int PROTOCOL_UNSTRUCTURED = 5; // 0x5
field public static final int TYPE_CBS = 128; // 0x80
field public static final int TYPE_DEFAULT = 17; // 0x11
field public static final int TYPE_DUN = 8; // 0x8
@@ -49660,6 +49714,7 @@ package android.view {
}
public class Surface implements android.os.Parcelable {
+ ctor public Surface(android.view.SurfaceControl);
ctor public Surface(android.graphics.SurfaceTexture);
method public int describeContents();
method public boolean isValid();
@@ -49682,6 +49737,38 @@ package android.view {
ctor public Surface.OutOfResourcesException(String);
}
+ public final class SurfaceControl implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean isValid();
+ method public void readFromParcel(android.os.Parcel);
+ method public void release();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.view.SurfaceControl> CREATOR;
+ }
+
+ public static class SurfaceControl.Builder {
+ ctor public SurfaceControl.Builder();
+ method public android.view.SurfaceControl build();
+ method public android.view.SurfaceControl.Builder setBufferSize(@IntRange(from=0) int, @IntRange(from=0) int);
+ method @NonNull public android.view.SurfaceControl.Builder setFormat(int);
+ method public android.view.SurfaceControl.Builder setName(String);
+ method @NonNull public android.view.SurfaceControl.Builder setOpaque(boolean);
+ method @NonNull public android.view.SurfaceControl.Builder setParent(@Nullable android.view.SurfaceControl);
+ }
+
+ public static class SurfaceControl.Transaction implements java.io.Closeable {
+ ctor public SurfaceControl.Transaction();
+ method public void apply();
+ method public void close();
+ method @NonNull public android.view.SurfaceControl.Transaction merge(@NonNull android.view.SurfaceControl.Transaction);
+ method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl);
+ method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float);
+ method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
+ method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
+ }
+
public interface SurfaceHolder {
method public void addCallback(android.view.SurfaceHolder.Callback);
method public android.view.Surface getSurface();
@@ -49726,6 +49813,7 @@ package android.view {
ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int, int);
method public boolean gatherTransparentRegion(android.graphics.Region);
method public android.view.SurfaceHolder getHolder();
+ method public android.view.SurfaceControl getSurfaceControl();
method public void setSecure(boolean);
method public void setZOrderMediaOverlay(boolean);
method public void setZOrderOnTop(boolean);
diff --git a/api/removed.txt b/api/removed.txt
index 2c567e05ca0b..e23222719ea9 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -325,7 +325,7 @@ package android.net {
@IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging {
}
- public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
+ @Deprecated public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
method @Deprecated public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 2088eaf32d0a..cdd2d804844b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1136,19 +1136,46 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect();
method public boolean isBleScanAlwaysAvailable();
method public boolean isLeEnabled();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean registerMetadataListener(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothAdapter.MetadataListener, android.os.Handler);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean unregisterMetadataListener(android.bluetooth.BluetoothDevice);
field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
field public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
}
+ public abstract class BluetoothAdapter.MetadataListener {
+ ctor public BluetoothAdapter.MetadataListener();
+ method public void onMetadataChanged(android.bluetooth.BluetoothDevice, int, String);
+ }
+
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public String getMetadata(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, String);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
field public static final int ACCESS_ALLOWED = 1; // 0x1
field public static final int ACCESS_REJECTED = 2; // 0x2
field public static final int ACCESS_UNKNOWN = 0; // 0x0
+ field public static final int METADATA_COMPANION_APP = 4; // 0x4
+ field public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16; // 0x10
+ field public static final int METADATA_HARDWARE_VERSION = 3; // 0x3
+ field public static final int METADATA_IS_UNTHETHERED_HEADSET = 6; // 0x6
+ field public static final int METADATA_MAIN_ICON = 5; // 0x5
+ field public static final int METADATA_MANUFACTURER_NAME = 0; // 0x0
+ field public static final int METADATA_MAX_LENGTH = 2048; // 0x800
+ field public static final int METADATA_MODEL_NAME = 1; // 0x1
+ field public static final int METADATA_SOFTWARE_VERSION = 2; // 0x2
+ field public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12; // 0xc
+ field public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15; // 0xf
+ field public static final int METADATA_UNTHETHERED_CASE_ICON = 9; // 0x9
+ field public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10; // 0xa
+ field public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13; // 0xd
+ field public static final int METADATA_UNTHETHERED_LEFT_ICON = 7; // 0x7
+ field public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11; // 0xb
+ field public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14; // 0xe
+ field public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8; // 0x8
}
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -1322,6 +1349,7 @@ package android.content.om {
public class OverlayManager {
method public java.util.List<android.content.om.OverlayInfo> getOverlayInfosForTarget(@Nullable String, int);
+ method public boolean setEnabled(@Nullable String, boolean, int);
method public boolean setEnabledExclusiveInCategory(@Nullable String, int);
}
@@ -1776,8 +1804,13 @@ package android.hardware.display {
}
public final class ColorDisplayManager {
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public int getTransformCapabilities();
method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setAppSaturationLevel(@NonNull String, @IntRange(from=0, to=100) int);
method @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS) public boolean setSaturationLevel(@IntRange(from=0, to=100) int);
+ field public static final int CAPABILITY_HARDWARE_ACCELERATION_GLOBAL = 2; // 0x2
+ field public static final int CAPABILITY_HARDWARE_ACCELERATION_PER_APP = 4; // 0x4
+ field public static final int CAPABILITY_NONE = 0; // 0x0
+ field public static final int CAPABILITY_PROTECTED_CONTENT = 1; // 0x1
}
public final class DisplayManager {
@@ -3204,12 +3237,14 @@ package android.location {
method public int getQuality();
method public float getSmallestDisplacement();
method public android.os.WorkSource getWorkSource();
+ method public boolean isLocationSettingsIgnored();
method public boolean isLowPowerMode();
method public android.location.LocationRequest setExpireAt(long);
method public android.location.LocationRequest setExpireIn(long);
method public android.location.LocationRequest setFastestInterval(long);
method public void setHideFromAppOps(boolean);
method public android.location.LocationRequest setInterval(long);
+ method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest setLocationSettingsIgnored(boolean);
method public android.location.LocationRequest setLowPowerMode(boolean);
method public android.location.LocationRequest setNumUpdates(int);
method public android.location.LocationRequest setProvider(String);
@@ -3530,6 +3565,76 @@ package android.media.session {
method public void unregisterCallback(@NonNull android.media.session.ControllerCallbackLink);
}
+ public abstract static class MediaSession.Callback {
+ method public void onSetMediaButtonEventDelegate(@NonNull android.media.session.MediaSessionEngine.MediaButtonEventDelegate);
+ }
+
+ public final class MediaSessionEngine implements java.lang.AutoCloseable {
+ ctor public MediaSessionEngine(@NonNull android.content.Context, @NonNull android.media.session.SessionLink, @NonNull android.media.session.SessionCallbackLink, @NonNull android.media.session.MediaSessionEngine.CallbackStub, int);
+ method public void close();
+ method public String getCallingPackage();
+ method @NonNull public android.media.session.MediaController getController();
+ method @NonNull public android.media.session.MediaSessionManager.RemoteUserInfo getCurrentControllerInfo();
+ method @NonNull public android.media.session.MediaSession.Token getSessionToken();
+ method public boolean isActive();
+ method public static boolean isActiveState(int);
+ method public void sendSessionEvent(@NonNull String, @Nullable android.os.Bundle);
+ method public void setActive(boolean);
+ method public void setCallback(@Nullable android.media.session.MediaSession.Callback);
+ method public void setCallback(@Nullable android.media.session.MediaSession.Callback, @NonNull android.os.Handler);
+ method public void setExtras(@Nullable android.os.Bundle);
+ method public void setFlags(int);
+ method public void setMediaButtonReceiver(@Nullable android.app.PendingIntent);
+ method public void setMetadata(@Nullable android.media.MediaMetadata);
+ method public void setPlaybackState(@Nullable android.media.session.PlaybackState);
+ method public void setPlaybackToLocal(android.media.AudioAttributes);
+ method public void setPlaybackToRemote(@NonNull android.media.VolumeProvider);
+ method public void setQueue(@Nullable java.util.List<android.media.session.MediaSession.QueueItem>);
+ method public void setQueueTitle(@Nullable CharSequence);
+ method public void setRatingType(int);
+ method public void setSessionActivity(@Nullable android.app.PendingIntent);
+ }
+
+ public static final class MediaSessionEngine.CallbackStub {
+ ctor public MediaSessionEngine.CallbackStub();
+ method public void onAdjustVolume(String, int, int, android.media.session.ControllerCallbackLink, int);
+ method public void onCommand(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle, android.os.ResultReceiver);
+ method public void onCustomAction(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle);
+ method public void onFastForward(String, int, int, android.media.session.ControllerCallbackLink);
+ method public void onMediaButton(String, int, int, android.content.Intent, int, android.os.ResultReceiver);
+ method public void onMediaButtonFromController(String, int, int, android.media.session.ControllerCallbackLink, android.content.Intent);
+ method public void onNext(String, int, int, android.media.session.ControllerCallbackLink);
+ method public void onPause(String, int, int, android.media.session.ControllerCallbackLink);
+ method public void onPlay(String, int, int, android.media.session.ControllerCallbackLink);
+ method public void onPlayFromMediaId(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle);
+ method public void onPlayFromSearch(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle);
+ method public void onPlayFromUri(String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
+ method public void onPrepare(String, int, int, android.media.session.ControllerCallbackLink);
+ method public void onPrepareFromMediaId(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle);
+ method public void onPrepareFromSearch(String, int, int, android.media.session.ControllerCallbackLink, String, android.os.Bundle);
+ method public void onPrepareFromUri(String, int, int, android.media.session.ControllerCallbackLink, android.net.Uri, android.os.Bundle);
+ method public void onPrevious(String, int, int, android.media.session.ControllerCallbackLink);
+ method public void onRate(String, int, int, android.media.session.ControllerCallbackLink, android.media.Rating);
+ method public void onRewind(String, int, int, android.media.session.ControllerCallbackLink);
+ method public void onSeekTo(String, int, int, android.media.session.ControllerCallbackLink, long);
+ method public void onSetVolumeTo(String, int, int, android.media.session.ControllerCallbackLink, int);
+ method public void onSkipToTrack(String, int, int, android.media.session.ControllerCallbackLink, long);
+ method public void onStop(String, int, int, android.media.session.ControllerCallbackLink);
+ }
+
+ public static interface MediaSessionEngine.MediaButtonEventDelegate {
+ method public boolean onMediaButtonIntent(android.content.Intent);
+ }
+
+ public static final class MediaSessionEngine.QueueItem {
+ ctor public MediaSessionEngine.QueueItem(android.media.MediaDescription, long);
+ ctor public MediaSessionEngine.QueueItem(android.os.Parcel);
+ method public android.media.MediaDescription getDescription();
+ method public long getQueueId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int UNKNOWN_ID = -1; // 0xffffffff
+ }
+
public final class MediaSessionManager {
method @RequiresPermission(android.Manifest.permission.SET_MEDIA_KEY_LISTENER) public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, @Nullable android.os.Handler);
method @RequiresPermission(android.Manifest.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER) public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, @Nullable android.os.Handler);
@@ -3895,12 +4000,15 @@ package android.metrics {
package android.net {
public class CaptivePortal implements android.os.Parcelable {
+ ctor public CaptivePortal(android.os.IBinder);
+ method public void useNetwork();
field public static final int APP_RETURN_DISMISSED = 0; // 0x0
field public static final int APP_RETURN_UNWANTED = 1; // 0x1
field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
}
public class ConnectivityManager {
+ method public boolean getAvoidBadWifi();
method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
@@ -3962,14 +4070,30 @@ package android.net {
public final class LinkProperties implements android.os.Parcelable {
ctor public LinkProperties();
+ method public boolean addDnsServer(java.net.InetAddress);
method public boolean addRoute(android.net.RouteInfo);
method public void clear();
+ method public String getTcpBufferSizes();
+ method public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
+ method public boolean hasGlobalIPv6Address();
+ method public boolean hasIPv4Address();
+ method public boolean hasIPv6DefaultRoute();
+ method public boolean isIPv4Provisioned();
+ method public boolean isIPv6Provisioned();
+ method public boolean isProvisioned();
+ method public boolean isReachable(java.net.InetAddress);
+ method public boolean removeDnsServer(java.net.InetAddress);
+ method public boolean removeRoute(android.net.RouteInfo);
method public void setDnsServers(java.util.Collection<java.net.InetAddress>);
method public void setDomains(String);
method public void setHttpProxy(android.net.ProxyInfo);
method public void setInterfaceName(String);
method public void setLinkAddresses(java.util.Collection<android.net.LinkAddress>);
method public void setMtu(int);
+ method public void setPrivateDnsServerName(@Nullable String);
+ method public void setTcpBufferSizes(String);
+ method public void setUsePrivateDns(boolean);
+ method public void setValidatedPrivateDnsServers(java.util.Collection<java.net.InetAddress>);
}
public class Network implements android.os.Parcelable {
@@ -3978,6 +4102,8 @@ package android.net {
public final class NetworkCapabilities implements android.os.Parcelable {
method public int getSignalStrength();
+ method public int[] getTransportTypes();
+ method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities);
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
}
@@ -4153,6 +4279,7 @@ package android.net.metrics {
public class IpConnectivityLog {
method public boolean log(long, android.net.metrics.IpConnectivityLog.Event);
method public boolean log(String, android.net.metrics.IpConnectivityLog.Event);
+ method public boolean log(android.net.Network, int[], android.net.metrics.IpConnectivityLog.Event);
method public boolean log(int, int[], android.net.metrics.IpConnectivityLog.Event);
method public boolean log(android.net.metrics.IpConnectivityLog.Event);
}
@@ -4220,6 +4347,17 @@ package android.net.metrics {
}
+package android.net.util {
+
+ public class SocketUtils {
+ method public static void bindSocketToInterface(java.io.FileDescriptor, String) throws android.system.ErrnoException;
+ method public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
+ method public static java.net.SocketAddress makePacketSocketAddress(short, int);
+ method public static java.net.SocketAddress makePacketSocketAddress(int, byte[]);
+ }
+
+}
+
package android.net.wifi {
public abstract class EasyConnectStatusCallback {
@@ -4461,7 +4599,6 @@ package android.net.wifi {
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(android.net.wifi.WifiConfiguration, 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, android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void disable(int, android.net.wifi.WifiManager.ActionListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void disableEphemeralNetwork(String);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void forget(int, android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>);
@@ -4695,7 +4832,7 @@ package android.net.wifi.hotspot2 {
method public abstract void onProvisioningStatus(int);
field public static final int OSU_FAILURE_ADD_PASSPOINT_CONFIGURATION = 22; // 0x16
field public static final int OSU_FAILURE_AP_CONNECTION = 1; // 0x1
- field public static final int OSU_FAILURE_INVALID_SERVER_URL = 8; // 0x8
+ field public static final int OSU_FAILURE_INVALID_URL_FORMAT_FOR_OSU = 8; // 0x8
field public static final int OSU_FAILURE_NO_AAA_SERVER_TRUST_ROOT_NODE = 17; // 0x11
field public static final int OSU_FAILURE_NO_AAA_TRUST_ROOT_CERTIFICATE = 21; // 0x15
field public static final int OSU_FAILURE_NO_OSU_ACTIVITY_FOUND = 14; // 0xe
@@ -5053,6 +5190,12 @@ package android.os {
method public void onResult(android.os.Bundle);
}
+ public class ServiceSpecificException extends java.lang.RuntimeException {
+ ctor public ServiceSpecificException(int, String);
+ ctor public ServiceSpecificException(int);
+ field public final int errorCode;
+ }
+
public final class StatsDimensionsValue implements android.os.Parcelable {
method public int describeContents();
method public boolean getBooleanValue();
@@ -5406,6 +5549,7 @@ package android.provider {
field public static final String NAMESPACE_GAME_DRIVER = "game_driver";
field public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
+ field public static final String NAMESPACE_NOTIFICATION_ASSISTANT = "notification_assistant";
}
public static interface DeviceConfig.OnPropertyChangedListener {
@@ -6047,12 +6191,15 @@ package android.service.euicc {
method public abstract int onSwitchToSubscription(int, @Nullable String, boolean);
method public abstract int onUpdateSubscriptionNickname(int, String, String);
field public static final String ACTION_BIND_CARRIER_PROVISIONING_SERVICE = "android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE";
+ field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.service.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
field public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS = "android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+ field public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED = "android.service.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
field @Deprecated public static final String ACTION_RESOLVE_CONFIRMATION_CODE = "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
field public static final String ACTION_RESOLVE_DEACTIVATE_SIM = "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
field public static final String ACTION_RESOLVE_NO_PRIVILEGES = "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
field public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS = "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";
+ field public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED = "android.service.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
field public static final String CATEGORY_EUICC_UI = "android.service.euicc.category.EUICC_UI";
field public static final String EUICC_SERVICE_INTERFACE = "android.service.euicc.EuiccService";
field public static final String EXTRA_RESOLUTION_ALLOW_POLICY_RULES = "android.service.euicc.extra.RESOLUTION_ALLOW_POLICY_RULES";
@@ -6630,6 +6777,7 @@ package android.telecom {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.WRITE_SECURE_SETTINGS}) public boolean setDefaultDialer(String);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(android.telecom.PhoneAccountHandle);
field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
@@ -6714,42 +6862,211 @@ package android.telephony {
}
public final class DataFailCause {
+ field public static final int ACCESS_ATTEMPT_ALREADY_IN_PROGRESS = 2219; // 0x8ab
+ field public static final int ACCESS_BLOCK = 2087; // 0x827
+ field public static final int ACCESS_BLOCK_ALL = 2088; // 0x828
+ field public static final int ACCESS_CLASS_DSAC_REJECTION = 2108; // 0x83c
+ field public static final int ACCESS_CONTROL_LIST_CHECK_FAILURE = 2128; // 0x850
+ field public static final int ACTIVATION_REJECTED_BCM_VIOLATION = 48; // 0x30
field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e
field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f
field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41
+ field public static final int APN_DISABLED = 2045; // 0x7fd
+ field public static final int APN_DISALLOWED_ON_ROAMING = 2059; // 0x80b
+ field public static final int APN_MISMATCH = 2054; // 0x806
+ field public static final int APN_PARAMETERS_CHANGED = 2060; // 0x80c
+ field public static final int APN_PENDING_HANDOVER = 2041; // 0x7f9
field public static final int APN_TYPE_CONFLICT = 112; // 0x70
field public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 122; // 0x7a
+ field public static final int BEARER_HANDLING_NOT_SUPPORTED = 60; // 0x3c
+ field public static final int CALL_DISALLOWED_IN_ROAMING = 2068; // 0x814
+ field public static final int CALL_PREEMPT_BY_EMERGENCY_APN = 127; // 0x7f
+ field public static final int CANNOT_ENCODE_OTA_MESSAGE = 2159; // 0x86f
+ field public static final int CDMA_ALERT_STOP = 2077; // 0x81d
+ field public static final int CDMA_INCOMING_CALL = 2076; // 0x81c
+ field public static final int CDMA_INTERCEPT = 2073; // 0x819
+ field public static final int CDMA_LOCK = 2072; // 0x818
+ field public static final int CDMA_RELEASE_DUE_TO_SO_REJECTION = 2075; // 0x81b
+ field public static final int CDMA_REORDER = 2074; // 0x81a
+ field public static final int CDMA_RETRY_ORDER = 2086; // 0x826
+ field public static final int CHANNEL_ACQUISITION_FAILURE = 2078; // 0x81e
+ field public static final int CLOSE_IN_PROGRESS = 2030; // 0x7ee
+ field public static final int COLLISION_WITH_NETWORK_INITIATED_REQUEST = 56; // 0x38
field public static final int COMPANION_IFACE_IN_USE = 118; // 0x76
+ field public static final int CONCURRENT_SERVICES_INCOMPATIBLE = 2083; // 0x823
+ field public static final int CONCURRENT_SERVICES_NOT_ALLOWED = 2091; // 0x82b
+ field public static final int CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION = 2080; // 0x820
field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
+ field public static final int CONGESTION = 2106; // 0x83a
+ field public static final int CONNECTION_RELEASED = 2113; // 0x841
+ field public static final int CS_DOMAIN_NOT_AVAILABLE = 2181; // 0x885
+ field public static final int CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED = 2188; // 0x88c
+ field public static final int DATA_PLAN_EXPIRED = 2198; // 0x896
+ field public static final int DATA_ROAMING_SETTINGS_DISABLED = 2064; // 0x810
+ field public static final int DATA_SETTINGS_DISABLED = 2063; // 0x80f
+ field public static final int DBM_OR_SMS_IN_PROGRESS = 2211; // 0x8a3
+ field public static final int DDS_SWITCHED = 2065; // 0x811
+ field public static final int DDS_SWITCH_IN_PROGRESS = 2067; // 0x813
+ field public static final int DRB_RELEASED_BY_RRC = 2112; // 0x840
+ field public static final int DS_EXPLICIT_DEACTIVATION = 2125; // 0x84d
+ field public static final int DUAL_SWITCH = 2227; // 0x8b3
+ field public static final int DUN_CALL_DISALLOWED = 2056; // 0x808
+ field public static final int DUPLICATE_BEARER_ID = 2118; // 0x846
+ field public static final int EHRPD_TO_HRPD_FALLBACK = 2049; // 0x801
+ field public static final int EMBMS_NOT_ENABLED = 2193; // 0x891
+ field public static final int EMBMS_REGULAR_DEACTIVATION = 2195; // 0x893
field public static final int EMERGENCY_IFACE_ONLY = 116; // 0x74
+ field public static final int EMERGENCY_MODE = 2221; // 0x8ad
field public static final int EMM_ACCESS_BARRED = 115; // 0x73
field public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 121; // 0x79
+ field public static final int EMM_ATTACH_FAILED = 2115; // 0x843
+ field public static final int EMM_ATTACH_STARTED = 2116; // 0x844
+ field public static final int EMM_DETACHED = 2114; // 0x842
+ field public static final int EMM_T3417_EXPIRED = 2130; // 0x852
+ field public static final int EMM_T3417_EXT_EXPIRED = 2131; // 0x853
+ field public static final int EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED = 2178; // 0x882
+ field public static final int EPS_SERVICES_NOT_ALLOWED_IN_PLMN = 2179; // 0x883
field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
+ field public static final int ESM_BAD_OTA_MESSAGE = 2122; // 0x84a
+ field public static final int ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK = 2120; // 0x848
+ field public static final int ESM_COLLISION_SCENARIOS = 2119; // 0x847
+ field public static final int ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT = 2124; // 0x84c
+ field public static final int ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL = 2123; // 0x84b
+ field public static final int ESM_FAILURE = 2182; // 0x886
field public static final int ESM_INFO_NOT_RECEIVED = 53; // 0x35
+ field public static final int ESM_LOCAL_CAUSE_NONE = 2126; // 0x84e
+ field public static final int ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER = 2121; // 0x849
+ field public static final int ESM_PROCEDURE_TIME_OUT = 2155; // 0x86b
+ field public static final int ESM_UNKNOWN_EPS_BEARER_CONTEXT = 2111; // 0x83f
+ field public static final int EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE = 2201; // 0x899
+ field public static final int EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY = 2200; // 0x898
+ field public static final int EVDO_HDR_CHANGED = 2202; // 0x89a
+ field public static final int EVDO_HDR_CONNECTION_SETUP_TIMEOUT = 2206; // 0x89e
+ field public static final int EVDO_HDR_EXITED = 2203; // 0x89b
+ field public static final int EVDO_HDR_NO_SESSION = 2204; // 0x89c
+ field public static final int EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL = 2205; // 0x89d
+ field public static final int FADE = 2217; // 0x8a9
+ field public static final int FAILED_TO_ACQUIRE_COLOCATED_HDR = 2207; // 0x89f
field public static final int FEATURE_NOT_SUPP = 40; // 0x28
field public static final int FILTER_SEMANTIC_ERROR = 44; // 0x2c
field public static final int FILTER_SYTAX_ERROR = 45; // 0x2d
+ field public static final int FORBIDDEN_APN_NAME = 2066; // 0x812
field public static final int GPRS_REGISTRATION_FAIL = -2; // 0xfffffffe
+ field public static final int GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED = 2097; // 0x831
+ field public static final int GPRS_SERVICES_NOT_ALLOWED = 2098; // 0x832
+ field public static final int GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN = 2103; // 0x837
+ field public static final int HANDOFF_PREFERENCE_CHANGED = 2251; // 0x8cb
+ field public static final int HDR_ACCESS_FAILURE = 2213; // 0x8a5
+ field public static final int HDR_FADE = 2212; // 0x8a4
+ field public static final int HDR_NO_LOCK_GRANTED = 2210; // 0x8a2
field public static final int IFACE_AND_POL_FAMILY_MISMATCH = 120; // 0x78
field public static final int IFACE_MISMATCH = 117; // 0x75
+ field public static final int ILLEGAL_ME = 2096; // 0x830
+ field public static final int ILLEGAL_MS = 2095; // 0x82f
+ field public static final int IMEI_NOT_ACCEPTED = 2177; // 0x881
+ field public static final int IMPLICITLY_DETACHED = 2100; // 0x834
+ field public static final int IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER = 2176; // 0x880
+ field public static final int INCOMING_CALL_REJECTED = 2092; // 0x82c
field public static final int INSUFFICIENT_RESOURCES = 26; // 0x1a
+ field public static final int INTERFACE_IN_USE = 2058; // 0x80a
field public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 114; // 0x72
+ field public static final int INTERNAL_EPC_NONEPC_TRANSITION = 2057; // 0x809
+ field public static final int INVALID_CONNECTION_ID = 2156; // 0x86c
+ field public static final int INVALID_DNS_ADDR = 123; // 0x7b
+ field public static final int INVALID_EMM_STATE = 2190; // 0x88e
field public static final int INVALID_MANDATORY_INFO = 96; // 0x60
+ field public static final int INVALID_MODE = 2223; // 0x8af
field public static final int INVALID_PCSCF_ADDR = 113; // 0x71
+ field public static final int INVALID_PCSCF_OR_DNS_ADDRESS = 124; // 0x7c
+ field public static final int INVALID_PRIMARY_NSAPI = 2158; // 0x86e
+ field public static final int INVALID_SIM_STATE = 2224; // 0x8b0
field public static final int INVALID_TRANSACTION_ID = 81; // 0x51
+ field public static final int IPV6_ADDRESS_TRANSFER_FAILED = 2047; // 0x7ff
+ field public static final int IPV6_PREFIX_UNAVAILABLE = 2250; // 0x8ca
field public static final int IP_ADDRESS_MISMATCH = 119; // 0x77
+ field public static final int IP_VERSION_MISMATCH = 2055; // 0x807
+ field public static final int IRAT_HANDOVER_FAILED = 2194; // 0x892
+ field public static final int IS707B_MAX_ACCESS_PROBES = 2089; // 0x829
+ field public static final int LIMITED_TO_IPV4 = 2234; // 0x8ba
+ field public static final int LIMITED_TO_IPV6 = 2235; // 0x8bb
field public static final int LLC_SNDCP = 25; // 0x19
+ field public static final int LOCAL_END = 2215; // 0x8a7
+ field public static final int LOCATION_AREA_NOT_ALLOWED = 2102; // 0x836
field public static final int LOST_CONNECTION = 65540; // 0x10004
+ field public static final int LOWER_LAYER_REGISTRATION_FAILURE = 2197; // 0x895
+ field public static final int LOW_POWER_MODE_OR_POWERING_DOWN = 2044; // 0x7fc
+ field public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 2117; // 0x845
+ field public static final int LTE_THROTTLING_NOT_REQUIRED = 2127; // 0x84f
+ field public static final int MAC_FAILURE = 2183; // 0x887
+ field public static final int MAXIMIUM_NSAPIS_EXCEEDED = 2157; // 0x86d
+ field public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 2166; // 0x876
+ field public static final int MAX_ACCESS_PROBE = 2079; // 0x81f
+ field public static final int MAX_IPV4_CONNECTIONS = 2052; // 0x804
+ field public static final int MAX_IPV6_CONNECTIONS = 2053; // 0x805
+ field public static final int MAX_PPP_INACTIVITY_TIMER_EXPIRED = 2046; // 0x7fe
field public static final int MESSAGE_INCORRECT_SEMANTIC = 95; // 0x5f
field public static final int MESSAGE_TYPE_UNSUPPORTED = 97; // 0x61
+ field public static final int MIP_CONFIG_FAILURE = 2050; // 0x802
+ field public static final int MIP_FA_ADMIN_PROHIBITED = 2001; // 0x7d1
+ field public static final int MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED = 2012; // 0x7dc
+ field public static final int MIP_FA_ENCAPSULATION_UNAVAILABLE = 2008; // 0x7d8
+ field public static final int MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE = 2004; // 0x7d4
+ field public static final int MIP_FA_INSUFFICIENT_RESOURCES = 2002; // 0x7d2
+ field public static final int MIP_FA_MALFORMED_REPLY = 2007; // 0x7d7
+ field public static final int MIP_FA_MALFORMED_REQUEST = 2006; // 0x7d6
+ field public static final int MIP_FA_MISSING_CHALLENGE = 2017; // 0x7e1
+ field public static final int MIP_FA_MISSING_HOME_ADDRESS = 2015; // 0x7df
+ field public static final int MIP_FA_MISSING_HOME_AGENT = 2014; // 0x7de
+ field public static final int MIP_FA_MISSING_NAI = 2013; // 0x7dd
+ field public static final int MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE = 2003; // 0x7d3
+ field public static final int MIP_FA_REASON_UNSPECIFIED = 2000; // 0x7d0
+ field public static final int MIP_FA_REQUESTED_LIFETIME_TOO_LONG = 2005; // 0x7d5
+ field public static final int MIP_FA_REVERSE_TUNNEL_IS_MANDATORY = 2011; // 0x7db
+ field public static final int MIP_FA_REVERSE_TUNNEL_UNAVAILABLE = 2010; // 0x7da
+ field public static final int MIP_FA_STALE_CHALLENGE = 2018; // 0x7e2
+ field public static final int MIP_FA_UNKNOWN_CHALLENGE = 2016; // 0x7e0
+ field public static final int MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE = 2009; // 0x7d9
+ field public static final int MIP_HA_ADMIN_PROHIBITED = 2020; // 0x7e4
+ field public static final int MIP_HA_ENCAPSULATION_UNAVAILABLE = 2029; // 0x7ed
+ field public static final int MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE = 2023; // 0x7e7
+ field public static final int MIP_HA_INSUFFICIENT_RESOURCES = 2021; // 0x7e5
+ field public static final int MIP_HA_MALFORMED_REQUEST = 2025; // 0x7e9
+ field public static final int MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE = 2022; // 0x7e6
+ field public static final int MIP_HA_REASON_UNSPECIFIED = 2019; // 0x7e3
+ field public static final int MIP_HA_REGISTRATION_ID_MISMATCH = 2024; // 0x7e8
+ field public static final int MIP_HA_REVERSE_TUNNEL_IS_MANDATORY = 2028; // 0x7ec
+ field public static final int MIP_HA_REVERSE_TUNNEL_UNAVAILABLE = 2027; // 0x7eb
+ field public static final int MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS = 2026; // 0x7ea
field public static final int MISSING_UNKNOWN_APN = 27; // 0x1b
+ field public static final int MODEM_APP_PREEMPTED = 2032; // 0x7f0
+ field public static final int MODEM_RESTART = 2037; // 0x7f5
+ field public static final int MSC_TEMPORARILY_NOT_REACHABLE = 2180; // 0x884
field public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 101; // 0x65
field public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 98; // 0x62
+ field public static final int MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK = 2099; // 0x833
+ field public static final int MULTIPLE_PDP_CALL_NOT_ALLOWED = 2192; // 0x890
field public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 55; // 0x37
+ field public static final int NAS_LAYER_FAILURE = 2191; // 0x88f
+ field public static final int NAS_REQUEST_REJECTED_BY_NETWORK = 2167; // 0x877
field public static final int NAS_SIGNALLING = 14; // 0xe
field public static final int NETWORK_FAILURE = 38; // 0x26
+ field public static final int NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH = 2154; // 0x86a
+ field public static final int NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH = 2153; // 0x869
+ field public static final int NETWORK_INITIATED_TERMINATION = 2031; // 0x7ef
field public static final int NONE = 0; // 0x0
+ field public static final int NON_IP_NOT_SUPPORTED = 2069; // 0x815
+ field public static final int NORMAL_RELEASE = 2218; // 0x8aa
+ field public static final int NO_CDMA_SERVICE = 2084; // 0x824
+ field public static final int NO_COLLOCATED_HDR = 2225; // 0x8b1
+ field public static final int NO_EPS_BEARER_CONTEXT_ACTIVATED = 2189; // 0x88d
+ field public static final int NO_GPRS_CONTEXT = 2094; // 0x82e
+ field public static final int NO_HYBRID_HDR_SERVICE = 2209; // 0x8a1
+ field public static final int NO_PDP_CONTEXT_ACTIVATED = 2107; // 0x83b
+ field public static final int NO_RESPONSE_FROM_BASE_STATION = 2081; // 0x821
+ field public static final int NO_SERVICE = 2216; // 0x8a8
+ field public static final int NO_SERVICE_ON_GATEWAY = 2093; // 0x82d
field public static final int NSAPI_IN_USE = 35; // 0x23
+ field public static final int NULL_APN_DISALLOWED = 2061; // 0x80d
field public static final int OEM_DCFAILCAUSE_1 = 4097; // 0x1001
field public static final int OEM_DCFAILCAUSE_10 = 4106; // 0x100a
field public static final int OEM_DCFAILCAUSE_11 = 4107; // 0x100b
@@ -6765,33 +7082,126 @@ package android.telephony {
field public static final int OEM_DCFAILCAUSE_7 = 4103; // 0x1007
field public static final int OEM_DCFAILCAUSE_8 = 4104; // 0x1008
field public static final int OEM_DCFAILCAUSE_9 = 4105; // 0x1009
+ field public static final int ONLY_IPV4V6_ALLOWED = 57; // 0x39
field public static final int ONLY_IPV4_ALLOWED = 50; // 0x32
field public static final int ONLY_IPV6_ALLOWED = 51; // 0x33
+ field public static final int ONLY_NON_IP_ALLOWED = 58; // 0x3a
field public static final int ONLY_SINGLE_BEARER_ALLOWED = 52; // 0x34
field public static final int OPERATOR_BARRED = 8; // 0x8
+ field public static final int OTASP_COMMIT_IN_PROGRESS = 2208; // 0x8a0
field public static final int PDN_CONN_DOES_NOT_EXIST = 54; // 0x36
+ field public static final int PDN_INACTIVITY_TIMER_EXPIRED = 2051; // 0x803
+ field public static final int PDN_IPV4_CALL_DISALLOWED = 2033; // 0x7f1
+ field public static final int PDN_IPV4_CALL_THROTTLED = 2034; // 0x7f2
+ field public static final int PDN_IPV6_CALL_DISALLOWED = 2035; // 0x7f3
+ field public static final int PDN_IPV6_CALL_THROTTLED = 2036; // 0x7f4
+ field public static final int PDN_NON_IP_CALL_DISALLOWED = 2071; // 0x817
+ field public static final int PDN_NON_IP_CALL_THROTTLED = 2070; // 0x816
+ field public static final int PDP_ACTIVATE_MAX_RETRY_FAILED = 2109; // 0x83d
+ field public static final int PDP_DUPLICATE = 2104; // 0x838
+ field public static final int PDP_ESTABLISH_TIMEOUT_EXPIRED = 2161; // 0x871
+ field public static final int PDP_INACTIVE_TIMEOUT_EXPIRED = 2163; // 0x873
+ field public static final int PDP_LOWERLAYER_ERROR = 2164; // 0x874
+ field public static final int PDP_MODIFY_COLLISION = 2165; // 0x875
+ field public static final int PDP_MODIFY_TIMEOUT_EXPIRED = 2162; // 0x872
+ field public static final int PDP_PPP_NOT_SUPPORTED = 2038; // 0x7f6
field public static final int PDP_WITHOUT_ACTIVE_TFT = 46; // 0x2e
+ field public static final int PHONE_IN_USE = 2222; // 0x8ae
+ field public static final int PHYSICAL_LINK_CLOSE_IN_PROGRESS = 2040; // 0x7f8
+ field public static final int PLMN_NOT_ALLOWED = 2101; // 0x835
+ field public static final int PPP_AUTH_FAILURE = 2229; // 0x8b5
+ field public static final int PPP_CHAP_FAILURE = 2232; // 0x8b8
+ field public static final int PPP_CLOSE_IN_PROGRESS = 2233; // 0x8b9
+ field public static final int PPP_OPTION_MISMATCH = 2230; // 0x8b6
+ field public static final int PPP_PAP_FAILURE = 2231; // 0x8b7
+ field public static final int PPP_TIMEOUT = 2228; // 0x8b4
field public static final int PREF_RADIO_TECH_CHANGED = -4; // 0xfffffffc
+ field public static final int PROFILE_BEARER_INCOMPATIBLE = 2042; // 0x7fa
field public static final int PROTOCOL_ERRORS = 111; // 0x6f
field public static final int QOS_NOT_ACCEPTED = 37; // 0x25
+ field public static final int RADIO_ACCESS_BEARER_FAILURE = 2110; // 0x83e
+ field public static final int RADIO_ACCESS_BEARER_SETUP_FAILURE = 2160; // 0x870
field public static final int RADIO_NOT_AVAILABLE = 65537; // 0x10001
field public static final int RADIO_POWER_OFF = -5; // 0xfffffffb
+ field public static final int REDIRECTION_OR_HANDOFF_IN_PROGRESS = 2220; // 0x8ac
field public static final int REGISTRATION_FAIL = -1; // 0xffffffff
field public static final int REGULAR_DEACTIVATION = 36; // 0x24
+ field public static final int REJECTED_BY_BASE_STATION = 2082; // 0x822
+ field public static final int RRC_CONNECTION_ABORTED_AFTER_HANDOVER = 2173; // 0x87d
+ field public static final int RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE = 2174; // 0x87e
+ field public static final int RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE = 2171; // 0x87b
+ field public static final int RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE = 2175; // 0x87f
+ field public static final int RRC_CONNECTION_ABORT_REQUEST = 2151; // 0x867
+ field public static final int RRC_CONNECTION_ACCESS_BARRED = 2139; // 0x85b
+ field public static final int RRC_CONNECTION_ACCESS_STRATUM_FAILURE = 2137; // 0x859
+ field public static final int RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS = 2138; // 0x85a
+ field public static final int RRC_CONNECTION_CELL_NOT_CAMPED = 2144; // 0x860
+ field public static final int RRC_CONNECTION_CELL_RESELECTION = 2140; // 0x85c
+ field public static final int RRC_CONNECTION_CONFIG_FAILURE = 2141; // 0x85d
+ field public static final int RRC_CONNECTION_INVALID_REQUEST = 2168; // 0x878
+ field public static final int RRC_CONNECTION_LINK_FAILURE = 2143; // 0x85f
+ field public static final int RRC_CONNECTION_NORMAL_RELEASE = 2147; // 0x863
+ field public static final int RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER = 2150; // 0x866
+ field public static final int RRC_CONNECTION_RADIO_LINK_FAILURE = 2148; // 0x864
+ field public static final int RRC_CONNECTION_REESTABLISHMENT_FAILURE = 2149; // 0x865
+ field public static final int RRC_CONNECTION_REJECT_BY_NETWORK = 2146; // 0x862
+ field public static final int RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE = 2172; // 0x87c
+ field public static final int RRC_CONNECTION_RF_UNAVAILABLE = 2170; // 0x87a
+ field public static final int RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR = 2152; // 0x868
+ field public static final int RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE = 2145; // 0x861
+ field public static final int RRC_CONNECTION_TIMER_EXPIRED = 2142; // 0x85e
+ field public static final int RRC_CONNECTION_TRACKING_AREA_ID_CHANGED = 2169; // 0x879
+ field public static final int RRC_UPLINK_CONNECTION_RELEASE = 2134; // 0x856
+ field public static final int RRC_UPLINK_DATA_TRANSMISSION_FAILURE = 2132; // 0x854
+ field public static final int RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER = 2133; // 0x855
+ field public static final int RRC_UPLINK_ERROR_REQUEST_FROM_NAS = 2136; // 0x858
+ field public static final int RRC_UPLINK_RADIO_LINK_FAILURE = 2135; // 0x857
+ field public static final int RUIM_NOT_PRESENT = 2085; // 0x825
+ field public static final int SECURITY_MODE_REJECTED = 2186; // 0x88a
+ field public static final int SERVICE_NOT_ALLOWED_ON_PLMN = 2129; // 0x851
field public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 33; // 0x21
field public static final int SERVICE_OPTION_NOT_SUPPORTED = 32; // 0x20
field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22
field public static final int SIGNAL_LOST = -3; // 0xfffffffd
+ field public static final int SIM_CARD_CHANGED = 2043; // 0x7fb
+ field public static final int SYNCHRONIZATION_FAILURE = 2184; // 0x888
+ field public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 2196; // 0x894
field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa
field public static final int TFT_SEMANTIC_ERROR = 41; // 0x29
field public static final int TFT_SYTAX_ERROR = 42; // 0x2a
+ field public static final int THERMAL_EMERGENCY = 2090; // 0x82a
+ field public static final int THERMAL_MITIGATION = 2062; // 0x80e
+ field public static final int TRAT_SWAP_FAILED = 2048; // 0x800
+ field public static final int UE_INITIATED_DETACH_OR_DISCONNECT = 128; // 0x80
+ field public static final int UE_IS_ENTERING_POWERSAVE_MODE = 2226; // 0x8b2
+ field public static final int UE_RAT_CHANGE = 2105; // 0x839
+ field public static final int UE_SECURITY_CAPABILITIES_MISMATCH = 2185; // 0x889
+ field public static final int UMTS_HANDOVER_TO_IWLAN = 2199; // 0x897
field public static final int UMTS_REACTIVATION_REQ = 39; // 0x27
+ field public static final int UNACCEPTABLE_NON_EPS_AUTHENTICATION = 2187; // 0x88b
field public static final int UNKNOWN = 65536; // 0x10000
field public static final int UNKNOWN_INFO_ELEMENT = 99; // 0x63
field public static final int UNKNOWN_PDP_ADDRESS_TYPE = 28; // 0x1c
field public static final int UNKNOWN_PDP_CONTEXT = 43; // 0x2b
+ field public static final int UNPREFERRED_RAT = 2039; // 0x7f7
+ field public static final int UNSUPPORTED_1X_PREV = 2214; // 0x8a6
field public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 66; // 0x42
+ field public static final int UNSUPPORTED_QCI_VALUE = 59; // 0x3b
field public static final int USER_AUTHENTICATION = 29; // 0x1d
+ field public static final int VSNCP_ADMINISTRATIVELY_PROHIBITED = 2245; // 0x8c5
+ field public static final int VSNCP_APN_UNATHORIZED = 2238; // 0x8be
+ field public static final int VSNCP_GEN_ERROR = 2237; // 0x8bd
+ field public static final int VSNCP_INSUFFICIENT_PARAMETERS = 2243; // 0x8c3
+ field public static final int VSNCP_NO_PDN_GATEWAY_ADDRESS = 2240; // 0x8c0
+ field public static final int VSNCP_PDN_EXISTS_FOR_THIS_APN = 2248; // 0x8c8
+ field public static final int VSNCP_PDN_GATEWAY_REJECT = 2242; // 0x8c2
+ field public static final int VSNCP_PDN_GATEWAY_UNREACHABLE = 2241; // 0x8c1
+ field public static final int VSNCP_PDN_ID_IN_USE = 2246; // 0x8c6
+ field public static final int VSNCP_PDN_LIMIT_EXCEEDED = 2239; // 0x8bf
+ field public static final int VSNCP_RECONNECT_NOT_ALLOWED = 2249; // 0x8c9
+ field public static final int VSNCP_RESOURCE_UNAVAILABLE = 2244; // 0x8c4
+ field public static final int VSNCP_SUBSCRIBER_LIMITATION = 2247; // 0x8c7
+ field public static final int VSNCP_TIMEOUT = 2236; // 0x8bc
}
public class DisconnectCause {
@@ -7533,9 +7943,12 @@ package android.telephony.euicc {
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public int getOtaStatus();
+ field public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
field @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) public static final String ACTION_OTA_STATUS_CHANGED = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
field public static final String ACTION_PROFILE_SELECTION = "android.telephony.euicc.action.PROFILE_SELECTION";
field public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION = "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+ field public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+ field public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED = "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
field public static final int EUICC_ACTIVATION_TYPE_BACKUP = 2; // 0x2
field public static final int EUICC_ACTIVATION_TYPE_DEFAULT = 1; // 0x1
field public static final int EUICC_ACTIVATION_TYPE_TRANSFER = 3; // 0x3
@@ -7546,7 +7959,10 @@ package android.telephony.euicc {
field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
field public static final String EXTRA_ACTIVATION_TYPE = "android.telephony.euicc.extra.ACTIVATION_TYPE";
field public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
+ field public static final String EXTRA_ENABLE_SUBSCRIPTION = "android.telephony.euicc.extra.ENABLE_SUBSCRIPTION";
field public static final String EXTRA_FORCE_PROVISION = "android.telephony.euicc.extra.FORCE_PROVISION";
+ field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.euicc.extra.SUBSCRIPTION_ID";
+ field public static final String EXTRA_SUBSCRIPTION_NICKNAME = "android.telephony.euicc.extra.SUBSCRIPTION_NICKNAME";
}
@IntDef(prefix={"EUICC_OTA_"}, value={android.telephony.euicc.EuiccManager.EUICC_OTA_IN_PROGRESS, android.telephony.euicc.EuiccManager.EUICC_OTA_FAILED, android.telephony.euicc.EuiccManager.EUICC_OTA_SUCCEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_NOT_NEEDED, android.telephony.euicc.EuiccManager.EUICC_OTA_STATUS_UNAVAILABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface EuiccManager.OtaStatus {
@@ -8601,6 +9017,22 @@ package android.telephony.mbms.vendor {
package android.util {
+ public class DocumentsStatsLog {
+ method public static void logActivityLaunch(int, boolean, int, int);
+ method public static void logFileOperation(int, int);
+ method public static void logFileOperationCanceled(int);
+ method public static void logFileOperationCopyMoveMode(int, int);
+ method public static void logFileOperationFailure(int, int);
+ method public static void logFilePick(int, long, int, boolean, int, int, int);
+ method public static void logInvalidScopedAccessRequest(int);
+ method public static void logPickerLaunchedFrom(String);
+ method public static void logRootVisited(int, int);
+ method public static void logSearchMode(int);
+ method public static void logSearchType(int);
+ method public static void logStartupMs(int);
+ method public static void logUserAction(int);
+ }
+
public class EventLog {
method public static void readEventsOnWrapping(int[], long, java.util.Collection<android.util.EventLog.Event>) throws java.io.IOException;
}
@@ -9096,6 +9528,10 @@ package android.webkit {
field public final android.content.pm.Signature[] signatures;
}
+ public abstract class WebViewRenderer {
+ ctor public WebViewRenderer();
+ }
+
public final class WebViewUpdateService {
method public static android.webkit.WebViewProviderInfo[] getAllWebViewPackages();
method public static String getCurrentWebViewPackageName();
diff --git a/api/test-current.txt b/api/test-current.txt
index 0d88937f6b7a..eca7a7a54edb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -772,6 +772,11 @@ package android.media.audiofx {
field public static final java.util.UUID EFFECT_TYPE_NULL;
}
+ public static class AudioEffect.Descriptor {
+ ctor public AudioEffect.Descriptor(android.os.Parcel);
+ method public void writeToParcel(android.os.Parcel);
+ }
+
public static interface AudioEffect.OnParameterChangeListener {
method public void onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]);
}
@@ -781,6 +786,8 @@ package android.media.audiofx {
package android.net {
public class CaptivePortal implements android.os.Parcelable {
+ ctor public CaptivePortal(android.os.IBinder);
+ method public void useNetwork();
field public static final int APP_RETURN_DISMISSED = 0; // 0x0
field public static final int APP_RETURN_UNWANTED = 1; // 0x1
field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
@@ -802,6 +809,25 @@ package android.net {
method public boolean isSameAddressAs(android.net.LinkAddress);
}
+ public final class LinkProperties implements android.os.Parcelable {
+ method public boolean addDnsServer(java.net.InetAddress);
+ method public String getTcpBufferSizes();
+ method public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
+ method public boolean hasGlobalIPv6Address();
+ method public boolean hasIPv4Address();
+ method public boolean hasIPv6DefaultRoute();
+ method public boolean isIPv4Provisioned();
+ method public boolean isIPv6Provisioned();
+ method public boolean isProvisioned();
+ method public boolean isReachable(java.net.InetAddress);
+ method public boolean removeDnsServer(java.net.InetAddress);
+ method public boolean removeRoute(android.net.RouteInfo);
+ method public void setPrivateDnsServerName(@Nullable String);
+ method public void setTcpBufferSizes(String);
+ method public void setUsePrivateDns(boolean);
+ method public void setValidatedPrivateDnsServers(java.util.Collection<java.net.InetAddress>);
+ }
+
public class Network implements android.os.Parcelable {
method public android.net.Network getPrivateDnsBypassingCopy();
}
@@ -809,6 +835,7 @@ package android.net {
public final class NetworkCapabilities implements android.os.Parcelable {
method public int[] getCapabilities();
method public int[] getTransportTypes();
+ method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities);
}
public final class RouteInfo implements android.os.Parcelable {
@@ -900,6 +927,7 @@ package android.net.metrics {
public class IpConnectivityLog {
method public boolean log(long, android.net.metrics.IpConnectivityLog.Event);
method public boolean log(String, android.net.metrics.IpConnectivityLog.Event);
+ method public boolean log(android.net.Network, int[], android.net.metrics.IpConnectivityLog.Event);
method public boolean log(int, int[], android.net.metrics.IpConnectivityLog.Event);
method public boolean log(android.net.metrics.IpConnectivityLog.Event);
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f8825ac94b65..1942b2e94f51 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -194,6 +194,16 @@ message Atom {
GnssNfwNotificationReported gnss_nfw_notification_reported = 131;
GnssConfigurationReported gnss_configuration_reported = 132;
UsbPortOverheatEvent usb_port_overheat_event_reported = 133;
+ NfcErrorOccurred nfc_error_occurred = 134;
+ NfcStateChanged nfc_state_changed = 135;
+ NfcBeamOccurred nfc_beam_occurred = 136;
+ NfcCardemulationOccurred nfc_cardemulation_occurred = 137;
+ NfcTagOccurred nfc_tag_occurred = 138;
+ NfcHceTransactionOccurred nfc_hce_transaction_occurred = 139;
+ SeStateChanged se_state_changed = 140;
+ SeOmapiReported se_omapi_reported = 141;
+ BroadcastDispatchLatencyReported broadcast_dispatch_latency_reported = 142;
+ AttentionManagerServiceResultReported attention_manager_service_result_reported = 143;
}
// Pulled events will start at field 10000.
@@ -244,6 +254,7 @@ message Atom {
ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042;
BatteryLevel battery_level = 10043;
BuildInformation build_information = 10044;
+ BatteryCycleCount battery_cycle_count = 10045;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -2514,6 +2525,19 @@ message UsbPortOverheatEvent {
optional int32 time_to_inactive_secs = 5;
};
+/**
+ * Logs total effective full charge and discharge cycles on a battery.
+ * Here are some examples of one effective cycle:
+ * 1) the battery charges from 0% to 100% and drains back to 0%,
+ * 2) charging from 50% to 100% and draining back to 50% twice.
+ * Pulled from:
+ * frameworks/base/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+ */
+message BatteryCycleCount {
+ /* Number of total charge and discharge cycles on the system battery. */
+ optional int32 cycle_count = 1;
+}
+
/*
* Logs when a connection becomes available and lost.
* Logged in StatsCompanionService.java
@@ -4309,3 +4333,167 @@ message GnssConfigurationReported {
// with spaces as separators.
optional string enabled_proxy_app_package_name_list = 13;
}
+
+/**
+ * Logs when a NFC device's error occurred.
+ * Logged from:
+ * system/nfc/src/nfc/nfc/nfc_ncif.cc
+ * packages/apps/Nfc/src/com/android/nfc/cardemulation/AidRoutingManager.java
+ */
+message NfcErrorOccurred {
+ enum Type {
+ UNKNOWN = 0;
+ CMD_TIMEOUT = 1;
+ ERROR_NOTIFICATION = 2;
+ AID_OVERFLOW = 3;
+ }
+ optional Type type = 1;
+ // If it's nci cmd timeout, log the timeout command.
+ optional uint32 nci_cmd = 2;
+
+ optional uint32 error_ntf_status_code = 3;
+}
+
+/**
+ * Logs when a NFC device's state changed event
+ * Logged from:
+ * packages/apps/Nfc/src/com/android/nfc/NfcService.java
+ */
+message NfcStateChanged {
+ enum State {
+ UNKNOWN = 0;
+ OFF = 1;
+ ON = 2;
+ ON_LOCKED = 3; // Secure Nfc enabled.
+ CRASH_RESTART = 4; // NfcService watchdog timeout restart.
+ }
+ optional State state = 1;
+}
+
+/**
+ * Logs when a NFC Beam Transaction occurred.
+ * Logged from:
+ * packages/apps/Nfc/src/com/android/nfc/P2pLinkManager.java
+ */
+message NfcBeamOccurred {
+ enum Operation {
+ UNKNOWN = 0;
+ SEND = 1;
+ RECEIVE = 2;
+ }
+ optional Operation operation = 1;
+}
+
+/**
+ * Logs when a NFC Card Emulation Transaction occurred.
+ * Logged from:
+ * packages/apps/Nfc/src/com/android/nfc/cardemulation/HostEmulationManager.java
+ * packages/apps/Nfc/src/com/android/nfc/cardemulation/HostNfcFEmulationManager.java
+ */
+message NfcCardemulationOccurred {
+ enum Category {
+ UNKNOWN = 0;
+ HCE_PAYMENT = 1;
+ HCE_OTHER = 2;
+ OFFHOST = 3;
+ }
+ // Transaction belongs to HCE payment or HCE other category, or offhost.
+ optional Category category = 1;
+ // SeName from transaction: SIMx, eSEx, HCE, HCEF.
+ optional string se_name = 2;
+}
+
+/**
+ * Logs when a NFC Tag event occurred.
+ * Logged from:
+ * packages/apps/Nfc/src/com/android/nfc/NfcDispatcher.java
+ */
+message NfcTagOccurred {
+ enum Type {
+ UNKNOWN = 0;
+ URL = 1;
+ BT_PAIRING = 2;
+ PROVISION = 3;
+ WIFI_CONNECT = 4;
+ APP_LAUNCH = 5;
+ OTHERS = 6;
+ }
+ optional Type type = 1;
+}
+
+/**
+ * Logs when Hce transaction triggered
+ * Logged from:
+ * system/nfc/src/nfc/nfc/nfc_ncif.cc
+ */
+message NfcHceTransactionOccurred {
+ // The latency period(in microseconds) it took for the first HCE data
+ // exchange.
+ optional uint32 latency_micros = 1;
+}
+
+/**
+ * Logs when SecureElement state event changed
+ * Logged from:
+ * packages/apps/SecureElement/src/com/android/se/Terminal.java
+ */
+message SeStateChanged {
+ enum State {
+ UNKNOWN = 0;
+ INITIALIZED = 1;
+ DISCONNECTED = 2;
+ CONNECTED = 3;
+ HALCRASH = 4;
+ }
+ optional State state = 1;
+
+ optional string state_change_reason = 2;
+ // SIMx or eSEx.
+ optional string terminal = 3;
+}
+
+/**
+ * Logs when Omapi API used
+ * Logged from:
+ * packages/apps/SecureElement/src/com/android/se/Terminal.java
+ */
+message SeOmapiReported {
+ enum Operation {
+ UNKNOWN = 0;
+ OPEN_CHANNEL = 1;
+ }
+ optional Operation operation = 1;
+ // SIMx or eSEx.
+ optional string terminal = 2;
+
+ optional string packageName = 3;
+}
+
+/**
+ * Logs the dispatch latency of a broadcast during processing of BOOT_COMPLETED.
+ * The dispatch latency is the dispatchClockTime - enqueueClockTime.
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
+ */
+message BroadcastDispatchLatencyReported {
+ optional int64 dispatch_latency_millis = 1;
+}
+
+/**
+ * Logs AttentionManagerService attention check result.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/attention/AttentionManagerService.java
+ */
+message AttentionManagerServiceResultReported {
+ // See core/java/android/service/attention/AttentionService.java
+ enum AttentionCheckResult {
+ UNKNOWN = 20;
+ ATTENTION_SUCCESS_ABSENT = 0;
+ ATTENTION_SUCCESS_PRESENT = 1;
+ ATTENTION_FAILURE_PREEMPTED = 2;
+ ATTENTION_FAILURE_TIMED_OUT = 3;
+ ATTENTION_FAILURE_UNKNOWN = 4;
+ }
+ optional AttentionCheckResult attention_check_result = 1 [default = UNKNOWN];
+}
diff --git a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
index b878652f5cf1..75b63f4b5f9e 100644
--- a/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourceHealthManagerPuller.cpp
@@ -24,16 +24,16 @@
#include "ResourceHealthManagerPuller.h"
#include "logd/LogEvent.h"
-#include "statslog.h"
#include "stats_log_util.h"
+#include "statslog.h"
using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
using android::hardware::health::V2_0::get_health_service;
using android::hardware::health::V2_0::HealthInfo;
using android::hardware::health::V2_0::IHealth;
using android::hardware::health::V2_0::Result;
-using android::hardware::Return;
-using android::hardware::Void;
using std::make_shared;
using std::shared_ptr;
@@ -75,35 +75,41 @@ bool ResourceHealthManagerPuller::PullInternal(vector<shared_ptr<LogEvent>>* dat
}
if (mTagId == android::util::REMAINING_BATTERY_CAPACITY) {
auto ptr = make_shared<LogEvent>(android::util::REMAINING_BATTERY_CAPACITY,
- wallClockTimestampNs, elapsedTimestampNs);
+ wallClockTimestampNs, elapsedTimestampNs);
ptr->write(v.legacy.batteryChargeCounter);
ptr->init();
data->push_back(ptr);
} else if (mTagId == android::util::FULL_BATTERY_CAPACITY) {
auto ptr = make_shared<LogEvent>(android::util::FULL_BATTERY_CAPACITY,
- wallClockTimestampNs, elapsedTimestampNs);
+ wallClockTimestampNs, elapsedTimestampNs);
ptr->write(v.legacy.batteryFullCharge);
ptr->init();
data->push_back(ptr);
} else if (mTagId == android::util::BATTERY_VOLTAGE) {
- auto ptr = make_shared<LogEvent>(android::util::BATTERY_VOLTAGE,
- wallClockTimestampNs, elapsedTimestampNs);
+ auto ptr = make_shared<LogEvent>(android::util::BATTERY_VOLTAGE, wallClockTimestampNs,
+ elapsedTimestampNs);
ptr->write(v.legacy.batteryVoltage);
ptr->init();
data->push_back(ptr);
} else if (mTagId == android::util::BATTERY_LEVEL) {
- auto ptr = make_shared<LogEvent>(android::util::BATTERY_LEVEL,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write(v.legacy.batteryLevel);
- ptr->init();
- data->push_back(ptr);
+ auto ptr = make_shared<LogEvent>(android::util::BATTERY_LEVEL, wallClockTimestampNs,
+ elapsedTimestampNs);
+ ptr->write(v.legacy.batteryLevel);
+ ptr->init();
+ data->push_back(ptr);
+ } else if (mTagId == android::util::BATTERY_CYCLE_COUNT) {
+ auto ptr = make_shared<LogEvent>(android::util::BATTERY_CYCLE_COUNT,
+ wallClockTimestampNs, elapsedTimestampNs);
+ ptr->write(v.legacy.batteryCycleCount);
+ ptr->init();
+ data->push_back(ptr);
} else {
ALOGE("Unsupported tag in ResourceHealthManagerPuller: %d", mTagId);
}
});
if (!result_success || !ret.isOk()) {
ALOGE("getHealthHal() failed: health HAL service not available. Description: %s",
- ret.description().c_str());
+ ret.description().c_str());
if (!ret.isOk() && ret.isDeadObject()) {
gHealthHal = nullptr;
}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 4a716cf3fa77..19a7389cb3d9 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -131,9 +131,12 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = {
// battery_voltage
{android::util::BATTERY_VOLTAGE,
{.puller = new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
- // battery_voltage
+ // battery_level
{android::util::BATTERY_LEVEL,
{.puller = new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
+ // battery_cycle_count
+ {android::util::BATTERY_CYCLE_COUNT,
+ {.puller = new ResourceHealthManagerPuller(android::util::BATTERY_CYCLE_COUNT)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
{.additiveFields = {4, 5, 6, 7, 8, 9},
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 4174ad7cd586..1b7fbfe0e32c 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -23,8 +23,10 @@ import android.os.IUserManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.telecom.Log;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
import com.android.internal.os.BaseCommand;
import com.android.internal.telecom.ITelecomService;
@@ -45,6 +47,8 @@ public final class Telecom extends BaseCommand {
private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled";
private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
+ private static final String COMMAND_SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT =
+ "set-user-selected-outgoing-phone-account";
private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account";
private static final String COMMAND_SET_TEST_CALL_REDIRECTION_APP = "set-test-call-redirection-app";
private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
@@ -70,6 +74,8 @@ public final class Telecom extends BaseCommand {
+ "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n"
+ "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n"
+ "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n"
+ + "usage: telecom set-user-selected-outgoing-phone-account <COMPONENT> <ID> "
+ + "<USER_SN>\n"
+ "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
+ "usage: telecom set-test-call-screening-app <PACKAGE>\n"
+ "usage: telecom set-test-auto-mode-app <PACKAGE>\n"
@@ -104,16 +110,18 @@ public final class Telecom extends BaseCommand {
mTelecomService = ITelecomService.Stub.asInterface(
ServiceManager.getService(Context.TELECOM_SERVICE));
if (mTelecomService == null) {
+ Log.w(this, "onRun: Can't access telecom manager.");
showError("Error: Could not access the Telecom Manager. Is the system running?");
return;
}
mUserManager = IUserManager.Stub
.asInterface(ServiceManager.getService(Context.USER_SERVICE));
if (mUserManager == null) {
+ Log.w(this, "onRun: Can't access user manager.");
showError("Error: Could not access the User Manager. Is the system running?");
return;
}
-
+ Log.i(this, "onRun: parsing command.");
String command = nextArgRequired();
switch (command) {
case COMMAND_SET_PHONE_ACCOUNT_ENABLED:
@@ -143,6 +151,9 @@ public final class Telecom extends BaseCommand {
case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
runRegisterSimPhoneAccount();
break;
+ case COMMAND_SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT:
+ runSetUserSelectedOutgoingPhoneAccount();
+ break;
case COMMAND_UNREGISTER_PHONE_ACCOUNT:
runUnregisterPhoneAccount();
break;
@@ -159,6 +170,7 @@ public final class Telecom extends BaseCommand {
runWaitOnHandler();
break;
default:
+ Log.w(this, "onRun: unknown command: %s", command);
throw new IllegalArgumentException ("unknown command '" + command + "'");
}
}
@@ -227,6 +239,13 @@ public final class Telecom extends BaseCommand {
mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
}
+ private void runSetUserSelectedOutgoingPhoneAccount() throws RemoteException {
+ Log.i(this, "runSetUserSelectedOutgoingPhoneAccount");
+ final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
+ mTelecomService.setUserSelectedOutgoingPhoneAccount(handle);
+ System.out.println("Success - " + handle + " set as default outgoing account.");
+ }
+
private void runUnregisterPhoneAccount() throws RemoteException {
final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
mTelecomService.unregisterPhoneAccount(handle);
@@ -256,7 +275,10 @@ public final class Telecom extends BaseCommand {
}
- private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException{
+ private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException {
+ if (TextUtils.isEmpty(mArgs.peekNextArg())) {
+ return null;
+ }
final ComponentName component = parseComponentName(nextArgRequired());
final String accountId = nextArgRequired();
final String userSnInStr = nextArgRequired();
@@ -265,6 +287,7 @@ public final class Telecom extends BaseCommand {
final int userSn = Integer.parseInt(userSnInStr);
userHandle = UserHandle.of(mUserManager.getUserHandle(userSn));
} catch (NumberFormatException ex) {
+ Log.w(this, "getPhoneAccountHandleFromArgs - invalid user %s", userSnInStr);
throw new IllegalArgumentException ("Invalid user serial number " + userSnInStr);
}
return new PhoneAccountHandle(component, accountId, userHandle);
@@ -277,4 +300,4 @@ public final class Telecom extends BaseCommand {
}
return cn;
}
-} \ No newline at end of file
+}
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 3ec0db4d99ce..c2e441b35f48 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -2768,6 +2768,11 @@ HPLandroid/hardware/location/GeofenceHardwareService$1;->getStatusOfMonitoringTy
HPLandroid/hardware/location/GeofenceHardwareService$1;->registerForMonitorStateChangeCallback(ILandroid/hardware/location/IGeofenceHardwareMonitorCallback;)Z
HPLandroid/hardware/location/GeofenceHardwareService$1;->removeGeofence(II)Z
HPLandroid/hardware/location/GeofenceHardwareService;->checkPermission(III)V
+HPLandroid/hardware/location/IActivityRecognitionHardwareClient$Stub$Proxy;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
+HPLandroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V
+HPLandroid/hardware/location/IActivityRecognitionHardwareClient$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IActivityRecognitionHardwareClient;
+HPLandroid/hardware/location/IActivityRecognitionHardwareClient$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
+HPLandroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
HPLandroid/hardware/location/IContextHubCallback$Stub;->asBinder()Landroid/os/IBinder;
HPLandroid/hardware/location/IContextHubCallback$Stub;->onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
HPLandroid/hardware/location/IContextHubService$Stub$Proxy;->findNanoAppOnHub(ILandroid/hardware/location/NanoAppFilter;)[I
@@ -21777,6 +21782,7 @@ HSPLandroid/hardware/input/KeyboardLayout$1;-><init>()V
HSPLandroid/hardware/input/TouchCalibration$1;-><init>()V
HSPLandroid/hardware/input/TouchCalibration;-><init>()V
HSPLandroid/hardware/input/TouchCalibration;->getAffineTransform()[F
+HSPLandroid/hardware/location/ActivityRecognitionHardware;->isSupported()Z
HSPLandroid/hardware/location/ContextHubInfo$1;-><init>()V
HSPLandroid/hardware/location/ContextHubInfo;-><init>(Landroid/hardware/contexthub/V1_0/ContextHub;)V
HSPLandroid/hardware/location/ContextHubMessage$1;-><init>()V
@@ -21796,6 +21802,13 @@ HSPLandroid/hardware/location/GeofenceHardwareService$1;->setGpsGeofenceHardware
HSPLandroid/hardware/location/GeofenceHardwareService;-><init>()V
HSPLandroid/hardware/location/GeofenceHardwareService;->onBind(Landroid/content/Intent;)Landroid/os/IBinder;
HSPLandroid/hardware/location/GeofenceHardwareService;->onCreate()V
+HSPLandroid/hardware/location/IActivityRecognitionHardware;->disableActivityEvent(Ljava/lang/String;I)Z
+HSPLandroid/hardware/location/IActivityRecognitionHardware;->enableActivityEvent(Ljava/lang/String;IJ)Z
+HSPLandroid/hardware/location/IActivityRecognitionHardware;->flush()Z
+HSPLandroid/hardware/location/IActivityRecognitionHardware;->getSupportedActivities()[Ljava/lang/String;
+HSPLandroid/hardware/location/IActivityRecognitionHardware;->isActivitySupported(Ljava/lang/String;)Z
+HSPLandroid/hardware/location/IActivityRecognitionHardware;->registerSink(Landroid/hardware/location/IActivityRecognitionHardwareSink;)Z
+HSPLandroid/hardware/location/IActivityRecognitionHardware;->unregisterSink(Landroid/hardware/location/IActivityRecognitionHardwareSink;)Z
HSPLandroid/hardware/location/IContextHubCallback$Stub$Proxy;->asBinder()Landroid/os/IBinder;
HSPLandroid/hardware/location/IContextHubCallback$Stub$Proxy;->onMessageReceipt(IILandroid/hardware/location/ContextHubMessage;)V
HSPLandroid/hardware/location/IContextHubCallback;->onMessageReceipt(IILandroid/hardware/location/ContextHubMessage;)V
@@ -55637,6 +55650,7 @@ Landroid/hardware/input/InputManagerInternal;
Landroid/hardware/input/KeyboardLayout$1;
Landroid/hardware/input/TouchCalibration$1;
Landroid/hardware/input/TouchCalibration;
+Landroid/hardware/location/ActivityRecognitionHardware;
Landroid/hardware/location/ContextHubInfo$1;
Landroid/hardware/location/ContextHubInfo;
Landroid/hardware/location/ContextHubManager;
@@ -55652,6 +55666,11 @@ Landroid/hardware/location/GeofenceHardwareMonitorEvent;
Landroid/hardware/location/GeofenceHardwareRequestParcelable$1;
Landroid/hardware/location/GeofenceHardwareService$1;
Landroid/hardware/location/GeofenceHardwareService;
+Landroid/hardware/location/IActivityRecognitionHardware$Stub;
+Landroid/hardware/location/IActivityRecognitionHardware;
+Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub$Proxy;
+Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;
+Landroid/hardware/location/IActivityRecognitionHardwareClient;
Landroid/hardware/location/IContextHubCallback$Stub$Proxy;
Landroid/hardware/location/IContextHubCallback;
Landroid/hardware/location/IContextHubClient$Stub;
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 700df641659c..fc47f67f174c 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -462,6 +462,8 @@ Landroid/hardware/input/IInputManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I
Landroid/hardware/input/IInputManager;->injectInputEvent(Landroid/view/InputEvent;I)Z
+Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V
+Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService;
Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager;
@@ -921,7 +923,6 @@ Landroid/os/PowerManager;->isLightDeviceIdleMode()Z
Landroid/os/PowerManager;->mService:Landroid/os/IPowerManager;
Landroid/os/PowerManager;->validateWakeLockParameters(ILjava/lang/String;)V
Landroid/os/PowerManager;->wakeUp(JLjava/lang/String;)V
-Landroid/os/Process;->BLUETOOTH_UID:I
Landroid/os/Process;->DRM_UID:I
Landroid/os/Process;->getFreeMemory()J
Landroid/os/Process;->getParentPid(I)I
@@ -948,10 +949,8 @@ Landroid/os/Process;->PROC_TERM_MASK:I
Landroid/os/Process;->PROC_ZERO_TERM:I
Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z
Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V
-Landroid/os/Process;->ROOT_UID:I
Landroid/os/Process;->setArgV0(Ljava/lang/String;)V
Landroid/os/Process;->setProcessGroup(II)V
-Landroid/os/Process;->SHELL_UID:I
Landroid/os/Process;->VPN_UID:I
Landroid/os/Process;->WIFI_UID:I
Landroid/os/RecoverySystem;->verifyPackageCompatibility(Ljava/io/InputStream;)Z
@@ -993,8 +992,6 @@ Landroid/os/ServiceManager;->sServiceManager:Landroid/os/IServiceManager;
Landroid/os/ServiceManagerNative;->asInterface(Landroid/os/IBinder;)Landroid/os/IServiceManager;
Landroid/os/ServiceManagerProxy;->getService(Ljava/lang/String;)Landroid/os/IBinder;
Landroid/os/ServiceManagerProxy;->mRemote:Landroid/os/IBinder;
-Landroid/os/ServiceSpecificException;-><init>(ILjava/lang/String;)V
-Landroid/os/ServiceSpecificException;->errorCode:I
Landroid/os/SharedMemory;->getFd()I
Landroid/os/ShellCommand;->peekNextArg()Ljava/lang/String;
Landroid/os/StatFs;->mStat:Landroid/system/StructStatVfs;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index cd798adf5d44..c8a2a9c19b28 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1405,7 +1405,10 @@ android.hardware.input.InputManager
android.hardware.input.InputManager$InputDeviceListener
android.hardware.input.InputManager$InputDeviceListenerDelegate
android.hardware.input.InputManager$InputDevicesChangedListener
+android.hardware.location.ActivityRecognitionHardware
android.hardware.location.ContextHubManager
+android.hardware.location.IActivityRecognitionHardware
+android.hardware.location.IActivityRecognitionHardware$Stub
android.hardware.radio.RadioManager
android.hardware.soundtrigger.SoundTrigger
android.hardware.soundtrigger.SoundTrigger$ConfidenceLevel
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 35098a0ff5d9..cebe6e1211e0 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -37,6 +37,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.Display;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -382,7 +383,8 @@ public abstract class AccessibilityService extends Service {
void init(int connectionId, IBinder windowToken);
boolean onGesture(int gestureId);
boolean onKeyEvent(KeyEvent event);
- void onMagnificationChanged(@NonNull Region region,
+ /** Magnification changed callbacks for different displays */
+ void onMagnificationChanged(int displayId, @NonNull Region region,
float scale, float centerX, float centerY);
void onSoftKeyboardShowModeChanged(int showMode);
void onPerformGestureResult(int sequence, boolean completedSuccessfully);
@@ -452,7 +454,9 @@ public abstract class AccessibilityService extends Service {
private WindowManager mWindowManager;
- private MagnificationController mMagnificationController;
+ /** List of magnification controllers, mapping from displayId -> MagnificationController. */
+ private final SparseArray<MagnificationController> mMagnificationControllers =
+ new SparseArray<>(0);
private SoftKeyboardController mSoftKeyboardController;
private AccessibilityButtonController mAccessibilityButtonController;
@@ -483,8 +487,10 @@ public abstract class AccessibilityService extends Service {
* client code.
*/
private void dispatchServiceConnected() {
- if (mMagnificationController != null) {
- mMagnificationController.onServiceConnected();
+ synchronized (mLock) {
+ for (int i = 0; i < mMagnificationControllers.size(); i++) {
+ mMagnificationControllers.valueAt(i).onServiceConnectedLocked();
+ }
}
if (mSoftKeyboardController != null) {
mSoftKeyboardController.onServiceConnected();
@@ -652,11 +658,34 @@ public abstract class AccessibilityService extends Service {
*/
@NonNull
public final MagnificationController getMagnificationController() {
+ return getMagnificationController(Display.DEFAULT_DISPLAY);
+ }
+
+ /**
+ * Returns the magnification controller of specified logical display, which may be used to
+ * query and modify the state of display magnification.
+ * <p>
+ * <strong>Note:</strong> In order to control magnification, your service
+ * must declare the capability by setting the
+ * {@link android.R.styleable#AccessibilityService_canControlMagnification}
+ * property in its meta-data. For more information, see
+ * {@link #SERVICE_META_DATA}.
+ *
+ * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for
+ * default display.
+ * @return the magnification controller
+ *
+ * @hide
+ */
+ @NonNull
+ public final MagnificationController getMagnificationController(int displayId) {
synchronized (mLock) {
- if (mMagnificationController == null) {
- mMagnificationController = new MagnificationController(this, mLock);
+ MagnificationController controller = mMagnificationControllers.get(displayId);
+ if (controller == null) {
+ controller = new MagnificationController(this, mLock, displayId);
+ mMagnificationControllers.put(displayId, controller);
}
- return mMagnificationController;
+ return controller;
}
}
@@ -765,11 +794,14 @@ public abstract class AccessibilityService extends Service {
}
}
- private void onMagnificationChanged(@NonNull Region region, float scale,
+ private void onMagnificationChanged(int displayId, @NonNull Region region, float scale,
float centerX, float centerY) {
- if (mMagnificationController != null) {
- mMagnificationController.dispatchMagnificationChanged(
- region, scale, centerX, centerY);
+ MagnificationController controller;
+ synchronized (mLock) {
+ controller = mMagnificationControllers.get(displayId);
+ }
+ if (controller != null) {
+ controller.dispatchMagnificationChanged(region, scale, centerX, centerY);
}
}
@@ -794,6 +826,7 @@ public abstract class AccessibilityService extends Service {
*/
public static final class MagnificationController {
private final AccessibilityService mService;
+ private final int mDisplayId;
/**
* Map of listeners to their handlers. Lazily created when adding the
@@ -802,19 +835,19 @@ public abstract class AccessibilityService extends Service {
private ArrayMap<OnMagnificationChangedListener, Handler> mListeners;
private final Object mLock;
- MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock) {
+ MagnificationController(@NonNull AccessibilityService service, @NonNull Object lock,
+ int displayId) {
mService = service;
mLock = lock;
+ mDisplayId = displayId;
}
/**
* Called when the service is connected.
*/
- void onServiceConnected() {
- synchronized (mLock) {
- if (mListeners != null && !mListeners.isEmpty()) {
- setMagnificationCallbackEnabled(true);
- }
+ void onServiceConnectedLocked() {
+ if (mListeners != null && !mListeners.isEmpty()) {
+ setMagnificationCallbackEnabled(true);
}
}
@@ -891,7 +924,7 @@ public abstract class AccessibilityService extends Service {
mService.mConnectionId);
if (connection != null) {
try {
- connection.setMagnificationCallbackEnabled(enabled);
+ connection.setMagnificationCallbackEnabled(mDisplayId, enabled);
} catch (RemoteException re) {
throw new RuntimeException(re);
}
@@ -952,7 +985,7 @@ public abstract class AccessibilityService extends Service {
mService.mConnectionId);
if (connection != null) {
try {
- return connection.getMagnificationScale();
+ return connection.getMagnificationScale(mDisplayId);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Failed to obtain scale", re);
re.rethrowFromSystemServer();
@@ -981,7 +1014,7 @@ public abstract class AccessibilityService extends Service {
mService.mConnectionId);
if (connection != null) {
try {
- return connection.getMagnificationCenterX();
+ return connection.getMagnificationCenterX(mDisplayId);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Failed to obtain center X", re);
re.rethrowFromSystemServer();
@@ -1010,7 +1043,7 @@ public abstract class AccessibilityService extends Service {
mService.mConnectionId);
if (connection != null) {
try {
- return connection.getMagnificationCenterY();
+ return connection.getMagnificationCenterY(mDisplayId);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Failed to obtain center Y", re);
re.rethrowFromSystemServer();
@@ -1044,7 +1077,7 @@ public abstract class AccessibilityService extends Service {
mService.mConnectionId);
if (connection != null) {
try {
- return connection.getMagnificationRegion();
+ return connection.getMagnificationRegion(mDisplayId);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Failed to obtain magnified region", re);
re.rethrowFromSystemServer();
@@ -1073,7 +1106,7 @@ public abstract class AccessibilityService extends Service {
mService.mConnectionId);
if (connection != null) {
try {
- return connection.resetMagnification(animate);
+ return connection.resetMagnification(mDisplayId, animate);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Failed to reset", re);
re.rethrowFromSystemServer();
@@ -1101,7 +1134,7 @@ public abstract class AccessibilityService extends Service {
mService.mConnectionId);
if (connection != null) {
try {
- return connection.setMagnificationScaleAndCenter(
+ return connection.setMagnificationScaleAndCenter(mDisplayId,
scale, Float.NaN, Float.NaN, animate);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Failed to set scale", re);
@@ -1133,7 +1166,7 @@ public abstract class AccessibilityService extends Service {
mService.mConnectionId);
if (connection != null) {
try {
- return connection.setMagnificationScaleAndCenter(
+ return connection.setMagnificationScaleAndCenter(mDisplayId,
Float.NaN, centerX, centerY, animate);
} catch (RemoteException re) {
Log.w(LOG_TAG, "Failed to set center", re);
@@ -1624,9 +1657,10 @@ public abstract class AccessibilityService extends Service {
}
@Override
- public void onMagnificationChanged(@NonNull Region region,
+ public void onMagnificationChanged(int displayId, @NonNull Region region,
float scale, float centerX, float centerY) {
- AccessibilityService.this.onMagnificationChanged(region, scale, centerX, centerY);
+ AccessibilityService.this.onMagnificationChanged(displayId, region, scale,
+ centerX, centerY);
}
@Override
@@ -1729,13 +1763,15 @@ public abstract class AccessibilityService extends Service {
mCaller.sendMessage(message);
}
- public void onMagnificationChanged(@NonNull Region region,
+ /** Magnification changed callbacks for different displays */
+ public void onMagnificationChanged(int displayId, @NonNull Region region,
float scale, float centerX, float centerY) {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = region;
args.arg2 = scale;
args.arg3 = centerX;
args.arg4 = centerY;
+ args.argi1 = displayId;
final Message message = mCaller.obtainMessageO(DO_ON_MAGNIFICATION_CHANGED, args);
mCaller.sendMessage(message);
@@ -1865,7 +1901,10 @@ public abstract class AccessibilityService extends Service {
final float scale = (float) args.arg2;
final float centerX = (float) args.arg3;
final float centerY = (float) args.arg4;
- mCallback.onMagnificationChanged(region, scale, centerX, centerY);
+ final int displayId = args.argi1;
+ args.recycle();
+ mCallback.onMagnificationChanged(displayId, region, scale,
+ centerX, centerY);
}
} return;
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 4e96b8f11628..1dae4fca111e 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -41,7 +41,7 @@ import android.view.KeyEvent;
void onKeyEvent(in KeyEvent event, int sequence);
- void onMagnificationChanged(in Region region, float scale, float centerX, float centerY);
+ void onMagnificationChanged(int displayId, in Region region, float scale, float centerX, float centerY);
void onSoftKeyboardShowModeChanged(int showMode);
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 276131f9d0d6..8c38fe4aaf6f 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -70,20 +70,20 @@ interface IAccessibilityServiceConnection {
oneway void setOnKeyEventResult(boolean handled, int sequence);
- float getMagnificationScale();
+ float getMagnificationScale(int displayId);
- float getMagnificationCenterX();
+ float getMagnificationCenterX(int displayId);
- float getMagnificationCenterY();
+ float getMagnificationCenterY(int displayId);
- Region getMagnificationRegion();
+ Region getMagnificationRegion(int displayId);
- boolean resetMagnification(boolean animate);
+ boolean resetMagnification(int displayId, boolean animate);
- boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY,
+ boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX, float centerY,
boolean animate);
- void setMagnificationCallbackEnabled(boolean enabled);
+ void setMagnificationCallbackEnabled(int displayId, boolean enabled);
boolean setSoftKeyboardShowMode(int showMode);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 836627efb379..1063be4c5c7d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -74,6 +74,7 @@ import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StrictMode;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.text.Selection;
import android.text.SpannableStringBuilder;
@@ -1049,33 +1050,56 @@ public class Activity extends ContextThemeWrapper
@Retention(RetentionPolicy.SOURCE)
@interface ContentCaptureNotificationType{}
-
- private void notifyContentCaptureManagerIfNeeded(@ContentCaptureNotificationType int type) {
- final ContentCaptureManager cm = getContentCaptureManager();
- if (cm == null) return;
-
+ private String getContentCaptureTypeAsString(@ContentCaptureNotificationType int type) {
switch (type) {
case CONTENT_CAPTURE_START:
- //TODO(b/111276913): decide whether the InteractionSessionId should be
- // saved / restored in the activity bundle - probably not
- int flags = 0;
- if ((getWindow().getAttributes().flags
- & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
- flags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
- }
- cm.onActivityStarted(mToken, getComponentName(), flags);
- break;
+ return "START";
case CONTENT_CAPTURE_PAUSE:
- cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_PAUSED);
- break;
+ return "PAUSE";
case CONTENT_CAPTURE_RESUME:
- cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_RESUMED);
- break;
+ return "RESUME";
case CONTENT_CAPTURE_STOP:
- cm.onActivityStopped();
- break;
+ return "STOP";
default:
- Log.wtf(TAG, "Invalid @ContentCaptureNotificationType: " + type);
+ return "UNKNOW-" + type;
+ }
+ }
+
+ private void notifyContentCaptureManagerIfNeeded(@ContentCaptureNotificationType int type) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "notifyContentCapture(" + getContentCaptureTypeAsString(type) + ") for "
+ + mComponent.toShortString());
+ }
+ try {
+ final ContentCaptureManager cm = getContentCaptureManager();
+ if (cm == null) return;
+
+ switch (type) {
+ case CONTENT_CAPTURE_START:
+ //TODO(b/111276913): decide whether the InteractionSessionId should be
+ // saved / restored in the activity bundle - probably not
+ int flags = 0;
+ if ((getWindow().getAttributes().flags
+ & WindowManager.LayoutParams.FLAG_SECURE) != 0) {
+ flags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
+ }
+ cm.onActivityStarted(mToken, getComponentName(), flags);
+ break;
+ case CONTENT_CAPTURE_PAUSE:
+ cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_PAUSED);
+ break;
+ case CONTENT_CAPTURE_RESUME:
+ cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_RESUMED);
+ break;
+ case CONTENT_CAPTURE_STOP:
+ cm.onActivityStopped();
+ break;
+ default:
+ Log.wtf(TAG, "Invalid @ContentCaptureNotificationType: " + type);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e0b8d78ebabc..1045b7aa0890 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3529,12 +3529,32 @@ public class ActivityManager {
/**
* Returns "true" if device is running in a test harness.
+ *
+ * @deprecated this method is false for all user builds. Users looking to check if their device
+ * is running in a device farm should see {@link #isRunningInUserTestHarness()}.
*/
+ @Deprecated
public static boolean isRunningInTestHarness() {
return SystemProperties.getBoolean("ro.test_harness", false);
}
/**
+ * Returns "true" if the device is running in Test Harness Mode.
+ *
+ * <p>Test Harness Mode is a feature that allows devices to run without human interaction in a
+ * device farm/testing harness (such as Firebase Test Lab). You should check this method if you
+ * want your app to behave differently when running in a test harness to skip setup screens that
+ * would impede UI testing. e.g. a keyboard application that has a full screen setup page for
+ * the first time it is launched.
+ *
+ * <p>Note that you should <em>not</em> use this to determine whether or not your app is running
+ * an instrumentation test, as it is not set for a standard device running a test.
+ */
+ public static boolean isRunningInUserTestHarness() {
+ return SystemProperties.getBoolean("persist.sys.test_harness", false);
+ }
+
+ /**
* Unsupported compiled sdk warning should always be shown for the intput activity
* even in cases where the system would normally not show the warning. E.g. when running in a
* test harness.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7767f0491a16..a3243a5de72a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5939,6 +5939,11 @@ public final class ActivityThread extends ClientTransactionHandler {
Binder.enableTracing();
}
+ // Initialize heap profiling.
+ if (isAppProfileable || Build.IS_DEBUGGABLE) {
+ nInitZygoteChildHeapProfiling();
+ }
+
// Allow renderer debugging features if we're debuggable.
boolean isAppDebuggable = (data.appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
HardwareRenderer.setDebuggingEnabled(isAppDebuggable || Build.IS_DEBUGGABLE);
@@ -6965,4 +6970,5 @@ public final class ActivityThread extends ClientTransactionHandler {
// ------------------ Regular JNI ------------------------
private native void nPurgePendingResources();
private native void nDumpGraphicsInfo(FileDescriptor fd);
+ private native void nInitZygoteChildHeapProfiling();
}
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index ab8f234766d6..4d3711ae7ff5 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -328,7 +328,7 @@ public class ActivityView extends ViewGroup {
}
} else {
mTmpTransaction.reparent(mRootSurfaceControl,
- mSurfaceView.getSurfaceControl().getHandle()).apply();
+ mSurfaceView.getSurfaceControl()).apply();
}
if (mVirtualDisplay != null) {
@@ -390,7 +390,7 @@ public class ActivityView extends ViewGroup {
.build();
try {
- wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle());
+ wm.reparentDisplayContent(displayId, mRootSurfaceControl);
wm.dontOverrideDisplayInfo(displayId);
if (mSingleTaskInstance) {
mActivityTaskManager.setDisplayToSingleTaskInstance(displayId);
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 1622c06b0a34..853b45e0a80d 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -1324,7 +1324,8 @@ public class DownloadManager {
* @param description the description that would appear for this file in Downloads App.
* @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files
* scanned by MediaScanner appear in the applications used to view media (for example,
- * Gallery app).
+ * Gallery app). Starting from {@link android.os.Build.VERSION_CODES#Q}, this argument is
+ * ignored and the file is always scanned by MediaScanner.
* @param mimeType mimetype of the file.
* @param path absolute pathname to the file. The file should be world-readable, so that it can
* be managed by the Downloads App and any other app that is used to read it (for example,
@@ -1353,7 +1354,8 @@ public class DownloadManager {
* @param description the description that would appear for this file in Downloads App.
* @param isMediaScannerScannable true if the file is to be scanned by MediaScanner. Files
* scanned by MediaScanner appear in the applications used to view media (for example,
- * Gallery app).
+ * Gallery app). Starting from {@link android.os.Build.VERSION_CODES#Q}, this argument is
+ * ignored and the file is always scanned by MediaScanner.
* @param mimeType mimetype of the file.
* @param path absolute pathname to the file. The file should be world-readable, so that it can
* be managed by the Downloads App and any other app that is used to read it (for example,
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 1ae0f52ac74e..75c90542ebce 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -19,6 +19,7 @@ package android.app;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -124,6 +125,7 @@ public class KeyguardManager {
*
* @return the intent for launching the activity or null if no password is required.
**/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public Intent createConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) {
if (!isDeviceSecure()) return null;
Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL);
@@ -176,6 +178,7 @@ public class KeyguardManager {
* @throws IllegalStateException if the device has already been provisioned
* @hide
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
@SystemApi
public Intent createConfirmFactoryResetCredentialIntent(
CharSequence title, CharSequence description, CharSequence alternateButtonLabel) {
@@ -231,6 +234,7 @@ public class KeyguardManager {
* secure notifications cannot be shown if {@code false}
* @hide
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
@RequiresPermission(Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)
@SystemApi
public void setPrivateNotificationsAllowed(boolean allow) {
@@ -249,6 +253,7 @@ public class KeyguardManager {
* By default, private notifications are allowed.
* @hide
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
@RequiresPermission(Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)
@SystemApi
public boolean getPrivateNotificationsAllowed() {
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 3f9627ed807c..a021e3cb2d78 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1240,7 +1240,7 @@ public final class UiAutomation {
}
@Override
- public void onMagnificationChanged(@NonNull Region region,
+ public void onMagnificationChanged(int displayId, @NonNull Region region,
float scale, float centerX, float centerY) {
/* do nothing */
}
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 5b87de48210f..5f1a94c835c3 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -215,19 +215,12 @@ public class VrManager {
}
/**
- * Start VR Input method for the packageName in {@link ComponentName}.
- * This method notifies InputMethodManagerService to use VR IME instead of
- * regular phone IME.
- * @param componentName ComponentName of a VR InputMethod that should be set as selected
- * input by InputMethodManagerService.
+ * This method is not implemented.
+ *
+ * @param componentName not used
*/
@RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
public void setVrInputMethod(ComponentName componentName) {
- try {
- mService.setVrInputMethod(componentName);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6d7c547f7cf1..b0a08edfcaaf 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1362,16 +1362,23 @@ public class DevicePolicyManager {
public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
/**
- * Activity action: have the user enter a new password. This activity should
- * be launched after using {@link #setPasswordQuality(ComponentName, int)},
- * or {@link #setPasswordMinimumLength(ComponentName, int)} to have the user
- * enter a new password that meets the current requirements. You can use
- * {@link #isActivePasswordSufficient()} to determine whether you need to
- * have the user select a new password in order to meet the current
- * constraints. Upon being resumed from this activity, you can check the new
+ * Activity action: have the user enter a new password.
+ *
+ * <p>For admin apps, this activity should be launched after using {@link
+ * #setPasswordQuality(ComponentName, int)}, or {@link
+ * #setPasswordMinimumLength(ComponentName, int)} to have the user enter a new password that
+ * meets the current requirements. You can use {@link #isActivePasswordSufficient()} to
+ * determine whether you need to have the user select a new password in order to meet the
+ * current constraints. Upon being resumed from this activity, you can check the new
* password characteristics to see if they are sufficient.
*
- * If the intent is launched from within a managed profile with a profile
+ * <p>Non-admin apps can use {@link #getPasswordComplexity()} to check the current screen lock
+ * complexity, and use this activity with extra {@link #EXTRA_PASSWORD_COMPLEXITY} to suggest
+ * to users how complex the app wants the new screen lock to be. Note that both {@link
+ * #getPasswordComplexity()} and the extra {@link #EXTRA_PASSWORD_COMPLEXITY} require the
+ * calling app to have the permission {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY}.
+ *
+ * <p>If the intent is launched from within a managed profile with a profile
* owner built against {@link android.os.Build.VERSION_CODES#M} or before,
* this will trigger entering a new password for the parent of the profile.
* For all other cases it will trigger entering a new password for the user
@@ -1384,6 +1391,24 @@ public class DevicePolicyManager {
= "android.app.action.SET_NEW_PASSWORD";
/**
+ * An integer indicating the complexity level of the new password an app would like the user to
+ * set when launching the action {@link #ACTION_SET_NEW_PASSWORD}.
+ *
+ * <p>Must be one of
+ * <ul>
+ * <li>{@link #PASSWORD_COMPLEXITY_HIGH}
+ * <li>{@link #PASSWORD_COMPLEXITY_MEDIUM}
+ * <li>{@link #PASSWORD_COMPLEXITY_LOW}
+ * <li>{@link #PASSWORD_COMPLEXITY_NONE}
+ * </ul>
+ *
+ * <p>If an invalid value is used, it will be treated as {@link #PASSWORD_COMPLEXITY_NONE}.
+ */
+ @RequiresPermission(android.Manifest.permission.GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY)
+ public static final String EXTRA_PASSWORD_COMPLEXITY =
+ "android.app.extra.PASSWORD_COMPLEXITY";
+
+ /**
* Constant for {@link #getPasswordComplexity()}: no password.
*
* <p>Note that these complexity constants are ordered so that higher values are more complex.
@@ -2513,6 +2538,9 @@ public class DevicePolicyManager {
* requested quality constant (between the policy set here, the user's preference, and any other
* considerations) is the one that is in effect.
* <p>
+ * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ * <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
@@ -2548,6 +2576,9 @@ public class DevicePolicyManager {
* returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
* restrictions on the parent profile.
*
+ * <p>Note: on devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature,
+ * the password is always treated as empty.
+ *
* @param admin The name of the admin component to check, or {@code null} to aggregate
* all admins.
*/
@@ -2580,6 +2611,9 @@ public class DevicePolicyManager {
* {@link #PASSWORD_QUALITY_ALPHANUMERIC}, or {@link #PASSWORD_QUALITY_COMPLEX} with
* {@link #setPasswordQuality}.
* <p>
+ * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ * <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
@@ -2609,11 +2643,13 @@ public class DevicePolicyManager {
* restrictions on this user and its participating profiles. Restrictions on profiles that have
* a separate challenge are not taken into account.
*
+ * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ *
* <p>This method can be called on the {@link DevicePolicyManager} instance
* returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
* restrictions on the parent profile.
*
- * user and its profiles or a particular one.
* @param admin The name of the admin component to check, or {@code null} to aggregate
* all admins.
*/
@@ -2644,6 +2680,9 @@ public class DevicePolicyManager {
* setting this value. This constraint is only imposed if the administrator has also requested
* {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
* <p>
+ * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ * <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
@@ -2678,6 +2717,9 @@ public class DevicePolicyManager {
* and only applies when the password quality is
* {@link #PASSWORD_QUALITY_COMPLEX}.
*
+ * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ *
* <p>This method can be called on the {@link DevicePolicyManager} instance
* returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
* restrictions on the parent profile.
@@ -2714,6 +2756,9 @@ public class DevicePolicyManager {
* setting this value. This constraint is only imposed if the administrator has also requested
* {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
* <p>
+ * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ * <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
@@ -2748,6 +2793,9 @@ public class DevicePolicyManager {
* and only applies when the password quality is
* {@link #PASSWORD_QUALITY_COMPLEX}.
*
+ * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ *
* <p>This method can be called on the {@link DevicePolicyManager} instance
* returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
* restrictions on the parent profile.
@@ -2784,6 +2832,9 @@ public class DevicePolicyManager {
* only imposed if the administrator has also requested {@link #PASSWORD_QUALITY_COMPLEX} with
* {@link #setPasswordQuality}. The default value is 1.
* <p>
+ * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ * <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
@@ -2818,6 +2869,9 @@ public class DevicePolicyManager {
* and only applies when the password quality is
* {@link #PASSWORD_QUALITY_COMPLEX}.
*
+ * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ *
* <p>This method can be called on the {@link DevicePolicyManager} instance
* returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
* restrictions on the parent profile.
@@ -2853,6 +2907,9 @@ public class DevicePolicyManager {
* setting this value. This constraint is only imposed if the administrator has also requested
* {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 1.
* <p>
+ * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ * <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
@@ -2887,6 +2944,9 @@ public class DevicePolicyManager {
* and only applies when the password quality is
* {@link #PASSWORD_QUALITY_COMPLEX}.
*
+ * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ *
* <p>This method can be called on the {@link DevicePolicyManager} instance
* returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
* restrictions on the parent profile.
@@ -2922,6 +2982,9 @@ public class DevicePolicyManager {
* only imposed if the administrator has also requested {@link #PASSWORD_QUALITY_COMPLEX} with
* {@link #setPasswordQuality}. The default value is 1.
* <p>
+ * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ * <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
@@ -2955,6 +3018,9 @@ public class DevicePolicyManager {
* and only applies when the password quality is
* {@link #PASSWORD_QUALITY_COMPLEX}.
*
+ * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ *
* <p>This method can be called on the {@link DevicePolicyManager} instance
* returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
* restrictions on the parent profile.
@@ -2990,6 +3056,9 @@ public class DevicePolicyManager {
* setting this value. This constraint is only imposed if the administrator has also requested
* {@link #PASSWORD_QUALITY_COMPLEX} with {@link #setPasswordQuality}. The default value is 0.
* <p>
+ * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ * <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
@@ -3024,6 +3093,9 @@ public class DevicePolicyManager {
* and only applies when the password quality is
* {@link #PASSWORD_QUALITY_COMPLEX}.
*
+ * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ *
* <p>This method can be called on the {@link DevicePolicyManager} instance
* returned by {@link #getParentProfileInstance(ComponentName)} in order to retrieve
* restrictions on the parent profile.
@@ -3060,6 +3132,9 @@ public class DevicePolicyManager {
* , {@link #PASSWORD_QUALITY_NUMERIC_COMPLEX} {@link #PASSWORD_QUALITY_ALPHABETIC}, or
* {@link #PASSWORD_QUALITY_ALPHANUMERIC} with {@link #setPasswordQuality}.
* <p>
+ * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ * <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
@@ -3112,6 +3187,7 @@ public class DevicePolicyManager {
* @throws SecurityException if {@code admin} is not an active administrator or {@code admin}
* does not use {@link DeviceAdminInfo#USES_POLICY_EXPIRE_PASSWORD}
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public void setPasswordExpirationTimeout(@NonNull ComponentName admin, long timeout) {
if (mService != null) {
try {
@@ -3136,6 +3212,7 @@ public class DevicePolicyManager {
* @param admin The name of the admin component to check, or {@code null} to aggregate all admins.
* @return The timeout for the given admin or the minimum of all timeouts
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public long getPasswordExpirationTimeout(@Nullable ComponentName admin) {
if (mService != null) {
try {
@@ -3160,6 +3237,7 @@ public class DevicePolicyManager {
* @param admin The name of the admin component to check, or {@code null} to aggregate all admins.
* @return The password expiration time, in milliseconds since epoch.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public long getPasswordExpiration(@Nullable ComponentName admin) {
if (mService != null) {
try {
@@ -3184,12 +3262,14 @@ public class DevicePolicyManager {
* all admins.
* @return The length of the password history
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public int getPasswordHistoryLength(@Nullable ComponentName admin) {
return getPasswordHistoryLength(admin, myUserId());
}
/** @hide per-user version */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public int getPasswordHistoryLength(@Nullable ComponentName admin, int userHandle) {
if (mService != null) {
try {
@@ -3204,10 +3284,16 @@ public class DevicePolicyManager {
/**
* Return the maximum password length that the device supports for a
* particular password quality.
+ * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always empty.
* @param quality The quality being interrogated.
* @return Returns the maximum length that the user can enter.
*/
public int getPasswordMaximumLength(int quality) {
+ PackageManager pm = mContext.getPackageManager();
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ID_ATTESTATION)) {
+ return 0;
+ }
// Kind-of arbitrary.
return 16;
}
@@ -3218,6 +3304,10 @@ public class DevicePolicyManager {
* user and its participating profiles. Restrictions on profiles that have a separate challenge
* are not taken into account. The user must be unlocked in order to perform the check.
* <p>
+ * On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty - i.e. this method will always return false on such
+ * devices, provided any password requirements were set.
+ * <p>
* The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call this method; if it has
* not, a security exception will be thrown.
@@ -3250,6 +3340,9 @@ public class DevicePolicyManager {
* explicitly querying the parent profile screen lock complexity via {@link
* #getParentProfileInstance}.
*
+ * <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
+ * password is always treated as empty.
+ *
* @throws IllegalStateException if the user is not unlocked.
* @throws SecurityException if the calling application does not have the permission
* {@link permission#GET_AND_REQUEST_SCREEN_LOCK_COMPLEXITY}
@@ -3329,6 +3422,7 @@ public class DevicePolicyManager {
* @throws SecurityException if the calling application does not own an active administrator
* that uses {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN}
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public int getCurrentFailedPasswordAttempts() {
return getCurrentFailedPasswordAttempts(myUserId());
}
@@ -3396,6 +3490,7 @@ public class DevicePolicyManager {
* both {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and
* {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public void setMaximumFailedPasswordsForWipe(@NonNull ComponentName admin, int num) {
if (mService != null) {
try {
@@ -3419,12 +3514,14 @@ public class DevicePolicyManager {
* @param admin The name of the admin component to check, or {@code null} to aggregate
* all admins.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public int getMaximumFailedPasswordsForWipe(@Nullable ComponentName admin) {
return getMaximumFailedPasswordsForWipe(admin, myUserId());
}
/** @hide per-user version */
@UnsupportedAppUsage
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public int getMaximumFailedPasswordsForWipe(@Nullable ComponentName admin, int userHandle) {
if (mService != null) {
try {
@@ -3444,6 +3541,7 @@ public class DevicePolicyManager {
* user passed in.
* @hide Used only by Keyguard
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public int getProfileWithMinimumFailedPasswordsForWipe(int userHandle) {
if (mService != null) {
try {
@@ -3514,6 +3612,7 @@ public class DevicePolicyManager {
* that uses {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD}
* @throws IllegalStateException if the calling user is locked or has a managed profile.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public boolean resetPassword(String password, int flags) {
throwIfParentInstance("resetPassword");
if (mService != null) {
@@ -3557,6 +3656,7 @@ public class DevicePolicyManager {
* @throws SecurityException if admin is not a device or profile owner.
* @throws IllegalArgumentException if the supplied token is invalid.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public boolean setResetPasswordToken(ComponentName admin, byte[] token) {
throwIfParentInstance("setResetPasswordToken");
if (mService != null) {
@@ -3576,6 +3676,7 @@ public class DevicePolicyManager {
* @return true if the operation is successful, false otherwise.
* @throws SecurityException if admin is not a device or profile owner.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public boolean clearResetPasswordToken(ComponentName admin) {
throwIfParentInstance("clearResetPasswordToken");
if (mService != null) {
@@ -3596,6 +3697,7 @@ public class DevicePolicyManager {
* @throws SecurityException if admin is not a device or profile owner.
* @throws IllegalStateException if no token has been set.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public boolean isResetPasswordTokenActive(ComponentName admin) {
throwIfParentInstance("isResetPasswordTokenActive");
if (mService != null) {
@@ -3637,6 +3739,7 @@ public class DevicePolicyManager {
* @throws SecurityException if admin is not a device or profile owner.
* @throws IllegalStateException if the provided token is not valid.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public boolean resetPasswordWithToken(@NonNull ComponentName admin, String password,
byte[] token, int flags) {
throwIfParentInstance("resetPassword");
@@ -3742,6 +3845,7 @@ public class DevicePolicyManager {
*
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public void setRequiredStrongAuthTimeout(@NonNull ComponentName admin,
long timeoutMs) {
if (mService != null) {
@@ -3766,12 +3870,14 @@ public class DevicePolicyManager {
* across all participating admins.
* @return The timeout in milliseconds or 0 if not configured for the provided admin.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public long getRequiredStrongAuthTimeout(@Nullable ComponentName admin) {
return getRequiredStrongAuthTimeout(admin, myUserId());
}
/** @hide per-user version */
@UnsupportedAppUsage
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public long getRequiredStrongAuthTimeout(@Nullable ComponentName admin, @UserIdInt int userId) {
if (mService != null) {
try {
@@ -5350,6 +5456,7 @@ public class DevicePolicyManager {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public void setActivePasswordState(PasswordMetrics metrics, int userHandle) {
if (mService != null) {
try {
@@ -5363,6 +5470,7 @@ public class DevicePolicyManager {
/**
* @hide
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public void reportPasswordChanged(@UserIdInt int userId) {
if (mService != null) {
try {
@@ -5377,6 +5485,7 @@ public class DevicePolicyManager {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public void reportFailedPasswordAttempt(int userHandle) {
if (mService != null) {
try {
@@ -5391,6 +5500,7 @@ public class DevicePolicyManager {
* @hide
*/
@UnsupportedAppUsage
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public void reportSuccessfulPasswordAttempt(int userHandle) {
if (mService != null) {
try {
@@ -5404,6 +5514,7 @@ public class DevicePolicyManager {
/**
* @hide
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public void reportFailedBiometricAttempt(int userHandle) {
if (mService != null) {
try {
@@ -5417,6 +5528,7 @@ public class DevicePolicyManager {
/**
* @hide
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public void reportSuccessfulBiometricAttempt(int userHandle) {
if (mService != null) {
try {
@@ -6383,6 +6495,7 @@ public class DevicePolicyManager {
* @throws SecurityException if {@code admin} is not an active administrator or does not use
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES}
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public void setTrustAgentConfiguration(@NonNull ComponentName admin,
@NonNull ComponentName target, PersistableBundle configuration) {
if (mService != null) {
@@ -6412,6 +6525,7 @@ public class DevicePolicyManager {
* @param agent Which component to get enabled features for.
* @return configuration for the given trust agent.
*/
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public @Nullable List<PersistableBundle> getTrustAgentConfiguration(
@Nullable ComponentName admin, @NonNull ComponentName agent) {
return getTrustAgentConfiguration(admin, agent, myUserId());
@@ -6419,6 +6533,7 @@ public class DevicePolicyManager {
/** @hide per-user version */
@UnsupportedAppUsage
+ @RequiresFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN)
public @Nullable List<PersistableBundle> getTrustAgentConfiguration(
@Nullable ComponentName admin, @NonNull ComponentName agent, int userHandle) {
if (mService != null) {
@@ -9403,12 +9518,18 @@ public class DevicePolicyManager {
}
/**
- * Allows the device owner to enable or disable the backup service.
+ * Allows the device owner or profile owner to enable or disable the backup service.
*
- * <p> Backup service manages all backup and restore mechanisms on the device. Setting this to
- * false will prevent data from being backed up or restored.
+ * <p> Each user has its own backup service which manages the backup and restore mechanisms in
+ * that user. Disabling the backup service will prevent data from being backed up or restored.
*
- * <p> Backup service is off by default when device owner is present.
+ * <p> Device owner calls this API to control backup services across all users on the device.
+ * Profile owner can use this API to enable or disable the profile's backup service. However,
+ * for a managed profile its backup functionality is only enabled if both the device owner
+ * and the profile owner have enabled the backup service.
+ *
+ * <p> By default, backup service is disabled on a device with device owner, and within a
+ * managed profile.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param enabled {@code true} to enable the backup service, {@code false} to disable it.
@@ -9424,7 +9545,12 @@ public class DevicePolicyManager {
}
/**
- * Return whether the backup service is enabled by the device owner.
+ * Return whether the backup service is enabled by the device owner or profile owner for the
+ * current user, as previously set by {@link #setBackupServiceEnabled(ComponentName, boolean)}.
+ *
+ * <p> Whether the backup functionality is actually enabled or not depends on settings from both
+ * the current user and the device owner, please see
+ * {@link #setBackupServiceEnabled(ComponentName, boolean)} for details.
*
* <p> Backup service manages all backup and restore mechanisms on the device.
*
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 8b41755f6dec..e5df2c70eeec 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -20,6 +20,11 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+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_UNSPECIFIED;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -27,6 +32,8 @@ import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -85,6 +92,101 @@ public class PasswordMetrics implements Parcelable {
nonLetter = in.readInt();
}
+ /** Returns the min quality allowed by {@code complexityLevel}. */
+ public static int complexityLevelToMinQuality(@PasswordComplexity int complexityLevel) {
+ // this would be the quality of the first metrics since mMetrics is sorted in ascending
+ // order of quality
+ return PasswordComplexityBucket
+ .complexityLevelToBucket(complexityLevel).mMetrics[0].quality;
+ }
+
+ /**
+ * Returns a merged minimum {@link PasswordMetrics} requirements that a new password must meet
+ * to fulfil {@code requestedQuality}, {@code requiresNumeric} and {@code
+ * requiresLettersOrSymbols}, which are derived from {@link DevicePolicyManager} requirements,
+ * and {@code complexityLevel}.
+ *
+ * <p>Note that we are taking {@code userEnteredPasswordQuality} into account because there are
+ * more than one set of metrics to meet the minimum complexity requirement and inspecting what
+ * the user has entered can help determine whether the alphabetic or alphanumeric set of metrics
+ * should be used. For example, suppose minimum complexity requires either ALPHABETIC(8+), or
+ * ALPHANUMERIC(6+). If the user has entered "a", the length requirement displayed on the UI
+ * would be 8. Then the user appends "1" to make it "a1". We now know the user is entering
+ * an alphanumeric password so we would update the min complexity required min length to 6.
+ */
+ public static PasswordMetrics getMinimumMetrics(@PasswordComplexity int complexityLevel,
+ int userEnteredPasswordQuality, int requestedQuality, boolean requiresNumeric,
+ boolean requiresLettersOrSymbols) {
+ int targetQuality = Math.max(
+ userEnteredPasswordQuality,
+ getActualRequiredQuality(
+ requestedQuality, requiresNumeric, requiresLettersOrSymbols));
+ return getTargetQualityMetrics(complexityLevel, targetQuality);
+ }
+
+ /**
+ * Returns the {@link PasswordMetrics} at {@code complexityLevel} which the metrics quality
+ * is the same as {@code targetQuality}.
+ *
+ * <p>If {@code complexityLevel} does not allow {@code targetQuality}, returns the metrics
+ * with the min quality at {@code complexityLevel}.
+ */
+ // TODO(bernardchau): update tests to test getMinimumMetrics and change this to be private
+ @VisibleForTesting
+ public static PasswordMetrics getTargetQualityMetrics(
+ @PasswordComplexity int complexityLevel, int targetQuality) {
+ PasswordComplexityBucket targetBucket =
+ PasswordComplexityBucket.complexityLevelToBucket(complexityLevel);
+ for (PasswordMetrics metrics : targetBucket.mMetrics) {
+ if (targetQuality == metrics.quality) {
+ return metrics;
+ }
+ }
+ // none of the metrics at complexityLevel has targetQuality, return metrics with min quality
+ // see test case testGetMinimumMetrics_actualRequiredQualityStricter for an example, where
+ // min complexity allows at least NUMERIC_COMPLEX, user has not entered anything yet, and
+ // requested quality is NUMERIC
+ return targetBucket.mMetrics[0];
+ }
+
+ /**
+ * Finds out the actual quality requirement based on whether quality is {@link
+ * DevicePolicyManager#PASSWORD_QUALITY_COMPLEX} and whether digits, letters or symbols are
+ * required.
+ */
+ @VisibleForTesting
+ // TODO(bernardchau): update tests to test getMinimumMetrics and change this to be private
+ public static int getActualRequiredQuality(
+ int requestedQuality, boolean requiresNumeric, boolean requiresLettersOrSymbols) {
+ if (requestedQuality != PASSWORD_QUALITY_COMPLEX) {
+ return requestedQuality;
+ }
+
+ // find out actual password quality from complex requirements
+ if (requiresNumeric && requiresLettersOrSymbols) {
+ return PASSWORD_QUALITY_ALPHANUMERIC;
+ }
+ if (requiresLettersOrSymbols) {
+ return PASSWORD_QUALITY_ALPHABETIC;
+ }
+ if (requiresNumeric) {
+ // cannot specify numeric complex using complex quality so this must be numeric
+ return PASSWORD_QUALITY_NUMERIC;
+ }
+
+ // reaching here means dpm sets quality to complex without specifying any requirements
+ return PASSWORD_QUALITY_UNSPECIFIED;
+ }
+
+ /**
+ * Returns {@code complexityLevel} or {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}
+ * if {@code complexityLevel} is not valid.
+ */
+ @PasswordComplexity
+ public static int sanitizeComplexityLevel(@PasswordComplexity int complexityLevel) {
+ return PasswordComplexityBucket.complexityLevelToBucket(complexityLevel).mComplexityLevel;
+ }
+
public boolean isDefault() {
return quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
&& length == 0 && letters == 0 && upperCase == 0 && lowerCase == 0
@@ -280,7 +382,7 @@ public class PasswordMetrics implements Parcelable {
@PasswordComplexity
public int determineComplexity() {
for (PasswordComplexityBucket bucket : PasswordComplexityBucket.BUCKETS) {
- if (satisfiesBucket(bucket.getMetrics())) {
+ if (satisfiesBucket(bucket.mMetrics)) {
return bucket.mComplexityLevel;
}
}
@@ -290,7 +392,7 @@ public class PasswordMetrics implements Parcelable {
/**
* Requirements in terms of {@link PasswordMetrics} for each {@link PasswordComplexity}.
*/
- public static class PasswordComplexityBucket {
+ private static class PasswordComplexityBucket {
/**
* Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_HIGH} in terms of
* {@link PasswordMetrics}.
@@ -299,12 +401,13 @@ public class PasswordMetrics implements Parcelable {
new PasswordComplexityBucket(
PASSWORD_COMPLEXITY_HIGH,
new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 6),
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
+ 8),
new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 6),
new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
- 8));
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */
+ 6));
/**
* Definition of {@link DevicePolicyManager#PASSWORD_COMPLEXITY_MEDIUM} in terms of
@@ -314,11 +417,12 @@ public class PasswordMetrics implements Parcelable {
new PasswordComplexityBucket(
PASSWORD_COMPLEXITY_MEDIUM,
new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */ 4),
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
+ 4),
new PasswordMetrics(
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, /* length= */ 4),
new PasswordMetrics(
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, /* length= */
+ DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, /* length= */
4));
/**
@@ -328,11 +432,11 @@ public class PasswordMetrics implements Parcelable {
private static final PasswordComplexityBucket LOW =
new PasswordComplexityBucket(
PASSWORD_COMPLEXITY_LOW,
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC),
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING),
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC),
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING));
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC),
+ new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC));
/**
* A special bucket to represent {@link DevicePolicyManager#PASSWORD_COMPLEXITY_NONE}.
@@ -348,19 +452,27 @@ public class PasswordMetrics implements Parcelable {
private final int mComplexityLevel;
private final PasswordMetrics[] mMetrics;
+ /**
+ * @param metricsArray must be sorted in ascending order of {@link #quality}.
+ */
private PasswordComplexityBucket(@PasswordComplexity int complexityLevel,
- PasswordMetrics... metrics) {
+ PasswordMetrics... metricsArray) {
+ int previousQuality = PASSWORD_QUALITY_UNSPECIFIED;
+ for (PasswordMetrics metrics : metricsArray) {
+ if (metrics.quality < previousQuality) {
+ throw new IllegalArgumentException("metricsArray must be sorted in ascending"
+ + " order of quality");
+ }
+ previousQuality = metrics.quality;
+ }
+
+ this.mMetrics = metricsArray;
this.mComplexityLevel = complexityLevel;
- this.mMetrics = metrics;
- }
- /** Returns the {@link PasswordMetrics} that meet the min requirements of this bucket. */
- public PasswordMetrics[] getMetrics() {
- return mMetrics;
}
/** Returns the bucket that {@code complexityLevel} represents. */
- public static PasswordComplexityBucket complexityLevelToBucket(
+ private static PasswordComplexityBucket complexityLevelToBucket(
@PasswordComplexity int complexityLevel) {
for (PasswordComplexityBucket bucket : BUCKETS) {
if (bucket.mComplexityLevel == complexityLevel) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 1945b2fd51de..97bc0796e9ce 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -36,6 +36,7 @@ import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.BatteryStats;
import android.os.Binder;
+import android.os.Handler;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -648,6 +649,32 @@ public final class BluetoothAdapter {
private final Object mLock = new Object();
private final Map<LeScanCallback, ScanCallback> mLeScanClients;
+ private static final Map<BluetoothDevice, List<Pair<MetadataListener, Handler>>>
+ sMetadataListeners = new HashMap<>();
+
+ /**
+ * Bluetooth metadata listener. Overrides the default BluetoothMetadataListener
+ * implementation.
+ */
+ private static final IBluetoothMetadataListener sBluetoothMetadataListener =
+ new IBluetoothMetadataListener.Stub() {
+ @Override
+ public void onMetadataChanged(BluetoothDevice device, int key, String value) {
+ synchronized (sMetadataListeners) {
+ if (sMetadataListeners.containsKey(device)) {
+ List<Pair<MetadataListener, Handler>> list = sMetadataListeners.get(device);
+ for (Pair<MetadataListener, Handler> pair : list) {
+ MetadataListener listener = pair.first;
+ Handler handler = pair.second;
+ handler.post(() -> {
+ listener.onMetadataChanged(device, key, value);
+ });
+ }
+ }
+ }
+ return;
+ }
+ };
/**
* Get a handle to the default local Bluetooth adapter.
@@ -2607,6 +2634,16 @@ public final class BluetoothAdapter {
}
}
}
+ synchronized (sMetadataListeners) {
+ sMetadataListeners.forEach((device, pair) -> {
+ try {
+ mService.registerMetadataListener(sBluetoothMetadataListener,
+ device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register metadata listener", e);
+ }
+ });
+ }
}
public void onBluetoothServiceDown() {
@@ -3090,4 +3127,142 @@ public final class BluetoothAdapter {
+ "listenUsingInsecureL2capChannel");
return listenUsingInsecureL2capChannel();
}
+
+ /**
+ * Register a {@link #MetadataListener} to receive update about metadata
+ * changes for this {@link BluetoothDevice}.
+ * Registration must be done when Bluetooth is ON and will last until
+ * {@link #unregisterMetadataListener(BluetoothDevice)} is called, even when Bluetooth
+ * restarted in the middle.
+ * All input parameters should not be null or {@link NullPointerException} will be triggered.
+ * The same {@link BluetoothDevice} and {@link #MetadataListener} pair can only be registered
+ * once, double registration would cause {@link IllegalArgumentException}.
+ *
+ * @param device {@link BluetoothDevice} that will be registered
+ * @param listener {@link #MetadataListener} that will receive asynchronous callbacks
+ * @param handler the handler for listener callback
+ * @return true on success, false on error
+ * @throws NullPointerException If one of {@code listener}, {@code device} or {@code handler}
+ * is null.
+ * @throws IllegalArgumentException The same {@link #MetadataListener} and
+ * {@link BluetoothDevice} are registered twice.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean registerMetadataListener(BluetoothDevice device, MetadataListener listener,
+ Handler handler) {
+ if (DBG) Log.d(TAG, "registerMetdataListener()");
+
+ final IBluetooth service = mService;
+ if (service == null) {
+ Log.e(TAG, "Bluetooth is not enabled. Cannot register metadata listener");
+ return false;
+ }
+ if (listener == null) {
+ throw new NullPointerException("listener is null");
+ }
+ if (device == null) {
+ throw new NullPointerException("device is null");
+ }
+ if (handler == null) {
+ throw new NullPointerException("handler is null");
+ }
+
+ synchronized (sMetadataListeners) {
+ List<Pair<MetadataListener, Handler>> listenerList = sMetadataListeners.get(device);
+ if (listenerList == null) {
+ // Create new listener/handler list for registeration
+ listenerList = new ArrayList<>();
+ sMetadataListeners.put(device, listenerList);
+ } else {
+ // Check whether this device was already registed by the lisenter
+ if (listenerList.stream().anyMatch((pair) -> (pair.first.equals(listener)))) {
+ throw new IllegalArgumentException("listener was already regestered"
+ + " for the device");
+ }
+ }
+
+ Pair<MetadataListener, Handler> listenerPair = new Pair(listener, handler);
+ listenerList.add(listenerPair);
+
+ boolean ret = false;
+ try {
+ ret = service.registerMetadataListener(sBluetoothMetadataListener, device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "registerMetadataListener fail", e);
+ } finally {
+ if (!ret) {
+ // Remove listener registered earlier when fail.
+ listenerList.remove(listenerPair);
+ if (listenerList.isEmpty()) {
+ // Remove the device if its listener list is empty
+ sMetadataListeners.remove(device);
+ }
+ }
+ }
+ return ret;
+ }
+ }
+
+ /**
+ * Unregister all {@link MetadataListener} from this {@link BluetoothDevice}.
+ * Unregistration can be done when Bluetooth is either ON or OFF.
+ * {@link #registerMetadataListener(MetadataListener, BluetoothDevice, Handler)} must
+ * be called before unregisteration.
+ * Unregistering a device that is not regestered would cause {@link IllegalArgumentException}.
+ *
+ * @param device {@link BluetoothDevice} that will be unregistered. it
+ * should not be null or {@link NullPointerException} will be triggered.
+ * @return true on success, false on error
+ * @throws NullPointerException If {@code device} is null.
+ * @throws IllegalArgumentException If {@code device} has not been registered before.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean unregisterMetadataListener(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "unregisterMetdataListener()");
+ if (device == null) {
+ throw new NullPointerException("device is null");
+ }
+
+ synchronized (sMetadataListeners) {
+ if (sMetadataListeners.containsKey(device)) {
+ sMetadataListeners.remove(device);
+ } else {
+ throw new IllegalArgumentException("device was not registered");
+ }
+
+ final IBluetooth service = mService;
+ if (service == null) {
+ // Bluetooth is OFF, do nothing to Bluetooth service.
+ return true;
+ }
+ try {
+ return service.unregisterMetadataListener(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "unregisterMetadataListener fail", e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * This abstract class is used to implement {@link BluetoothAdapter} metadata listener.
+ * @hide
+ */
+ @SystemApi
+ public abstract class MetadataListener {
+ /**
+ * Callback triggered if the metadata of {@link BluetoothDevice} registered in
+ * {@link #registerMetadataListener}.
+ *
+ * @param device changed {@link BluetoothDevice}.
+ * @param key changed metadata key, one of BluetoothDevice.METADATA_*.
+ * @param value the new value of metadata.
+ */
+ public void onMetadataChanged(BluetoothDevice device, int key, String value) {
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 235dc5c59c2a..17cf702bbdea 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -341,6 +341,137 @@ public final class BluetoothDevice implements Parcelable {
"android.bluetooth.device.action.SDP_RECORD";
/**
+ * Maximum length of a metadata entry, this is to avoid exploding Bluetooth
+ * disk usage
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_MAX_LENGTH = 2048;
+
+ /**
+ * Manufacturer name of this Bluetooth device
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_MANUFACTURER_NAME = 0;
+
+ /**
+ * Model name of this Bluetooth device
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_MODEL_NAME = 1;
+
+ /**
+ * Software version of this Bluetooth device
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_SOFTWARE_VERSION = 2;
+
+ /**
+ * Hardware version of this Bluetooth device
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_HARDWARE_VERSION = 3;
+
+ /**
+ * Package name of the companion app, if any
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_COMPANION_APP = 4;
+
+ /**
+ * URI to the main icon shown on the settings UI
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_MAIN_ICON = 5;
+
+ /**
+ * Whether this device is an untethered headset with left, right and case
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_IS_UNTHETHERED_HEADSET = 6;
+
+ /**
+ * URI to icon of the left headset
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTHETHERED_LEFT_ICON = 7;
+
+ /**
+ * URI to icon of the right headset
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTHETHERED_RIGHT_ICON = 8;
+
+ /**
+ * URI to icon of the headset charging case
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTHETHERED_CASE_ICON = 9;
+
+ /**
+ * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
+ * is invalid, of the left headset
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTHETHERED_LEFT_BATTERY = 10;
+
+ /**
+ * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
+ * is invalid, of the right headset
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTHETHERED_RIGHT_BATTERY = 11;
+
+ /**
+ * Battery level (0-100), {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
+ * is invalid, of the headset charging case
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTHETHERED_CASE_BATTERY = 12;
+
+ /**
+ * Whether the left headset is charging
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTHETHERED_LEFT_CHARGING = 13;
+
+ /**
+ * Whether the right headset is charging
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTHETHERED_RIGHT_CHARGING = 14;
+
+ /**
+ * Whether the headset charging case is charging
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_UNTHETHERED_CASE_CHARGING = 15;
+
+ /**
+ * URI to the enhanced settings UI slice, null or empty String means
+ * the UI does not exist
+ * @hide
+ */
+ @SystemApi
+ public static final int METADATA_ENHANCED_SETTINGS_UI_URI = 16;
+
+ /**
* Broadcast Action: This intent is used to broadcast the {@link UUID}
* wrapped as a {@link android.os.ParcelUuid} of the remote device after it
* has been fetched. This intent is sent only when the UUIDs of the remote
@@ -2026,4 +2157,61 @@ public final class BluetoothDevice implements Parcelable {
Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel");
return createInsecureL2capChannel(psm);
}
+
+ /**
+ * Set a keyed metadata of this {@link BluetoothDevice} to a
+ * {@link String} value.
+ * Only bonded devices's metadata will be persisted across Bluetooth
+ * restart.
+ * Metadata will be removed when the device's bond state is moved to
+ * {@link #BOND_NONE}.
+ *
+ * @param key must be within the list of BluetoothDevice.METADATA_*
+ * @param value the string data to set for key. Must be less than
+ * {@link BluetoothAdapter#METADATA_MAX_LENGTH} characters in length
+ * @return true on success, false on error
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean setMetadata(int key, String value) {
+ final IBluetooth service = sService;
+ if (service == null) {
+ Log.e(TAG, "Bluetooth is not enabled. Cannot set metadata");
+ return false;
+ }
+ if (value.length() > METADATA_MAX_LENGTH) {
+ throw new IllegalArgumentException("value length is " + value.length()
+ + ", should not over " + METADATA_MAX_LENGTH);
+ }
+ try {
+ return service.setMetadata(this, key, value);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setMetadata fail", e);
+ return false;
+ }
+ }
+
+ /**
+ * Get a keyed metadata for this {@link BluetoothDevice} as {@link String}
+ *
+ * @param key must be within the list of BluetoothDevice.METADATA_*
+ * @return Metadata of the key as string, null on error or not found
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public String getMetadata(int key) {
+ final IBluetooth service = sService;
+ if (service == null) {
+ Log.e(TAG, "Bluetooth is not enabled. Cannot get metadata");
+ return null;
+ }
+ try {
+ return service.getMetadata(this, key);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getMetadata fail", e);
+ return null;
+ }
+ }
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cefc700d372a..280f1ac9c067 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -771,7 +771,9 @@ public abstract class Context {
* <p>
* This is not generally intended for third party application developers.
*/
- public abstract String getOpPackageName();
+ public String getOpPackageName() {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
/** Return the full application info for this context's package. */
public abstract ApplicationInfo getApplicationInfo();
@@ -2980,9 +2982,11 @@ public abstract class Context {
*
* @see #bindService
*/
- public abstract boolean bindIsolatedService(@RequiresPermission Intent service,
+ public boolean bindIsolatedService(@RequiresPermission Intent service,
@NonNull ServiceConnection conn, @BindServiceFlags int flags,
- @NonNull String instanceName);
+ @NonNull String instanceName) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
/**
* Same as {@link #bindService(Intent, ServiceConnection, int)}, but with an explicit userHandle
@@ -3037,8 +3041,10 @@ public abstract class Context {
* a related groups -- higher importance values will be killed before
* lower ones.
*/
- public abstract void updateServiceGroup(@NonNull ServiceConnection conn, int group,
- int importance);
+ public void updateServiceGroup(@NonNull ServiceConnection conn, int group,
+ int importance) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
/**
* Disconnect from an application service. You will no longer receive
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8497656df2be..a6b47ff1320d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2008,6 +2008,15 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.extra.PERMISSION_GROUP_NAME";
/**
+ * Intent extra: The number of milliseconds.
+ * <p>
+ * Type: long
+ * </p>
+ */
+ public static final String EXTRA_DURATION_MILLIS =
+ "android.intent.extra.DURATION_MILLIS";
+
+ /**
* Activity action: Launch UI to review app uses of permissions.
* <p>
* Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission name
@@ -2020,11 +2029,16 @@ public class Intent implements Parcelable, Cloneable {
* {@link #EXTRA_PERMISSION_NAME}.
* </p>
* <p>
+ * Input: {@link #EXTRA_DURATION_MILLIS} specifies the minimum number of milliseconds of recent
+ * activity to show (optional). Must be non-negative.
+ * </p>
+ * <p>
* Output: Nothing.
* </p>
*
* @see #EXTRA_PERMISSION_NAME
* @see #EXTRA_PERMISSION_GROUP_NAME
+ * @see #EXTRA_DURATION_MILLIS
*
* @hide
*/
@@ -3192,7 +3206,18 @@ public class Intent implements Parcelable, Cloneable {
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
+ *
+ * <p class="note">If the user has chosen a {@link android.telecom.CallRedirectionService} to
+ * handle redirection of outgoing calls, this intent will NOT be sent as an ordered broadcast.
+ * This means that attempts to re-write the outgoing call by other apps using this intent will
+ * be ignored.
+ * </p>
+ *
+ * @deprecated Apps that redirect outgoing calls should use the
+ * {@link android.telecom.CallRedirectionService} API. Apps that perform call screening
+ * should use the {@link android.telecom.CallScreeningService} API.
*/
+ @Deprecated
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_NEW_OUTGOING_CALL =
"android.intent.action.NEW_OUTGOING_CALL";
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 7a2220bfddb6..8e72fa5e1cfd 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -54,6 +54,7 @@ public class OverlayManager {
this(context, IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE)));
}
+
/**
* Request that an overlay package is enabled and any other overlay packages with the same
* target package and category are disabled.
@@ -75,6 +76,26 @@ public class OverlayManager {
}
/**
+ * Request that an overlay package is enabled.
+ *
+ * @param packageName the name of the overlay package to enable.
+ * @param enable {@code false} if the overlay should be turned off.
+ * @param userId The user for which to change the overlay.
+ * @return true if the system successfully registered the request, false otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean setEnabled(@Nullable final String packageName, final boolean enable,
+ int userId) {
+ try {
+ return mService.setEnabled(packageName, enable, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns information about all overlays for the given target package for
* the specified user. The returned list is ordered according to the
* overlay priority with the highest priority at the end of the list.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e73322cc86ca..9e2f31684395 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2060,6 +2060,14 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device has a secure implementation of keyguard, meaning the
+ * device supports PIN, pattern and password as defined in Android CDD
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_SECURE_LOCK_SCREEN = "android.software.secure_lock_screen";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device includes an accelerometer.
*/
@SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index c7320b0d6ccf..c9a4c8270390 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -794,6 +794,12 @@ public abstract class PackageManagerInternal {
"android.content.pm.extra.ENABLE_ROLLBACK_INSTALL_FLAGS";
/**
+ * Extra field name for the set of installed users for a given rollback package.
+ */
+ public static final String EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS =
+ "android.content.pm.extra.ENABLE_ROLLBACK_INSTALLED_USERS";
+
+ /**
* Used as the {@code enableRollbackCode} argument for
* {@link PackageManagerInternal#setEnableRollbackCode} to indicate that
* enabling rollback succeeded.
@@ -827,4 +833,10 @@ public abstract class PackageManagerInternal {
* Ask the package manager to compile layouts in the given package.
*/
public abstract boolean compileLayouts(String packageName);
+
+ /*
+ * Inform the package manager that the pending package install identified by
+ * {@code token} can be completed.
+ */
+ public abstract void finishPackageInstall(int token, boolean didLaunch);
}
diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl
index 7f557cd8bbe8..420bcb69e0c4 100644
--- a/core/java/android/content/rollback/IRollbackManager.aidl
+++ b/core/java/android/content/rollback/IRollbackManager.aidl
@@ -33,6 +33,12 @@ interface IRollbackManager {
void executeRollback(in RollbackInfo rollback, String callerPackageName,
in IntentSender statusReceiver);
+ // Exposed for use from the system server only. Callback from the package
+ // manager during the install flow when user data can be restored for a given
+ // package.
+ void restoreUserData(String packageName, int userId, int appId, long ceDataInode,
+ String seInfo, int token);
+
// Exposed for test purposes only.
void reloadPersistedData();
diff --git a/core/java/android/hardware/display/ColorDisplayManager.java b/core/java/android/hardware/display/ColorDisplayManager.java
index 70a9f08ea58a..fa335c8b9924 100644
--- a/core/java/android/hardware/display/ColorDisplayManager.java
+++ b/core/java/android/hardware/display/ColorDisplayManager.java
@@ -17,6 +17,7 @@
package android.hardware.display;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -30,6 +31,9 @@ import android.os.ServiceManager.ServiceNotFoundException;
import com.android.internal.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Manages the display's color transforms and modes.
*
@@ -39,6 +43,44 @@ import com.android.internal.R;
@SystemService(Context.COLOR_DISPLAY_SERVICE)
public final class ColorDisplayManager {
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CAPABILITY_NONE, CAPABILITY_PROTECTED_CONTENT, CAPABILITY_HARDWARE_ACCELERATION_GLOBAL,
+ CAPABILITY_HARDWARE_ACCELERATION_PER_APP})
+ public @interface CapabilityType {}
+
+ /**
+ * The device does not support color transforms.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CAPABILITY_NONE = 0x0;
+ /**
+ * The device can properly apply transforms over protected content.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CAPABILITY_PROTECTED_CONTENT = 0x1;
+ /**
+ * The device's hardware can efficiently apply transforms to the entire display.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CAPABILITY_HARDWARE_ACCELERATION_GLOBAL = 0x2;
+ /**
+ * The device's hardware can efficiently apply transforms to a specific Surface (window) so
+ * that apps can be transformed independently of one another.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int CAPABILITY_HARDWARE_ACCELERATION_PER_APP = 0x4;
+
private final ColorDisplayManagerInternal mManager;
/**
@@ -114,6 +156,17 @@ public final class ColorDisplayManager {
return context.getResources().getBoolean(R.bool.config_setColorTransformAccelerated);
}
+ /**
+ * Returns the available software and hardware color transform capabilities of this device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS)
+ public @CapabilityType int getTransformCapabilities() {
+ return mManager.getTransformCapabilities();
+ }
+
private static class ColorDisplayManagerInternal {
private static ColorDisplayManagerInternal sInstance;
@@ -162,5 +215,13 @@ public final class ColorDisplayManager {
throw e.rethrowFromSystemServer();
}
}
+
+ int getTransformCapabilities() {
+ try {
+ return mCdm.getTransformCapabilities();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
}
diff --git a/core/java/android/hardware/display/IColorDisplayManager.aidl b/core/java/android/hardware/display/IColorDisplayManager.aidl
index 644f510d45f9..53cb8db8cc3d 100644
--- a/core/java/android/hardware/display/IColorDisplayManager.aidl
+++ b/core/java/android/hardware/display/IColorDisplayManager.aidl
@@ -22,4 +22,6 @@ interface IColorDisplayManager {
boolean setSaturationLevel(int saturationLevel);
boolean setAppSaturationLevel(String packageName, int saturationLevel);
+
+ int getTransformCapabilities();
} \ No newline at end of file
diff --git a/core/java/android/hardware/hdmi/HdmiUtils.java b/core/java/android/hardware/hdmi/HdmiUtils.java
index 308173816f13..8c94b7841a80 100644
--- a/core/java/android/hardware/hdmi/HdmiUtils.java
+++ b/core/java/android/hardware/hdmi/HdmiUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -16,14 +16,18 @@
package android.hardware.hdmi;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
- * Various utilities to handle HDMI CEC messages.
+ * Various utilities related to HDMI CEC.
*
* TODO(b/110094868): unhide for Q
* @hide
*/
-public class HdmiUtils {
-
+public final class HdmiUtils {
/**
* Return value of {@link #getLocalPortFromPhysicalAddress(int, int)}
*/
@@ -78,4 +82,164 @@ public class HdmiUtils {
}
return port;
}
+
+ /**
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({HDMI_RELATIVE_POSITION_UNKNOWN, HDMI_RELATIVE_POSITION_DIRECTLY_BELOW,
+ HDMI_RELATIVE_POSITION_BELOW, HDMI_RELATIVE_POSITION_SAME,
+ HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE, HDMI_RELATIVE_POSITION_ABOVE,
+ HDMI_RELATIVE_POSITION_SIBLING, HDMI_RELATIVE_POSITION_DIFFERENT_BRANCH})
+ public @interface HdmiAddressRelativePosition {}
+ /**
+ * HDMI relative position is not determined.
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ public static final int HDMI_RELATIVE_POSITION_UNKNOWN = 0;
+ /**
+ * HDMI relative position: directly blow the device.
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ public static final int HDMI_RELATIVE_POSITION_DIRECTLY_BELOW = 1;
+ /**
+ * HDMI relative position: indirectly below the device.
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ public static final int HDMI_RELATIVE_POSITION_BELOW = 2;
+ /**
+ * HDMI relative position: the same device.
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ public static final int HDMI_RELATIVE_POSITION_SAME = 3;
+ /**
+ * HDMI relative position: directly above the device.
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ public static final int HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE = 4;
+ /**
+ * HDMI relative position: indirectly above the device.
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ public static final int HDMI_RELATIVE_POSITION_ABOVE = 5;
+ /**
+ * HDMI relative position: directly below a same device.
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ public static final int HDMI_RELATIVE_POSITION_SIBLING = 6;
+ /**
+ * HDMI relative position: different branch.
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ public static final int HDMI_RELATIVE_POSITION_DIFFERENT_BRANCH = 7;
+
+ private static final int NPOS = -1;
+
+ /**
+ * Check if the given physical address is valid.
+ *
+ * @param address physical address
+ * @return {@code true} if the given address is valid
+ */
+ public static boolean isValidPhysicalAddress(int address) {
+ if (address < 0 || address >= 0xFFFF) {
+ return false;
+ }
+ int mask = 0xF000;
+ boolean hasZero = false;
+ for (int i = 0; i < 4; i++) {
+ if ((address & mask) == 0) {
+ hasZero = true;
+ } else if (hasZero) {
+ // only 0s are valid after a 0.
+ // e.g. 0x1012 is not valid.
+ return false;
+ }
+ mask >>= 4;
+ }
+ return true;
+ }
+
+
+ /**
+ * Returns the relative position of two physical addresses.
+ */
+ @HdmiAddressRelativePosition
+ public static int getHdmiAddressRelativePosition(int src, int dest) {
+ if (src == 0xFFFF || dest == 0xFFFF) {
+ // address not assigned
+ return HDMI_RELATIVE_POSITION_UNKNOWN;
+ }
+ try {
+ int firstDiffPos = physicalAddressFirstDifferentDigitPos(src, dest);
+ if (firstDiffPos == NPOS) {
+ return HDMI_RELATIVE_POSITION_SAME;
+ }
+ int mask = (0xF000 >> (firstDiffPos * 4));
+ int nextPos = firstDiffPos + 1;
+ if ((src & mask) == 0) {
+ // src is above dest
+ if (nextPos == 4) {
+ // last digits are different
+ return HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE;
+ }
+ if (((0xF000 >> (nextPos * 4)) & dest) == 0) {
+ // next digit is 0
+ return HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE;
+ }
+ return HDMI_RELATIVE_POSITION_ABOVE;
+ }
+
+ if ((dest & mask) == 0) {
+ // src is below dest
+ if (nextPos == 4) {
+ // last digits are different
+ return HDMI_RELATIVE_POSITION_DIRECTLY_BELOW;
+ }
+ if (((0xF000 >> (nextPos * 4)) & src) == 0) {
+ // next digit is 0
+ return HDMI_RELATIVE_POSITION_DIRECTLY_BELOW;
+ }
+ return HDMI_RELATIVE_POSITION_BELOW;
+ }
+ if (nextPos == 4) {
+ // last digits are different
+ return HDMI_RELATIVE_POSITION_SIBLING;
+ }
+ if (((0xF000 >> (nextPos * 4)) & src) == 0 && ((0xF000 >> (nextPos * 4)) & dest) == 0) {
+ return HDMI_RELATIVE_POSITION_SIBLING;
+ }
+ return HDMI_RELATIVE_POSITION_DIFFERENT_BRANCH;
+ } catch (IllegalArgumentException e) {
+ // invalid address
+ return HDMI_RELATIVE_POSITION_UNKNOWN;
+ }
+ }
+
+ private static int physicalAddressFirstDifferentDigitPos(int address1, int address2)
+ throws IllegalArgumentException {
+ if (!isValidPhysicalAddress(address1)) {
+ throw new IllegalArgumentException(address1 + " is not a valid address.");
+ }
+ if (!isValidPhysicalAddress(address2)) {
+ throw new IllegalArgumentException(address2 + " is not a valid address.");
+ }
+ int mask = 0xF000;
+ for (int i = 0; i < 4; i++) {
+ if ((address1 & mask) != (address2 & mask)) {
+ return i;
+ }
+ mask = mask >> 4;
+ }
+ return NPOS;
+ }
}
diff --git a/core/java/android/hardware/location/ActivityChangedEvent.aidl b/core/java/android/hardware/location/ActivityChangedEvent.aidl
new file mode 100644
index 000000000000..21f24459ac85
--- /dev/null
+++ b/core/java/android/hardware/location/ActivityChangedEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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
+ */
+
+package android.hardware.location;
+
+parcelable ActivityChangedEvent; \ No newline at end of file
diff --git a/core/java/android/hardware/location/ActivityChangedEvent.java b/core/java/android/hardware/location/ActivityChangedEvent.java
new file mode 100644
index 000000000000..16cfe6e23e88
--- /dev/null
+++ b/core/java/android/hardware/location/ActivityChangedEvent.java
@@ -0,0 +1,92 @@
+/*
+ * 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
+ */
+
+package android.hardware.location;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.InvalidParameterException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A class representing an event for Activity changes.
+ *
+ * @hide
+ */
+public class ActivityChangedEvent implements Parcelable {
+ private final List<ActivityRecognitionEvent> mActivityRecognitionEvents;
+
+ public ActivityChangedEvent(ActivityRecognitionEvent[] activityRecognitionEvents) {
+ if (activityRecognitionEvents == null) {
+ throw new InvalidParameterException(
+ "Parameter 'activityRecognitionEvents' must not be null.");
+ }
+
+ mActivityRecognitionEvents = Arrays.asList(activityRecognitionEvents);
+ }
+
+ @NonNull
+ public Iterable<ActivityRecognitionEvent> getActivityRecognitionEvents() {
+ return mActivityRecognitionEvents;
+ }
+
+ public static final Creator<ActivityChangedEvent> CREATOR =
+ new Creator<ActivityChangedEvent>() {
+ @Override
+ public ActivityChangedEvent createFromParcel(Parcel source) {
+ int activityRecognitionEventsLength = source.readInt();
+ ActivityRecognitionEvent[] activityRecognitionEvents =
+ new ActivityRecognitionEvent[activityRecognitionEventsLength];
+ source.readTypedArray(activityRecognitionEvents, ActivityRecognitionEvent.CREATOR);
+
+ return new ActivityChangedEvent(activityRecognitionEvents);
+ }
+
+ @Override
+ public ActivityChangedEvent[] newArray(int size) {
+ return new ActivityChangedEvent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ ActivityRecognitionEvent[] activityRecognitionEventArray =
+ mActivityRecognitionEvents.toArray(new ActivityRecognitionEvent[0]);
+ parcel.writeInt(activityRecognitionEventArray.length);
+ parcel.writeTypedArray(activityRecognitionEventArray, flags);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("[ ActivityChangedEvent:");
+
+ for (ActivityRecognitionEvent event : mActivityRecognitionEvents) {
+ builder.append("\n ");
+ builder.append(event.toString());
+ }
+ builder.append("\n]");
+
+ return builder.toString();
+ }
+}
diff --git a/core/java/android/hardware/location/ActivityRecognitionEvent.java b/core/java/android/hardware/location/ActivityRecognitionEvent.java
new file mode 100644
index 000000000000..190030a82d6b
--- /dev/null
+++ b/core/java/android/hardware/location/ActivityRecognitionEvent.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package android.hardware.location;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class that represents an Activity Recognition Event.
+ *
+ * @hide
+ */
+public class ActivityRecognitionEvent implements Parcelable {
+ private final String mActivity;
+ private final int mEventType;
+ private final long mTimestampNs;
+
+ public ActivityRecognitionEvent(String activity, int eventType, long timestampNs) {
+ mActivity = activity;
+ mEventType = eventType;
+ mTimestampNs = timestampNs;
+ }
+
+ public String getActivity() {
+ return mActivity;
+ }
+
+ public int getEventType() {
+ return mEventType;
+ }
+
+ public long getTimestampNs() {
+ return mTimestampNs;
+ }
+
+ public static final Creator<ActivityRecognitionEvent> CREATOR =
+ new Creator<ActivityRecognitionEvent>() {
+ @Override
+ public ActivityRecognitionEvent createFromParcel(Parcel source) {
+ String activity = source.readString();
+ int eventType = source.readInt();
+ long timestampNs = source.readLong();
+
+ return new ActivityRecognitionEvent(activity, eventType, timestampNs);
+ }
+
+ @Override
+ public ActivityRecognitionEvent[] newArray(int size) {
+ return new ActivityRecognitionEvent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mActivity);
+ parcel.writeInt(mEventType);
+ parcel.writeLong(mTimestampNs);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "Activity='%s', EventType=%s, TimestampNs=%s",
+ mActivity,
+ mEventType,
+ mTimestampNs);
+ }
+}
diff --git a/core/java/android/hardware/location/ActivityRecognitionHardware.java b/core/java/android/hardware/location/ActivityRecognitionHardware.java
new file mode 100644
index 000000000000..8acd1ff27917
--- /dev/null
+++ b/core/java/android/hardware/location/ActivityRecognitionHardware.java
@@ -0,0 +1,279 @@
+/*
+ * 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
+ */
+
+package android.hardware.location;
+
+import android.Manifest;
+import android.content.Context;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * A class that implements an {@link IActivityRecognitionHardware} backed up by the Activity
+ * Recognition HAL.
+ *
+ * @hide
+ */
+public class ActivityRecognitionHardware extends IActivityRecognitionHardware.Stub {
+ private static final String TAG = "ActivityRecognitionHW";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
+ private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
+ + HARDWARE_PERMISSION + "' not granted to access ActivityRecognitionHardware";
+
+ private static final int INVALID_ACTIVITY_TYPE = -1;
+ private static final int NATIVE_SUCCESS_RESULT = 0;
+ private static final int EVENT_TYPE_DISABLED = 0;
+ private static final int EVENT_TYPE_ENABLED = 1;
+
+ /**
+ * Contains the number of supported Event Types.
+ *
+ * NOTE: increment this counter every time a new EVENT_TYPE_ is added to
+ * com.android.location.provider.ActivityRecognitionProvider
+ */
+ private static final int EVENT_TYPE_COUNT = 3;
+
+ private static ActivityRecognitionHardware sSingletonInstance;
+ private static final Object sSingletonInstanceLock = new Object();
+
+ private final Context mContext;
+ private final int mSupportedActivitiesCount;
+ private final String[] mSupportedActivities;
+ private final int[][] mSupportedActivitiesEnabledEvents;
+ private final SinkList mSinks = new SinkList();
+
+ private static class Event {
+ public int activity;
+ public int type;
+ public long timestamp;
+ }
+
+ private ActivityRecognitionHardware(Context context) {
+ nativeInitialize();
+
+ mContext = context;
+ mSupportedActivities = fetchSupportedActivities();
+ mSupportedActivitiesCount = mSupportedActivities.length;
+ mSupportedActivitiesEnabledEvents = new int[mSupportedActivitiesCount][EVENT_TYPE_COUNT];
+ }
+
+ public static ActivityRecognitionHardware getInstance(Context context) {
+ synchronized (sSingletonInstanceLock) {
+ if (sSingletonInstance == null) {
+ sSingletonInstance = new ActivityRecognitionHardware(context);
+ }
+
+ return sSingletonInstance;
+ }
+ }
+
+ public static boolean isSupported() {
+ return nativeIsSupported();
+ }
+
+ @Override
+ public String[] getSupportedActivities() {
+ checkPermissions();
+ return mSupportedActivities;
+ }
+
+ @Override
+ public boolean isActivitySupported(String activity) {
+ checkPermissions();
+ int activityType = getActivityType(activity);
+ return activityType != INVALID_ACTIVITY_TYPE;
+ }
+
+ @Override
+ public boolean registerSink(IActivityRecognitionHardwareSink sink) {
+ checkPermissions();
+ return mSinks.register(sink);
+ }
+
+ @Override
+ public boolean unregisterSink(IActivityRecognitionHardwareSink sink) {
+ checkPermissions();
+ return mSinks.unregister(sink);
+ }
+
+ @Override
+ public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) {
+ checkPermissions();
+
+ int activityType = getActivityType(activity);
+ if (activityType == INVALID_ACTIVITY_TYPE) {
+ return false;
+ }
+
+ int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs);
+ if (result == NATIVE_SUCCESS_RESULT) {
+ mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_ENABLED;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean disableActivityEvent(String activity, int eventType) {
+ checkPermissions();
+
+ int activityType = getActivityType(activity);
+ if (activityType == INVALID_ACTIVITY_TYPE) {
+ return false;
+ }
+
+ int result = nativeDisableActivityEvent(activityType, eventType);
+ if (result == NATIVE_SUCCESS_RESULT) {
+ mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean flush() {
+ checkPermissions();
+ int result = nativeFlush();
+ return result == NATIVE_SUCCESS_RESULT;
+ }
+
+ /**
+ * Called by the Activity-Recognition HAL.
+ */
+ private void onActivityChanged(Event[] events) {
+ if (events == null || events.length == 0) {
+ if (DEBUG) Log.d(TAG, "No events to broadcast for onActivityChanged.");
+ return;
+ }
+
+ int eventsLength = events.length;
+ ActivityRecognitionEvent activityRecognitionEventArray[] =
+ new ActivityRecognitionEvent[eventsLength];
+ for (int i = 0; i < eventsLength; ++i) {
+ Event event = events[i];
+ String activityName = getActivityName(event.activity);
+ activityRecognitionEventArray[i] =
+ new ActivityRecognitionEvent(activityName, event.type, event.timestamp);
+ }
+ ActivityChangedEvent activityChangedEvent =
+ new ActivityChangedEvent(activityRecognitionEventArray);
+
+ int size = mSinks.beginBroadcast();
+ for (int i = 0; i < size; ++i) {
+ IActivityRecognitionHardwareSink sink = mSinks.getBroadcastItem(i);
+ try {
+ sink.onActivityChanged(activityChangedEvent);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error delivering activity changed event.", e);
+ }
+ }
+ mSinks.finishBroadcast();
+ }
+
+ private String getActivityName(int activityType) {
+ if (activityType < 0 || activityType >= mSupportedActivities.length) {
+ String message = String.format(
+ "Invalid ActivityType: %d, SupportedActivities: %d",
+ activityType,
+ mSupportedActivities.length);
+ Log.e(TAG, message);
+ return null;
+ }
+
+ return mSupportedActivities[activityType];
+ }
+
+ private int getActivityType(String activity) {
+ if (TextUtils.isEmpty(activity)) {
+ return INVALID_ACTIVITY_TYPE;
+ }
+
+ int supportedActivitiesLength = mSupportedActivities.length;
+ for (int i = 0; i < supportedActivitiesLength; ++i) {
+ if (activity.equals(mSupportedActivities[i])) {
+ return i;
+ }
+ }
+
+ return INVALID_ACTIVITY_TYPE;
+ }
+
+ private void checkPermissions() {
+ mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
+ }
+
+ private String[] fetchSupportedActivities() {
+ String[] supportedActivities = nativeGetSupportedActivities();
+ if (supportedActivities != null) {
+ return supportedActivities;
+ }
+
+ return new String[0];
+ }
+
+ private class SinkList extends RemoteCallbackList<IActivityRecognitionHardwareSink> {
+ @Override
+ public void onCallbackDied(IActivityRecognitionHardwareSink callback) {
+ int callbackCount = mSinks.getRegisteredCallbackCount();
+ if (DEBUG) Log.d(TAG, "RegisteredCallbackCount: " + callbackCount);
+ if (callbackCount != 0) {
+ return;
+ }
+ // currently there is only one client for this, so if all its sinks have died, we clean
+ // up after them, this ensures that the AR HAL is not out of sink
+ for (int activity = 0; activity < mSupportedActivitiesCount; ++activity) {
+ for (int event = 0; event < EVENT_TYPE_COUNT; ++event) {
+ disableActivityEventIfEnabled(activity, event);
+ }
+ }
+ }
+
+ private void disableActivityEventIfEnabled(int activityType, int eventType) {
+ if (mSupportedActivitiesEnabledEvents[activityType][eventType] != EVENT_TYPE_ENABLED) {
+ return;
+ }
+
+ int result = nativeDisableActivityEvent(activityType, eventType);
+ mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
+ String message = String.format(
+ "DisableActivityEvent: activityType=%d, eventType=%d, result=%d",
+ activityType,
+ eventType,
+ result);
+ Log.e(TAG, message);
+ }
+ }
+
+ // native bindings
+ static { nativeClassInit(); }
+
+ private static native void nativeClassInit();
+ private static native boolean nativeIsSupported();
+
+ private native void nativeInitialize();
+ private native void nativeRelease();
+ private native String[] nativeGetSupportedActivities();
+ private native int nativeEnableActivityEvent(
+ int activityType,
+ int eventType,
+ long reportLatenceNs);
+ private native int nativeDisableActivityEvent(int activityType, int eventType);
+ private native int nativeFlush();
+}
diff --git a/core/java/android/hardware/location/IActivityRecognitionHardware.aidl b/core/java/android/hardware/location/IActivityRecognitionHardware.aidl
new file mode 100644
index 000000000000..bc6b1830cd4f
--- /dev/null
+++ b/core/java/android/hardware/location/IActivityRecognitionHardware.aidl
@@ -0,0 +1,62 @@
+/*
+ * 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/license/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.hardware.location;
+
+import android.hardware.location.IActivityRecognitionHardwareSink;
+
+/**
+ * Activity Recognition Hardware provider interface.
+ * This interface can be used to implement hardware based activity recognition.
+ *
+ * @hide
+ */
+interface IActivityRecognitionHardware {
+ /**
+ * Gets an array of supported activities by hardware.
+ */
+ String[] getSupportedActivities();
+
+ /**
+ * Returns true if the given activity is supported, false otherwise.
+ */
+ boolean isActivitySupported(in String activityType);
+
+ /**
+ * Registers a sink with Hardware Activity-Recognition.
+ */
+ boolean registerSink(in IActivityRecognitionHardwareSink sink);
+
+ /**
+ * Unregisters a sink with Hardware Activity-Recognition.
+ */
+ boolean unregisterSink(in IActivityRecognitionHardwareSink sink);
+
+ /**
+ * Enables tracking of a given activity/event type, if the activity is supported.
+ */
+ boolean enableActivityEvent(in String activityType, int eventType, long reportLatencyNs);
+
+ /**
+ * Disables tracking of a given activity/eventy type.
+ */
+ boolean disableActivityEvent(in String activityType, int eventType);
+
+ /**
+ * Requests hardware for all the activity events detected up to the given point in time.
+ */
+ boolean flush();
+} \ No newline at end of file
diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl
new file mode 100644
index 000000000000..3fe645c59a30
--- /dev/null
+++ b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015, 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/license/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.hardware.location;
+
+import android.hardware.location.IActivityRecognitionHardware;
+
+/**
+ * Activity Recognition Hardware client interface.
+ * This interface can be used to receive interfaces to implementations of
+ * {@link IActivityRecognitionHardware}.
+ *
+ * @hide
+ */
+oneway interface IActivityRecognitionHardwareClient {
+ /**
+ * Hardware Activity-Recognition availability event.
+ *
+ * @param isSupported whether the platform has hardware support for the feature
+ * @param instance the available instance to provide access to the feature
+ */
+ void onAvailabilityChanged(in boolean isSupported, in IActivityRecognitionHardware instance);
+}
diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl
new file mode 100644
index 000000000000..21c8e87e6c6d
--- /dev/null
+++ b/core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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/license/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.hardware.location;
+
+import android.hardware.location.ActivityChangedEvent;
+
+/**
+ * Activity Recognition Hardware provider Sink interface.
+ * This interface can be used to implement sinks to receive activity notifications.
+ *
+ * @hide
+ */
+interface IActivityRecognitionHardwareSink {
+ /**
+ * Activity changed event.
+ */
+ void onActivityChanged(in ActivityChangedEvent event);
+} \ No newline at end of file
diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
new file mode 100644
index 000000000000..12e3117259e1
--- /dev/null
+++ b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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/license/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.hardware.location;
+
+import android.hardware.location.IActivityRecognitionHardware;
+
+/**
+ * Activity Recognition Hardware watcher. This interface can be used to receive interfaces to
+ * implementations of {@link IActivityRecognitionHardware}.
+ *
+ * @deprecated use {@link IActivityRecognitionHardwareClient} instead.
+
+ * @hide
+ */
+interface IActivityRecognitionHardwareWatcher {
+ /**
+ * Hardware Activity-Recognition availability event.
+ */
+ void onInstanceChanged(in IActivityRecognitionHardware instance);
+}
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index 4047068f1c7b..3b0126673779 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -45,6 +45,8 @@ public class CaptivePortal implements Parcelable {
private final IBinder mBinder;
/** @hide */
+ @SystemApi
+ @TestApi
public CaptivePortal(IBinder binder) {
mBinder = binder;
}
@@ -107,6 +109,8 @@ public class CaptivePortal implements Parcelable {
* connectivity for apps because the captive portal is still in place.
* @hide
*/
+ @SystemApi
+ @TestApi
public void useNetwork() {
try {
ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_WANTED_AS_IS);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index cee3a409fc23..c809ccad5907 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3699,6 +3699,19 @@ public class ConnectivityManager {
}
/**
+ * Determine whether the device is configured to avoid bad wifi.
+ * @hide
+ */
+ @SystemApi
+ public boolean getAvoidBadWifi() {
+ try {
+ return mService.getAvoidBadWifi();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* It is acceptable to briefly use multipath data to provide seamless connectivity for
* time-sensitive user-facing operations when the system default network is temporarily
* unresponsive. The amount of data should be limited (less than one megabyte for every call to
diff --git a/core/java/android/net/DnsPacket.java b/core/java/android/net/DnsPacket.java
new file mode 100644
index 000000000000..458fb340b196
--- /dev/null
+++ b/core/java/android/net/DnsPacket.java
@@ -0,0 +1,235 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+
+import com.android.internal.util.BitUtils;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.text.DecimalFormat;
+import java.text.FieldPosition;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringJoiner;
+
+/**
+ * Defines basic data for DNS protocol based on RFC 1035.
+ * Subclasses create the specific format used in DNS packet.
+ *
+ * @hide
+ */
+public abstract class DnsPacket {
+ public class DnsHeader {
+ private static final String TAG = "DnsHeader";
+ public final int id;
+ public final int flags;
+ public final int rcode;
+ private final int[] mSectionCount;
+
+ /**
+ * Create a new DnsHeader from a positioned ByteBuffer.
+ *
+ * The ByteBuffer must be in network byte order (which is the default).
+ * Reads the passed ByteBuffer from its current position and decodes a DNS header.
+ * When this constructor returns, the reading position of the ByteBuffer has been
+ * advanced to the end of the DNS header record.
+ * This is meant to chain with other methods reading a DNS response in sequence.
+ *
+ */
+ DnsHeader(@NonNull ByteBuffer buf) throws BufferUnderflowException {
+ id = BitUtils.uint16(buf.getShort());
+ flags = BitUtils.uint16(buf.getShort());
+ rcode = flags & 0xF;
+ mSectionCount = new int[NUM_SECTIONS];
+ for (int i = 0; i < NUM_SECTIONS; ++i) {
+ mSectionCount[i] = BitUtils.uint16(buf.getShort());
+ }
+ }
+
+ /**
+ * Get section count by section type.
+ */
+ public int getSectionCount(int sectionType) {
+ return mSectionCount[sectionType];
+ }
+ }
+
+ public class DnsSection {
+ private static final int MAXNAMESIZE = 255;
+ private static final int MAXLABELSIZE = 63;
+ private static final int MAXLABELCOUNT = 128;
+ private static final int NAME_NORMAL = 0;
+ private static final int NAME_COMPRESSION = 0xC0;
+ private final DecimalFormat byteFormat = new DecimalFormat();
+ private final FieldPosition pos = new FieldPosition(0);
+
+ private static final String TAG = "DnsSection";
+
+ public final String dName;
+ public final int nsType;
+ public final int nsClass;
+ public final long ttl;
+ private final byte[] mRR;
+
+ /**
+ * Create a new DnsSection from a positioned ByteBuffer.
+ *
+ * The ByteBuffer must be in network byte order (which is the default).
+ * Reads the passed ByteBuffer from its current position and decodes a DNS section.
+ * When this constructor returns, the reading position of the ByteBuffer has been
+ * advanced to the end of the DNS header record.
+ * This is meant to chain with other methods reading a DNS response in sequence.
+ *
+ */
+ DnsSection(int sectionType, @NonNull ByteBuffer buf)
+ throws BufferUnderflowException, ParseException {
+ dName = parseName(buf, 0 /* Parse depth */);
+ if (dName.length() > MAXNAMESIZE) {
+ throw new ParseException("Parse name fail, name size is too long");
+ }
+ nsType = BitUtils.uint16(buf.getShort());
+ nsClass = BitUtils.uint16(buf.getShort());
+
+ if (sectionType != QDSECTION) {
+ ttl = BitUtils.uint32(buf.getInt());
+ final int length = BitUtils.uint16(buf.getShort());
+ mRR = new byte[length];
+ buf.get(mRR);
+ } else {
+ ttl = 0;
+ mRR = null;
+ }
+ }
+
+ /**
+ * Get a copy of rr.
+ */
+ @Nullable public byte[] getRR() {
+ return (mRR == null) ? null : mRR.clone();
+ }
+
+ /**
+ * Convert label from {@code byte[]} to {@code String}
+ *
+ * It follows the same converting rule as native layer.
+ * (See ns_name.c in libc)
+ *
+ */
+ private String labelToString(@NonNull byte[] label) {
+ final StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < label.length; ++i) {
+ int b = BitUtils.uint8(label[i]);
+ // Control characters and non-ASCII characters.
+ if (b <= 0x20 || b >= 0x7f) {
+ sb.append('\\');
+ byteFormat.format(b, sb, pos);
+ } else if (b == '"' || b == '.' || b == ';' || b == '\\'
+ || b == '(' || b == ')' || b == '@' || b == '$') {
+ sb.append('\\');
+ sb.append((char) b);
+ } else {
+ sb.append((char) b);
+ }
+ }
+ return sb.toString();
+ }
+
+ private String parseName(@NonNull ByteBuffer buf, int depth) throws
+ BufferUnderflowException, ParseException {
+ if (depth > MAXLABELCOUNT) throw new ParseException("Parse name fails, too many labels");
+ final int len = BitUtils.uint8(buf.get());
+ final int mask = len & NAME_COMPRESSION;
+ if (0 == len) {
+ return "";
+ } else if (mask != NAME_NORMAL && mask != NAME_COMPRESSION) {
+ throw new ParseException("Parse name fail, bad label type");
+ } else if (mask == NAME_COMPRESSION) {
+ // Name compression based on RFC 1035 - 4.1.4 Message compression
+ final int offset = ((len & ~NAME_COMPRESSION) << 8) + BitUtils.uint8(buf.get());
+ final int oldPos = buf.position();
+ if (offset >= oldPos - 2) {
+ throw new ParseException("Parse compression name fail, invalid compression");
+ }
+ buf.position(offset);
+ final String pointed = parseName(buf, depth + 1);
+ buf.position(oldPos);
+ return pointed;
+ } else {
+ final byte[] label = new byte[len];
+ buf.get(label);
+ final String head = labelToString(label);
+ if (head.length() > MAXLABELSIZE) {
+ throw new ParseException("Parse name fail, invalid label length");
+ }
+ final String tail = parseName(buf, depth + 1);
+ return TextUtils.isEmpty(tail) ? head : head + "." + tail;
+ }
+ }
+ }
+
+ public static final int QDSECTION = 0;
+ public static final int ANSECTION = 1;
+ public static final int NSSECTION = 2;
+ public static final int ARSECTION = 3;
+ private static final int NUM_SECTIONS = ARSECTION + 1;
+
+ private static final String TAG = DnsPacket.class.getSimpleName();
+
+ protected final DnsHeader mHeader;
+ protected final List<DnsSection>[] mSections;
+
+ public static class ParseException extends Exception {
+ public ParseException(String msg) {
+ super(msg);
+ }
+
+ public ParseException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+ }
+
+ protected DnsPacket(@NonNull byte[] data) throws ParseException {
+ if (null == data) throw new ParseException("Parse header failed, null input data");
+ final ByteBuffer buffer;
+ try {
+ buffer = ByteBuffer.wrap(data);
+ mHeader = new DnsHeader(buffer);
+ } catch (BufferUnderflowException e) {
+ throw new ParseException("Parse Header fail, bad input data", e);
+ }
+
+ mSections = new ArrayList[NUM_SECTIONS];
+
+ for (int i = 0; i < NUM_SECTIONS; ++i) {
+ final int count = mHeader.getSectionCount(i);
+ if (count > 0) {
+ mSections[i] = new ArrayList(count);
+ }
+ for (int j = 0; j < count; ++j) {
+ try {
+ mSections[i].add(new DnsSection(i, buffer));
+ } catch (BufferUnderflowException e) {
+ throw new ParseException("Parse section fail", e);
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
new file mode 100644
index 000000000000..6d54264cd89f
--- /dev/null
+++ b/core/java/android/net/DnsResolver.java
@@ -0,0 +1,289 @@
+/*
+ * 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;
+
+import static android.net.NetworkUtils.resNetworkQuery;
+import static android.net.NetworkUtils.resNetworkResult;
+import static android.net.NetworkUtils.resNetworkSend;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Handler;
+import android.os.MessageQueue;
+import android.system.ErrnoException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+
+/**
+ * Dns resolver class for asynchronous dns querying
+ *
+ */
+public final class DnsResolver {
+ private static final String TAG = "DnsResolver";
+ private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
+ private static final int MAXPACKET = 8 * 1024;
+
+ @IntDef(prefix = { "CLASS_" }, value = {
+ CLASS_IN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface QueryClass {}
+ public static final int CLASS_IN = 1;
+
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_A,
+ TYPE_AAAA
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface QueryType {}
+ public static final int TYPE_A = 1;
+ public static final int TYPE_AAAA = 28;
+
+ @IntDef(prefix = { "FLAG_" }, value = {
+ FLAG_EMPTY,
+ FLAG_NO_RETRY,
+ FLAG_NO_CACHE_STORE,
+ FLAG_NO_CACHE_LOOKUP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface QueryFlag {}
+ public static final int FLAG_EMPTY = 0;
+ public static final int FLAG_NO_RETRY = 1 << 0;
+ public static final int FLAG_NO_CACHE_STORE = 1 << 1;
+ public static final int FLAG_NO_CACHE_LOOKUP = 1 << 2;
+
+ private static final int DNS_RAW_RESPONSE = 1;
+
+ private static final int NETID_UNSET = 0;
+
+ private static final DnsResolver sInstance = new DnsResolver();
+
+ /**
+ * listener for receiving raw answers
+ */
+ public interface RawAnswerListener {
+ /**
+ * {@code byte[]} is {@code null} if query timed out
+ */
+ void onAnswer(@Nullable byte[] answer);
+ }
+
+ /**
+ * listener for receiving parsed answers
+ */
+ public interface InetAddressAnswerListener {
+ /**
+ * Will be called exactly once with all the answers to the query.
+ * size of addresses will be zero if no available answer could be parsed.
+ */
+ void onAnswer(@NonNull List<InetAddress> addresses);
+ }
+
+ /**
+ * Get instance for DnsResolver
+ */
+ public static DnsResolver getInstance() {
+ return sInstance;
+ }
+
+ private DnsResolver() {}
+
+ /**
+ * Pass in a blob and corresponding setting,
+ * get a blob back asynchronously with the entire raw answer.
+ *
+ * @param network {@link Network} specifying which network for querying.
+ * {@code null} for query on default network.
+ * @param query blob message
+ * @param flags flags as a combination of the FLAGS_* constants
+ * @param handler {@link Handler} to specify the thread
+ * upon which the {@link RawAnswerListener} will be invoked.
+ * @param listener a {@link RawAnswerListener} which will be called to notify the caller
+ * of the result of dns query.
+ */
+ public void query(@Nullable Network network, @NonNull byte[] query, @QueryFlag int flags,
+ @NonNull Handler handler, @NonNull RawAnswerListener listener) throws ErrnoException {
+ final FileDescriptor queryfd = resNetworkSend((network != null
+ ? network.netId : NETID_UNSET), query, query.length, flags);
+ registerFDListener(handler.getLooper().getQueue(), queryfd,
+ answerbuf -> listener.onAnswer(answerbuf));
+ }
+
+ /**
+ * Pass in a domain name and corresponding setting,
+ * get a blob back asynchronously with the entire raw answer.
+ *
+ * @param network {@link Network} specifying which network for querying.
+ * {@code null} for query on default network.
+ * @param domain domain name for querying
+ * @param nsClass dns class as one of the CLASS_* constants
+ * @param nsType dns resource record (RR) type as one of the TYPE_* constants
+ * @param flags flags as a combination of the FLAGS_* constants
+ * @param handler {@link Handler} to specify the thread
+ * upon which the {@link RawAnswerListener} will be invoked.
+ * @param listener a {@link RawAnswerListener} which will be called to notify the caller
+ * of the result of dns query.
+ */
+ public void query(@Nullable Network network, @NonNull String domain, @QueryClass int nsClass,
+ @QueryType int nsType, @QueryFlag int flags,
+ @NonNull Handler handler, @NonNull RawAnswerListener listener) throws ErrnoException {
+ final FileDescriptor queryfd = resNetworkQuery((network != null
+ ? network.netId : NETID_UNSET), domain, nsClass, nsType, flags);
+ registerFDListener(handler.getLooper().getQueue(), queryfd,
+ answerbuf -> listener.onAnswer(answerbuf));
+ }
+
+ /**
+ * Pass in a domain name and corresponding setting,
+ * get back a set of InetAddresses asynchronously.
+ *
+ * @param network {@link Network} specifying which network for querying.
+ * {@code null} for query on default network.
+ * @param domain domain name for querying
+ * @param flags flags as a combination of the FLAGS_* constants
+ * @param handler {@link Handler} to specify the thread
+ * upon which the {@link InetAddressAnswerListener} will be invoked.
+ * @param listener an {@link InetAddressAnswerListener} which will be called to
+ * notify the caller of the result of dns query.
+ *
+ */
+ public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags,
+ @NonNull Handler handler, @NonNull InetAddressAnswerListener listener)
+ throws ErrnoException {
+ final FileDescriptor v4fd = resNetworkQuery((network != null
+ ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags);
+ final FileDescriptor v6fd = resNetworkQuery((network != null
+ ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags);
+
+ final InetAddressAnswerAccumulator accmulator =
+ new InetAddressAnswerAccumulator(2, listener);
+ final Consumer<byte[]> consumer = answerbuf ->
+ accmulator.accumulate(parseAnswers(answerbuf));
+
+ registerFDListener(handler.getLooper().getQueue(), v4fd, consumer);
+ registerFDListener(handler.getLooper().getQueue(), v6fd, consumer);
+ }
+
+ private void registerFDListener(@NonNull MessageQueue queue,
+ @NonNull FileDescriptor queryfd, @NonNull Consumer<byte[]> answerConsumer) {
+ queue.addOnFileDescriptorEventListener(
+ queryfd,
+ FD_EVENTS,
+ (fd, events) -> {
+ byte[] answerbuf = null;
+ try {
+ // TODO: Implement result function in Java side instead of using JNI
+ // Because JNI method close fd prior than unregistering fd on
+ // event listener.
+ answerbuf = resNetworkResult(fd);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "resNetworkResult:" + e.toString());
+ }
+ answerConsumer.accept(answerbuf);
+
+ // Unregister this fd listener
+ return 0;
+ });
+ }
+
+ private class DnsAddressAnswer extends DnsPacket {
+ private static final String TAG = "DnsResolver.DnsAddressAnswer";
+ private static final boolean DBG = false;
+
+ private final int mQueryType;
+
+ DnsAddressAnswer(@NonNull byte[] data) throws ParseException {
+ super(data);
+ if ((mHeader.flags & (1 << 15)) == 0) {
+ throw new ParseException("Not an answer packet");
+ }
+ if (mHeader.rcode != 0) {
+ throw new ParseException("Response error, rcode:" + mHeader.rcode);
+ }
+ if (mHeader.getSectionCount(ANSECTION) == 0) {
+ throw new ParseException("No available answer");
+ }
+ if (mHeader.getSectionCount(QDSECTION) == 0) {
+ throw new ParseException("No question found");
+ }
+ // Assume only one question per answer packet. (RFC1035)
+ mQueryType = mSections[QDSECTION].get(0).nsType;
+ }
+
+ public @NonNull List<InetAddress> getAddresses() {
+ final List<InetAddress> results = new ArrayList<InetAddress>();
+ for (final DnsSection ansSec : mSections[ANSECTION]) {
+ // Only support A and AAAA, also ignore answers if query type != answer type.
+ int nsType = ansSec.nsType;
+ if (nsType != mQueryType || (nsType != TYPE_A && nsType != TYPE_AAAA)) {
+ continue;
+ }
+ try {
+ results.add(InetAddress.getByAddress(ansSec.getRR()));
+ } catch (UnknownHostException e) {
+ if (DBG) {
+ Log.w(TAG, "rr to address fail");
+ }
+ }
+ }
+ return results;
+ }
+ }
+
+ private @Nullable List<InetAddress> parseAnswers(@Nullable byte[] data) {
+ try {
+ return (data == null) ? null : new DnsAddressAnswer(data).getAddresses();
+ } catch (DnsPacket.ParseException e) {
+ Log.e(TAG, "Parse answer fail " + e.getMessage());
+ return null;
+ }
+ }
+
+ private class InetAddressAnswerAccumulator {
+ private final List<InetAddress> mAllAnswers;
+ private final InetAddressAnswerListener mAnswerListener;
+ private final int mTargetAnswerCount;
+ private int mReceivedAnswerCount = 0;
+
+ InetAddressAnswerAccumulator(int size, @NonNull InetAddressAnswerListener listener) {
+ mTargetAnswerCount = size;
+ mAllAnswers = new ArrayList<>();
+ mAnswerListener = listener;
+ }
+
+ public void accumulate(@Nullable List<InetAddress> answer) {
+ if (null != answer) {
+ mAllAnswers.addAll(answer);
+ }
+ if (++mReceivedAnswerCount == mTargetAnswerCount) {
+ mAnswerListener.onAnswer(mAllAnswers);
+ }
+ }
+ }
+}
diff --git a/core/java/android/net/ICaptivePortal.aidl b/core/java/android/net/ICaptivePortal.aidl
index a013e79106e7..56ae57dc0e8d 100644
--- a/core/java/android/net/ICaptivePortal.aidl
+++ b/core/java/android/net/ICaptivePortal.aidl
@@ -20,7 +20,6 @@ package android.net;
* Interface to inform NetworkMonitor of decisions of app handling captive portal.
* @hide
*/
-interface ICaptivePortal
-{
- oneway void appResponse(int response);
+oneway interface ICaptivePortal {
+ void appResponse(int response);
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index da5d96e49d02..131925ec28e9 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -165,6 +165,7 @@ interface IConnectivityManager
void setAvoidUnvalidated(in Network network);
void startCaptivePortalApp(in Network network);
+ boolean getAvoidBadWifi();
int getMultipathPreference(in Network Network);
NetworkRequest getDefaultRequest();
@@ -187,4 +188,6 @@ interface IConnectivityManager
byte[] getNetworkWatchlistConfigHash();
int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
+ boolean isCallerCurrentAlwaysOnVpnApp();
+ boolean isCallerCurrentAlwaysOnVpnLockdownApp();
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index c2963fd605c0..21b6a8eb1990 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -19,6 +19,7 @@ package android.net;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -368,7 +369,8 @@ public final class LinkProperties implements Parcelable {
* @return true if the DNS server was added, false if it was already present.
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean addDnsServer(InetAddress dnsServer) {
if (dnsServer != null && !mDnses.contains(dnsServer)) {
mDnses.add(dnsServer);
@@ -384,7 +386,8 @@ public final class LinkProperties implements Parcelable {
* @return true if the DNS server was removed, false if it did not exist.
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean removeDnsServer(InetAddress dnsServer) {
if (dnsServer != null) {
return mDnses.remove(dnsServer);
@@ -423,6 +426,8 @@ public final class LinkProperties implements Parcelable {
* @param usePrivateDns The private DNS state.
* @hide
*/
+ @TestApi
+ @SystemApi
public void setUsePrivateDns(boolean usePrivateDns) {
mUsePrivateDns = usePrivateDns;
}
@@ -448,6 +453,8 @@ public final class LinkProperties implements Parcelable {
* @param privateDnsServerName The private DNS server name.
* @hide
*/
+ @TestApi
+ @SystemApi
public void setPrivateDnsServerName(@Nullable String privateDnsServerName) {
mPrivateDnsServerName = privateDnsServerName;
}
@@ -510,6 +517,8 @@ public final class LinkProperties implements Parcelable {
* object.
* @hide
*/
+ @TestApi
+ @SystemApi
public void setValidatedPrivateDnsServers(Collection<InetAddress> dnsServers) {
mValidatedPrivateDnses.clear();
for (InetAddress dnsServer: dnsServers) {
@@ -525,6 +534,8 @@ public final class LinkProperties implements Parcelable {
* DNS servers on this link.
* @hide
*/
+ @TestApi
+ @SystemApi
public List<InetAddress> getValidatedPrivateDnsServers() {
return Collections.unmodifiableList(mValidatedPrivateDnses);
}
@@ -636,7 +647,8 @@ public final class LinkProperties implements Parcelable {
*
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public void setTcpBufferSizes(String tcpBufferSizes) {
mTcpBufferSizes = tcpBufferSizes;
}
@@ -648,7 +660,8 @@ public final class LinkProperties implements Parcelable {
*
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public String getTcpBufferSizes() {
return mTcpBufferSizes;
}
@@ -699,7 +712,8 @@ public final class LinkProperties implements Parcelable {
*
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean removeRoute(RouteInfo route) {
return route != null &&
Objects.equals(mIfaceName, route.getInterface()) &&
@@ -960,7 +974,8 @@ public final class LinkProperties implements Parcelable {
* @return {@code true} if there is an IPv4 address, {@code false} otherwise.
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean hasIPv4Address() {
for (LinkAddress address : mLinkAddresses) {
if (address.getAddress() instanceof Inet4Address) {
@@ -988,7 +1003,8 @@ public final class LinkProperties implements Parcelable {
* @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise.
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean hasGlobalIPv6Address() {
for (LinkAddress address : mLinkAddresses) {
if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) {
@@ -1020,7 +1036,8 @@ public final class LinkProperties implements Parcelable {
* @return {@code true} if there is an IPv6 default route, {@code false} otherwise.
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean hasIPv6DefaultRoute() {
for (RouteInfo r : mRoutes) {
if (r.isIPv6Default()) {
@@ -1099,6 +1116,8 @@ public final class LinkProperties implements Parcelable {
* @return {@code true} if the link is provisioned, {@code false} otherwise.
* @hide
*/
+ @TestApi
+ @SystemApi
public boolean isIPv4Provisioned() {
return (hasIPv4Address() &&
hasIPv4DefaultRoute() &&
@@ -1112,7 +1131,8 @@ public final class LinkProperties implements Parcelable {
* @return {@code true} if the link is provisioned, {@code false} otherwise.
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean isIPv6Provisioned() {
return (hasGlobalIPv6Address() &&
hasIPv6DefaultRoute() &&
@@ -1126,7 +1146,8 @@ public final class LinkProperties implements Parcelable {
* @return {@code true} if the link is provisioned, {@code false} otherwise.
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean isProvisioned() {
return (isIPv4Provisioned() || isIPv6Provisioned());
}
@@ -1138,7 +1159,8 @@ public final class LinkProperties implements Parcelable {
* {@code false} otherwise.
* @hide
*/
- @UnsupportedAppUsage
+ @TestApi
+ @SystemApi
public boolean isReachable(InetAddress ip) {
final List<RouteInfo> allRoutes = getAllRoutes();
// If we don't have a route to this IP address, it's not reachable.
diff --git a/core/java/android/net/LinkPropertiesParcelable.aidl b/core/java/android/net/LinkPropertiesParcelable.aidl
index b153dc70e1b8..6b52239b4168 100644
--- a/core/java/android/net/LinkPropertiesParcelable.aidl
+++ b/core/java/android/net/LinkPropertiesParcelable.aidl
@@ -35,5 +35,4 @@ parcelable LinkPropertiesParcelable {
int mtu;
String tcpBufferSizes;
IpPrefixParcelable nat64Prefix;
- LinkPropertiesParcelable[] stackedLinks;
} \ No newline at end of file
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 1b44c920a205..7e9bda14b199 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -712,6 +712,7 @@ public final class NetworkCapabilities implements Parcelable {
* @hide
*/
@TestApi
+ @SystemApi
public @Transport int[] getTransportTypes() {
return BitUtils.unpackBits(mTransportTypes);
}
@@ -1312,6 +1313,8 @@ public final class NetworkCapabilities implements Parcelable {
*
* @hide
*/
+ @TestApi
+ @SystemApi
public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
return satisfiedByNetworkCapabilities(nc, false);
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 4eab49cd0fdf..c996d01fe8e4 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -39,6 +39,8 @@ import java.util.Collection;
import java.util.Locale;
import java.util.TreeSet;
+import android.system.ErrnoException;
+
/**
* Native methods for managing network interfaces.
*
@@ -138,6 +140,32 @@ public class NetworkUtils {
public native static boolean queryUserAccess(int uid, int netId);
/**
+ * DNS resolver series jni method.
+ * Issue the query {@code msg} on the network designated by {@code netId}.
+ * {@code flags} is an additional config to control actual querying behavior.
+ * @return a file descriptor to watch for read events
+ */
+ public static native FileDescriptor resNetworkSend(
+ int netId, byte[] msg, int msglen, int flags) throws ErrnoException;
+
+ /**
+ * DNS resolver series jni method.
+ * Look up the {@code nsClass} {@code nsType} Resource Record (RR) associated
+ * with Domain Name {@code dname} on the network designated by {@code netId}.
+ * {@code flags} is an additional config to control actual querying behavior.
+ * @return a file descriptor to watch for read events
+ */
+ public static native FileDescriptor resNetworkQuery(
+ int netId, String dname, int nsClass, int nsType, int flags) throws ErrnoException;
+
+ /**
+ * DNS resolver series jni method.
+ * Read a result for the query associated with the {@code fd}.
+ * @return a byte array containing blob answer
+ */
+ public static native byte[] resNetworkResult(FileDescriptor fd) throws ErrnoException;
+
+ /**
* Add an entry into the ARP cache.
*/
public static void addArpEntry(Inet4Address ipv4Addr, MacAddress ethAddr, String ifname,
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index abc1cac02c7e..90dccb5b82d5 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -86,7 +86,16 @@ import javax.net.ssl.X509TrustManager;
* <p>On development devices, "setprop socket.relaxsslcheck yes" bypasses all
* SSL certificate and hostname checks for testing purposes. This setting
* requires root access.
+ *
+ * @deprecated This class has less error-prone replacements using standard APIs. To create an
+ * {@code SSLSocket}, obtain an {@link SSLSocketFactory} from {@link SSLSocketFactory#getDefault()}
+ * or {@link javax.net.ssl.SSLContext#getSocketFactory()}. To verify hostnames, pass
+ * {@code "HTTPS"} to
+ * {@link javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)}. To enable ALPN,
+ * use {@link javax.net.ssl.SSLParameters#setApplicationProtocols(String[])}. To enable SNI,
+ * use {@link javax.net.ssl.SSLParameters#setServerNames(java.util.List)}.
*/
+@Deprecated
public class SSLCertificateSocketFactory extends SSLSocketFactory {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private static final String TAG = "SSLCertificateSocketFactory";
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index f0c0462cec18..37bf3a71ce62 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -368,6 +368,29 @@ public class VpnService extends Service {
}
/**
+ * Returns whether the service is running in always-on VPN mode.
+ */
+ public final boolean isAlwaysOn() {
+ try {
+ return getService().isCallerCurrentAlwaysOnVpnApp();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether the service is running in always-on VPN mode blocking connections without
+ * VPN.
+ */
+ public final boolean isLockdownEnabled() {
+ try {
+ return getService().isCallerCurrentAlwaysOnVpnLockdownApp();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Return the communication interface to the service. This method returns
* {@code null} on {@link Intent}s other than {@link #SERVICE_INTERFACE}
* action. Applications overriding this method must identify the intent
diff --git a/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl b/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
index 57f59a17cfe7..fb4ca3b97895 100644
--- a/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
+++ b/core/java/android/net/ipmemorystore/IOnNetworkAttributesRetrieved.aidl
@@ -25,6 +25,6 @@ oneway interface IOnNetworkAttributesRetrieved {
* Network attributes were fetched for the specified L2 key. While the L2 key will never
* be null, the attributes may be if no data is stored about this L2 key.
*/
- void onL2KeyResponse(in StatusParcelable status, in String l2Key,
+ void onNetworkAttributesRetrieved(in StatusParcelable status, in String l2Key,
in NetworkAttributesParcelable attributes);
}
diff --git a/core/java/android/net/ipmemorystore/NetworkAttributes.java b/core/java/android/net/ipmemorystore/NetworkAttributes.java
index b932d2197f85..6a9eae00e3ff 100644
--- a/core/java/android/net/ipmemorystore/NetworkAttributes.java
+++ b/core/java/android/net/ipmemorystore/NetworkAttributes.java
@@ -37,27 +37,57 @@ import java.util.StringJoiner;
public class NetworkAttributes {
private static final boolean DBG = true;
+ // Weight cutoff for grouping. To group, a similarity score is computed with the following
+ // algorithm : if both fields are non-null and equals() then add their assigned weight, else if
+ // both are null then add a portion of their assigned weight (see NULL_MATCH_WEIGHT),
+ // otherwise add nothing.
+ // As a guideline, this should be something like 60~75% of the total weights in this class. The
+ // design states "in essence a reader should imagine that if two important columns don't match,
+ // or one important and several unimportant columns don't match then the two records are
+ // considered a different group".
+ private static final float TOTAL_WEIGHT_CUTOFF = 520.0f;
+ // The portion of the weight that is earned when scoring group-sameness by having both columns
+ // being null. This is because some networks rightfully don't have some attributes (e.g. a
+ // V6-only network won't have an assigned V4 address) and both being null should count for
+ // something, but attributes may also be null just because data is unavailable.
+ private static final float NULL_MATCH_WEIGHT = 0.25f;
+
// The v4 address that was assigned to this device the last time it joined this network.
// This typically comes from DHCP but could be something else like static configuration.
// This does not apply to IPv6.
// TODO : add a list of v6 prefixes for the v6 case.
@Nullable
public final Inet4Address assignedV4Address;
+ private static final float WEIGHT_ASSIGNEDV4ADDR = 300.0f;
// Optionally supplied by the client if it has an opinion on L3 network. For example, this
// could be a hash of the SSID + security type on WiFi.
@Nullable
public final String groupHint;
+ private static final float WEIGHT_GROUPHINT = 300.0f;
// The list of DNS server addresses.
@Nullable
public final List<InetAddress> dnsAddresses;
+ private static final float WEIGHT_DNSADDRESSES = 200.0f;
// The mtu on this network.
@Nullable
public final Integer mtu;
+ private static final float WEIGHT_MTU = 50.0f;
- NetworkAttributes(
+ // The sum of all weights in this class. Tests ensure that this stays equal to the total of
+ // all weights.
+ /** @hide */
+ @VisibleForTesting
+ public static final float TOTAL_WEIGHT = WEIGHT_ASSIGNEDV4ADDR
+ + WEIGHT_GROUPHINT
+ + WEIGHT_DNSADDRESSES
+ + WEIGHT_MTU;
+
+ /** @hide */
+ @VisibleForTesting
+ public NetworkAttributes(
@Nullable final Inet4Address assignedV4Address,
@Nullable final String groupHint,
@Nullable final List<InetAddress> dnsAddresses,
@@ -126,6 +156,34 @@ public class NetworkAttributes {
return parcelable;
}
+ private float samenessContribution(final float weight,
+ @Nullable final Object o1, @Nullable final Object o2) {
+ if (null == o1) {
+ return (null == o2) ? weight * NULL_MATCH_WEIGHT : 0f;
+ }
+ return Objects.equals(o1, o2) ? weight : 0f;
+ }
+
+ /** @hide */
+ public float getNetworkGroupSamenessConfidence(@NonNull final NetworkAttributes o) {
+ final float samenessScore =
+ samenessContribution(WEIGHT_ASSIGNEDV4ADDR, assignedV4Address, o.assignedV4Address)
+ + samenessContribution(WEIGHT_GROUPHINT, groupHint, o.groupHint)
+ + samenessContribution(WEIGHT_DNSADDRESSES, dnsAddresses, o.dnsAddresses)
+ + samenessContribution(WEIGHT_MTU, mtu, o.mtu);
+ // The minimum is 0, the max is TOTAL_WEIGHT and should be represented by 1.0, and
+ // TOTAL_WEIGHT_CUTOFF should represent 0.5, but there is no requirement that
+ // TOTAL_WEIGHT_CUTOFF would be half of TOTAL_WEIGHT (indeed, it should not be).
+ // So scale scores under the cutoff between 0 and 0.5, and the scores over the cutoff
+ // between 0.5 and 1.0.
+ if (samenessScore < TOTAL_WEIGHT_CUTOFF) {
+ return samenessScore / (TOTAL_WEIGHT_CUTOFF * 2);
+ } else {
+ return (samenessScore - TOTAL_WEIGHT_CUTOFF) / (TOTAL_WEIGHT - TOTAL_WEIGHT_CUTOFF) / 2
+ + 0.5f;
+ }
+ }
+
/** @hide */
public static class Builder {
@Nullable
@@ -194,6 +252,12 @@ public class NetworkAttributes {
}
}
+ /** @hide */
+ public boolean isEmpty() {
+ return (null == assignedV4Address) && (null == groupHint)
+ && (null == dnsAddresses) && (null == mtu);
+ }
+
@Override
public boolean equals(@Nullable final Object o) {
if (!(o instanceof NetworkAttributes)) return false;
diff --git a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
index d040dcc3d608..291aca8fc611 100644
--- a/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
+++ b/core/java/android/net/ipmemorystore/SameL3NetworkResponse.java
@@ -91,7 +91,8 @@ public class SameL3NetworkResponse {
return confidence > 0.5 ? NETWORK_SAME : NETWORK_DIFFERENT;
}
- SameL3NetworkResponse(@NonNull final String l2Key1, @NonNull final String l2Key2,
+ /** @hide */
+ public SameL3NetworkResponse(@NonNull final String l2Key1, @NonNull final String l2Key2,
final float confidence) {
this.l2Key1 = l2Key1;
this.l2Key2 = l2Key2;
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index c04fcdcadb10..16aea31b97c9 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -21,6 +21,7 @@ import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
+import android.net.Network;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -128,6 +129,18 @@ public class IpConnectivityLog {
/**
* Log an IpConnectivity event.
+ * @param network the network associated with the event.
+ * @param transports the current transports of the network associated with the event, as defined
+ * in NetworkCapabilities.
+ * @param data is a Parcelable instance representing the event.
+ * @return true if the event was successfully logged.
+ */
+ public boolean log(Network network, int[] transports, Event data) {
+ return log(network.netId, transports, data);
+ }
+
+ /**
+ * Log an IpConnectivity event.
* @param netid the id of the network associated with the event.
* @param transports the current transports of the network associated with the event, as defined
* in NetworkCapabilities.
diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java
new file mode 100644
index 000000000000..de67cf5952f4
--- /dev/null
+++ b/core/java/android/net/util/SocketUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 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.util;
+
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_BINDTODEVICE;
+
+import android.annotation.SystemApi;
+import android.net.NetworkUtils;
+import android.system.ErrnoException;
+import android.system.NetlinkSocketAddress;
+import android.system.Os;
+import android.system.PacketSocketAddress;
+
+import java.io.FileDescriptor;
+import java.net.SocketAddress;
+
+/**
+ * Collection of utilities to interact with raw sockets.
+ * @hide
+ */
+@SystemApi
+public class SocketUtils {
+ /**
+ * Create a raw datagram socket that is bound to an interface.
+ *
+ * <p>Data sent through the socket will go directly to the underlying network, ignoring VPNs.
+ */
+ public static void bindSocketToInterface(FileDescriptor socket, String iface)
+ throws ErrnoException {
+ // SO_BINDTODEVICE actually takes a string. This works because the first member
+ // of struct ifreq is a NULL-terminated interface name.
+ // TODO: add a setsockoptString()
+ Os.setsockoptIfreq(socket, SOL_SOCKET, SO_BINDTODEVICE, iface);
+ NetworkUtils.protectFromVpn(socket);
+ }
+
+ /**
+ * Make a socket address to communicate with netlink.
+ */
+ public static SocketAddress makeNetlinkSocketAddress(int portId, int groupsMask) {
+ return new NetlinkSocketAddress(portId, groupsMask);
+ }
+
+ /**
+ * Make a socket address to bind to packet sockets.
+ */
+ public static SocketAddress makePacketSocketAddress(short protocol, int ifIndex) {
+ return new PacketSocketAddress(protocol, ifIndex);
+ }
+
+ /**
+ * Make a socket address to send raw packets.
+ */
+ public static SocketAddress makePacketSocketAddress(int ifIndex, byte[] hwAddr) {
+ return new PacketSocketAddress(ifIndex, hwAddr);
+ }
+
+ private SocketUtils() {}
+}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index ca3905148ede..51c3c4c25ce0 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -72,6 +72,7 @@ import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Comparator;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -831,6 +832,16 @@ public class FileUtils {
return false;
}
+ /** {@hide} */
+ public static boolean contains(Collection<File> dirs, File file) {
+ for (File dir : dirs) {
+ if (contains(dir, file)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Test if a file lives under the given directory, either as a direct child
* or a distant grandchild.
diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java
index a4d5c6f4b202..92fcbb65b106 100644
--- a/core/java/android/os/HandlerThread.java
+++ b/core/java/android/os/HandlerThread.java
@@ -20,8 +20,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
/**
- * Handy class for starting a new thread that has a looper. The looper can then be
- * used to create handler classes. Note that start() must still be called.
+ * A {@link Thread} that has a {@link Looper}.
+ * The {@link Looper} can then be used to create {@link Handler}s.
+ * <p>
+ * Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
*/
public class HandlerThread extends Thread {
int mPriority;
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index 827170144dea..fe17c6bb14ca 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -45,4 +45,6 @@ interface IDeviceIdleController {
void exitIdle(String reason);
boolean registerMaintenanceActivityListener(IMaintenanceActivityListener listener);
void unregisterMaintenanceActivityListener(IMaintenanceActivityListener listener);
+ int setPreIdleTimeoutMode(int Mode);
+ void resetPreIdleTimeoutMode();
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 0942d97df6c7..4ce760f2c4a6 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1724,6 +1724,25 @@ public final class PowerManager {
= "android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED";
/**
+ * Constant for PreIdleTimeout normal mode (default mode, not short nor extend timeout) .
+ * @hide
+ */
+ public static final int PRE_IDLE_TIMEOUT_MODE_NORMAL = 0;
+
+ /**
+ * Constant for PreIdleTimeout long mode (extend timeout to keep in inactive mode
+ * longer).
+ * @hide
+ */
+ public static final int PRE_IDLE_TIMEOUT_MODE_LONG = 1;
+
+ /**
+ * Constant for PreIdleTimeout short mode (short timeout to go to doze mode quickly)
+ * @hide
+ */
+ public static final int PRE_IDLE_TIMEOUT_MODE_SHORT = 2;
+
+ /**
* A wake lock is a mechanism to indicate that your application needs
* to have the device stay on.
* <p>
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 760fef7566c7..f2a9adb6feea 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -48,7 +48,6 @@ public class Process {
/**
* Defines the root UID.
- * @hide
*/
public static final int ROOT_UID = 0;
@@ -64,7 +63,6 @@ public class Process {
/**
* Defines the UID/GID for the user shell.
- * @hide
*/
public static final int SHELL_UID = 2000;
@@ -118,7 +116,6 @@ public class Process {
/**
* Defines the UID/GID for the Bluetooth service process.
- * @hide
*/
public static final int BLUETOOTH_UID = 1002;
diff --git a/core/java/android/os/ServiceSpecificException.java b/core/java/android/os/ServiceSpecificException.java
index 3e0f6dae04d8..3b0f26ae8867 100644
--- a/core/java/android/os/ServiceSpecificException.java
+++ b/core/java/android/os/ServiceSpecificException.java
@@ -15,6 +15,8 @@
*/
package android.os;
+import android.annotation.SystemApi;
+
/**
* An exception specific to a service.
*
@@ -28,6 +30,7 @@ package android.os;
*
* @hide
*/
+@SystemApi
public class ServiceSpecificException extends RuntimeException {
public final int errorCode;
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 2299ab2a23ff..76fe560ed902 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -140,6 +140,21 @@ public class WorkSource implements Parcelable {
return mUids[index];
}
+ /**
+ * Return the UID to which this WorkSource should be attributed to, i.e, the UID that
+ * initiated the work and not the UID performing it. If the WorkSource has no UIDs, returns -1
+ * instead.
+ *
+ * @hide
+ */
+ public int getAttributionUid() {
+ if (isEmpty()) {
+ return -1;
+ }
+
+ return mNum > 0 ? mUids[0] : mChains.get(0).getAttributionUid();
+ }
+
/** @hide */
@TestApi
public String getName(int index) {
@@ -912,17 +927,18 @@ public class WorkSource implements Parcelable {
/**
* Return the UID to which this WorkChain should be attributed to, i.e, the UID that
- * initiated the work and not the UID performing it.
+ * initiated the work and not the UID performing it. Returns -1 if the chain is empty.
*/
public int getAttributionUid() {
- return mUids[0];
+ return mSize > 0 ? mUids[0] : -1;
}
/**
* Return the tag associated with the attribution UID. See (@link #getAttributionUid}.
+ * Returns null if the chain is empty.
*/
public String getAttributionTag() {
- return mTags[0];
+ return mTags.length > 0 ? mTags[0] : null;
}
// TODO: The following three trivial getters are purely for testing and will be removed
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index df1a7131a7ae..714a06126ef7 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -35,6 +35,7 @@ import com.android.internal.util.Preconditions;
import java.io.CharArrayWriter;
import java.io.File;
+import java.util.Locale;
/**
* Information about a shared/external storage volume for a specific user.
@@ -263,6 +264,11 @@ public final class StorageVolume implements Parcelable {
return mFsUuid;
}
+ /** {@hide} */
+ public @Nullable String getNormalizedUuid() {
+ return mFsUuid != null ? mFsUuid.toLowerCase(Locale.US) : null;
+ }
+
/**
* Parse and return volume UUID as FAT volume ID, or return -1 if unable to
* parse or UUID is unknown.
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 8c3aa1750acf..5d310e1c2db9 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -42,6 +42,7 @@ import com.android.internal.util.Preconditions;
import java.io.CharArrayWriter;
import java.io.File;
import java.util.Comparator;
+import java.util.Locale;
import java.util.Objects;
/**
@@ -254,6 +255,10 @@ public class VolumeInfo implements Parcelable {
return fsUuid;
}
+ public @Nullable String getNormalizedFsUuid() {
+ return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null;
+ }
+
@UnsupportedAppUsage
public int getMountUserId() {
return mountUserId;
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index a7e6601b040b..cd3efb495034 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -94,6 +94,15 @@ public final class DeviceConfig {
@SystemApi
public static final String NAMESPACE_NETD_NATIVE = "netd_native";
+ /**
+ * Namespace for features related to the ExtServices Notification Assistant.
+ * These features are applied immediately.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_NOTIFICATION_ASSISTANT = "notification_assistant";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static Map<OnPropertyChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index a323ed1a51cb..5f1c56016fa7 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -16,7 +16,6 @@
package android.provider;
-import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkCollectionNotEmpty;
@@ -50,6 +49,8 @@ import android.os.ParcelableException;
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import dalvik.system.VMRuntime;
import java.io.File;
@@ -640,6 +641,28 @@ public final class DocumentsContract {
public static final String COLUMN_MIME_TYPES = "mime_types";
/**
+ * Query arguments supported by this root. This column is optional
+ * and related to {@link #COLUMN_FLAGS} and {@link #FLAG_SUPPORTS_SEARCH}.
+ * If the flags include {@link #FLAG_SUPPORTS_SEARCH}, and the column is
+ * {@code null}, the root is assumed to support {@link #QUERY_ARG_DISPLAY_NAME}
+ * search of {@link Document#COLUMN_DISPLAY_NAME}. Multiple query arguments
+ * can be separated by a newline. For example, a root supporting
+ * {@link #QUERY_ARG_MIME_TYPES} and {@link #QUERY_ARG_DISPLAY_NAME} might
+ * return "android:query-arg-mime-types\nandroid:query-arg-display-name".
+ * <p>
+ * Type: STRING
+ * @see #COLUMN_FLAGS
+ * @see #FLAG_SUPPORTS_SEARCH
+ * @see #QUERY_ARG_DISPLAY_NAME
+ * @see #QUERY_ARG_FILE_SIZE_OVER
+ * @see #QUERY_ARG_LAST_MODIFIED_AFTER
+ * @see #QUERY_ARG_MIME_TYPES
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ */
+ public static final String COLUMN_QUERY_ARGS = "query_args";
+
+ /**
* MIME type for a root.
*/
public static final String MIME_TYPE_ITEM = "vnd.android.document/root";
@@ -680,6 +703,8 @@ public final class DocumentsContract {
* String)
* @see DocumentsProvider#querySearchDocuments(String, String,
* String[])
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
*/
public static final int FLAG_SUPPORTS_SEARCH = 1 << 3;
@@ -1109,11 +1134,13 @@ public final class DocumentsContract {
}
/**
- * Test if the given URI represents roots backed by {@link DocumentsProvider}.
+ * Test if the given URI represents all roots of the authority
+ * backed by {@link DocumentsProvider}.
*
* @see #buildRootsUri(String)
*/
- public static boolean isRootsUri(Context context, @Nullable Uri uri) {
+ public static boolean isRootsUri(@NonNull Context context, @Nullable Uri uri) {
+ Preconditions.checkNotNull(context, "context can not be null");
return isRootUri(context, uri, 1 /* pathSize */);
}
@@ -1122,7 +1149,8 @@ public final class DocumentsContract {
*
* @see #buildRootUri(String, String)
*/
- public static boolean isRootUri(Context context, @Nullable Uri uri) {
+ public static boolean isRootUri(@NonNull Context context, @Nullable Uri uri) {
+ Preconditions.checkNotNull(context, "context can not be null");
return isRootUri(context, uri, 2 /* pathSize */);
}
@@ -1215,6 +1243,7 @@ public final class DocumentsContract {
* {@hide}
*/
public static String getSearchDocumentsQuery(@NonNull Bundle bundle) {
+ Preconditions.checkNotNull(bundle, "bundle can not be null");
return bundle.getString(QUERY_ARG_DISPLAY_NAME, "" /* defaultValue */);
}
@@ -1315,8 +1344,12 @@ public final class DocumentsContract {
* @return if given document is a descendant of the given parent.
* @see Root#FLAG_SUPPORTS_IS_CHILD
*/
- public static boolean isChildDocument(ContentInterface content, Uri parentDocumentUri,
- Uri childDocumentUri) throws FileNotFoundException {
+ public static boolean isChildDocument(@NonNull ContentInterface content,
+ @NonNull Uri parentDocumentUri, @NonNull Uri childDocumentUri)
+ throws FileNotFoundException {
+ Preconditions.checkNotNull(content, "content can not be null");
+ Preconditions.checkNotNull(parentDocumentUri, "parentDocumentUri can not be null");
+ Preconditions.checkNotNull(childDocumentUri, "childDocumentUri can not be null");
try {
final Bundle in = new Bundle();
in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri);
@@ -1325,7 +1358,7 @@ public final class DocumentsContract {
final Bundle out = content.call(parentDocumentUri.getAuthority(),
METHOD_IS_CHILD_DOCUMENT, null, in);
if (out == null) {
- throw new RemoteException("Failed to get a reponse from isChildDocument query.");
+ throw new RemoteException("Failed to get a response from isChildDocument query.");
}
if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) {
throw new RemoteException("Response did not include result field..");
@@ -1559,8 +1592,10 @@ public final class DocumentsContract {
* @param documentUri a Document URI
* @return a Bundle of Bundles.
*/
- public static Bundle getDocumentMetadata(ContentInterface content, Uri documentUri)
- throws FileNotFoundException {
+ public static @Nullable Bundle getDocumentMetadata(@NonNull ContentInterface content,
+ @NonNull Uri documentUri) throws FileNotFoundException {
+ Preconditions.checkNotNull(content, "content can not be null");
+ Preconditions.checkNotNull(documentUri, "documentUri can not be null");
try {
final Bundle in = new Bundle();
in.putParcelable(EXTRA_URI, documentUri);
@@ -1595,8 +1630,6 @@ public final class DocumentsContract {
*/
public static Path findDocumentPath(ContentInterface content, Uri treeUri)
throws FileNotFoundException {
- checkArgument(isTreeUri(treeUri), treeUri + " is not a tree uri.");
-
try {
final Bundle in = new Bundle();
in.putParcelable(DocumentsContract.EXTRA_URI, treeUri);
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 70c84f8cc324..9b9e2dee8f9b 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -37,6 +37,7 @@ import static android.provider.DocumentsContract.isTreeUri;
import android.Manifest;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AuthenticationRequiredException;
import android.content.ClipDescription;
@@ -63,6 +64,8 @@ import android.provider.DocumentsContract.Path;
import android.provider.DocumentsContract.Root;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import libcore.io.IoUtils;
import java.io.FileNotFoundException;
@@ -687,14 +690,17 @@ public abstract class DocumentsProvider extends ContentProvider {
* extras {@link Bundle} when any QUERY_ARG_* value was honored
* during the preparation of the results.
*
+ * @see Root#COLUMN_QUERY_ARGS
* @see ContentResolver#EXTRA_HONORED_ARGS
* @see DocumentsContract#EXTRA_LOADING
* @see DocumentsContract#EXTRA_INFO
* @see DocumentsContract#EXTRA_ERROR
*/
@SuppressWarnings("unused")
- public Cursor querySearchDocuments(String rootId, String[] projection, Bundle queryArgs)
- throws FileNotFoundException {
+ public Cursor querySearchDocuments(@NonNull String rootId, @Nullable String[] projection,
+ @NonNull Bundle queryArgs) throws FileNotFoundException {
+ Preconditions.checkNotNull(rootId, "rootId can not be null");
+ Preconditions.checkNotNull(queryArgs, "queryArgs can not be null");
return querySearchDocuments(rootId, DocumentsContract.getSearchDocumentsQuery(queryArgs),
projection);
}
@@ -732,7 +738,7 @@ public abstract class DocumentsProvider extends ContentProvider {
* @return a Bundle of Bundles.
* @see DocumentsContract#getDocumentMetadata(ContentResolver, Uri)
*/
- public @Nullable Bundle getDocumentMetadata(String documentId)
+ public @Nullable Bundle getDocumentMetadata(@NonNull String documentId)
throws FileNotFoundException {
throw new UnsupportedOperationException("Metadata not supported");
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 3a4998630544..487198ba4d45 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -70,6 +70,8 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -1224,7 +1226,7 @@ public final class MediaStore {
if (sv.isPrimary()) {
return VOLUME_EXTERNAL;
} else {
- return checkArgumentVolumeName(sv.getUuid());
+ return checkArgumentVolumeName(sv.getNormalizedUuid());
}
}
throw new IllegalStateException("Unknown volume at " + path);
@@ -2919,7 +2921,7 @@ public final class MediaStore {
if (vi.isPrimary()) {
volumeNames.add(VOLUME_EXTERNAL);
} else {
- volumeNames.add(vi.getFsUuid());
+ volumeNames.add(vi.getNormalizedFsUuid());
}
}
}
@@ -2953,8 +2955,7 @@ public final class MediaStore {
// When not one of the well-known values above, it must be a hex UUID
for (int i = 0; i < volumeName.length(); i++) {
final char c = volumeName.charAt(i);
- if (('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
- || ('0' <= c && c <= '9') || (c == '-')) {
+ if (('a' <= c && c <= 'f') || ('0' <= c && c <= '9') || (c == '-')) {
continue;
} else {
throw new IllegalArgumentException("Invalid volume name: " + volumeName);
@@ -2963,23 +2964,26 @@ public final class MediaStore {
return volumeName;
}
- /** {@hide} */
+ /**
+ * Return path where the given volume is mounted. Not valid for
+ * {@link #VOLUME_INTERNAL}.
+ *
+ * @hide
+ */
public static @NonNull File getVolumePath(@NonNull String volumeName)
throws FileNotFoundException {
if (TextUtils.isEmpty(volumeName)) {
throw new IllegalArgumentException();
}
- if (VOLUME_INTERNAL.equals(volumeName)) {
- return Environment.getDataDirectory();
- } else if (VOLUME_EXTERNAL.equals(volumeName)) {
+ if (VOLUME_EXTERNAL.equals(volumeName)) {
return Environment.getExternalStorageDirectory();
}
final StorageManager sm = AppGlobals.getInitialApplication()
.getSystemService(StorageManager.class);
for (VolumeInfo vi : sm.getVolumes()) {
- if (Objects.equals(vi.getFsUuid(), volumeName)) {
+ if (Objects.equals(vi.getNormalizedFsUuid(), volumeName)) {
final File path = vi.getPathForUser(UserHandle.myUserId());
if (path != null) {
return path;
@@ -2992,6 +2996,33 @@ public final class MediaStore {
}
/**
+ * Return paths that should be scanned for the given volume.
+ *
+ * @hide
+ */
+ public static @NonNull Collection<File> getVolumeScanPaths(@NonNull String volumeName)
+ throws FileNotFoundException {
+ if (TextUtils.isEmpty(volumeName)) {
+ throw new IllegalArgumentException();
+ }
+
+ final ArrayList<File> res = new ArrayList<>();
+ if (VOLUME_INTERNAL.equals(volumeName)) {
+ res.add(new File(Environment.getRootDirectory(), "media"));
+ res.add(new File(Environment.getOemDirectory(), "media"));
+ res.add(new File(Environment.getProductDirectory(), "media"));
+ } else {
+ res.add(getVolumePath(volumeName));
+ final UserManager um = AppGlobals.getInitialApplication()
+ .getSystemService(UserManager.class);
+ if (VOLUME_EXTERNAL.equals(volumeName) && um.isDemoUser()) {
+ res.add(Environment.getDataPreloadsMediaDirectory());
+ }
+ }
+ return res;
+ }
+
+ /**
* Uri for querying the state of the media scanner.
*/
public static Uri getMediaScannerUri() {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 516f49ccc910..643c473f8417 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3300,6 +3300,14 @@ public final class Settings {
public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1;
/**
+ * Control whether to enable adaptive sleep mode.
+ * @hide
+ */
+ public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
+
+ private static final Validator ADAPTIVE_SLEEP_VALIDATOR = BOOLEAN_VALIDATOR;
+
+ /**
* Control whether the process CPU usage meter should be shown.
*
* @deprecated This functionality is no longer available as of
@@ -4232,6 +4240,7 @@ public final class Settings {
SCREEN_BRIGHTNESS_MODE,
SCREEN_AUTO_BRIGHTNESS_ADJ,
SCREEN_BRIGHTNESS_FOR_VR,
+ ADAPTIVE_SLEEP,
VIBRATE_INPUT_DEVICES,
MODE_RINGER_STREAMS_AFFECTED,
TEXT_AUTO_REPLACE,
@@ -4307,6 +4316,7 @@ public final class Settings {
PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS);
PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FOR_VR);
PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_MODE);
+ PUBLIC_SETTINGS.add(ADAPTIVE_SLEEP);
PUBLIC_SETTINGS.add(MODE_RINGER_STREAMS_AFFECTED);
PUBLIC_SETTINGS.add(MUTE_STREAMS_AFFECTED);
PUBLIC_SETTINGS.add(VIBRATE_ON);
@@ -4411,6 +4421,7 @@ public final class Settings {
VALIDATORS.put(SCREEN_OFF_TIMEOUT, SCREEN_OFF_TIMEOUT_VALIDATOR);
VALIDATORS.put(SCREEN_BRIGHTNESS_FOR_VR, SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR);
VALIDATORS.put(SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_VALIDATOR);
+ VALIDATORS.put(ADAPTIVE_SLEEP, ADAPTIVE_SLEEP_VALIDATOR);
VALIDATORS.put(MODE_RINGER_STREAMS_AFFECTED, MODE_RINGER_STREAMS_AFFECTED_VALIDATOR);
VALIDATORS.put(MUTE_STREAMS_AFFECTED, MUTE_STREAMS_AFFECTED_VALIDATOR);
VALIDATORS.put(VIBRATE_ON, VIBRATE_ON_VALIDATOR);
@@ -13917,11 +13928,12 @@ public final class Settings {
* The following keys are supported:
*
* <pre>
- * enabled (boolean)
- * requires_targeting_p (boolean)
- * max_squeeze_remeasure_attempts (int)
- * edit_choices_before_sending (boolean)
- * show_in_heads_up (boolean)
+ * enabled (boolean)
+ * requires_targeting_p (boolean)
+ * max_squeeze_remeasure_attempts (int)
+ * edit_choices_before_sending (boolean)
+ * show_in_heads_up (boolean)
+ * min_num_system_generated_replies (int)
* </pre>
* @see com.android.systemui.statusbar.policy.SmartReplyConstants
* @hide
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 4dc10cd2e4cc..ffb524d5c2e4 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -103,10 +103,23 @@ public abstract class EuiccService extends Service {
*/
public static final String ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS =
"android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS";
+
/** @see android.telephony.euicc.EuiccManager#ACTION_PROVISION_EMBEDDED_SUBSCRIPTION */
public static final String ACTION_PROVISION_EMBEDDED_SUBSCRIPTION =
"android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION";
+ /** @see android.telephony.euicc.EuiccManager#ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED */
+ public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED =
+ "android.service.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
+
+ /** @see android.telephony.euicc.EuiccManager#ACTION_DELETE_SUBSCRIPTION_PRIVILEGED */
+ public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED =
+ "android.service.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
+
+ /** @see android.telephony.euicc.EuiccManager#ACTION_RENAME_SUBSCRIPTION_PRIVILEGED */
+ public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED =
+ "android.service.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+
// LUI resolution actions. These are called by the platform to resolve errors in situations that
// require user interaction.
// TODO(b/33075886): Define extras for any input parameters to these dialogs once they are
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index f7acfc5918a8..b0269e3325bf 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -110,13 +110,5 @@ interface IVrManager {
* @param standy True if the device is entering standby, false if it's exiting standby.
*/
void setStandbyEnabled(boolean standby);
-
- /**
- * Start VR Input method for the given packageName in {@param componentName}.
- * This method notifies InputMethodManagerService to use VR IME instead of
- * regular phone IME.
- */
- void setVrInputMethod(in ComponentName componentName);
-
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 0d7223d67907..b197c8a8a388 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -784,8 +784,11 @@ public abstract class WallpaperService extends Service {
// only internal implementations like ImageWallpaper
DisplayInfo displayInfo = new DisplayInfo();
mDisplay.getDisplayInfo(displayInfo);
- mLayout.width = Math.max(displayInfo.logicalWidth, myWidth);
- mLayout.height = Math.max(displayInfo.logicalHeight, myHeight);
+ final float layoutScale = Math.max(
+ (float) displayInfo.logicalHeight / (float) myHeight,
+ (float) displayInfo.logicalWidth / (float) myWidth);
+ mLayout.height = (int) (myHeight * layoutScale);
+ mLayout.width = (int) (myWidth * layoutScale);
mWindowFlags |= WindowManager.LayoutParams.FLAG_SCALED;
}
diff --git a/core/java/android/util/DocumentsStatsLog.java b/core/java/android/util/DocumentsStatsLog.java
new file mode 100644
index 000000000000..f483944c97c7
--- /dev/null
+++ b/core/java/android/util/DocumentsStatsLog.java
@@ -0,0 +1,168 @@
+/*
+ * 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.util;
+
+import android.annotation.SystemApi;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsProvider;
+
+/**
+ * DocumentsStatsLog provides APIs to send DocumentsUI related events to statsd.
+ * @hide
+ */
+@SystemApi
+public class DocumentsStatsLog {
+
+ private DocumentsStatsLog() {}
+
+ /**
+ * Logs when DocumentsUI is started, and how. Call this when DocumentsUI first starts up.
+ *
+ * @param action action that launches DocumentsUI.
+ * @param hasInitialUri is DocumentsUI launched with
+ * {@link DocumentsContract#EXTRA_INITIAL_URI}.
+ * @param mimeType the requested mime type.
+ * @param rootUri the resolved rootUri, or {@code null} if the provider doesn't
+ * support {@link DocumentsProvider#findDocumentPath(String, String)}
+ */
+ public static void logActivityLaunch(
+ int action, boolean hasInitialUri, int mimeType, int rootUri) {
+ StatsLog.write(StatsLog.DOCS_UI_LAUNCH_REPORTED, action, hasInitialUri, mimeType, rootUri);
+ }
+
+ /**
+ * Logs root visited event.
+ *
+ * @param scope whether it's in FILES or PICKER mode.
+ * @param root the root that user visited
+ */
+ public static void logRootVisited(int scope, int root) {
+ StatsLog.write(StatsLog.DOCS_UI_ROOT_VISITED, scope, root);
+ }
+
+ /**
+ * Logs file operation stats. Call this when a file operation has completed.
+ *
+ * @param provider whether it's system or external provider
+ * @param fileOp the file operation
+ */
+ public static void logFileOperation(int provider, int fileOp) {
+ StatsLog.write(StatsLog.DOCS_UI_PROVIDER_FILE_OP, provider, fileOp);
+ }
+
+ /**
+ * Logs file operation stats. Call this when a copy/move operation has completed with a specific
+ * mode.
+ *
+ * @param fileOp copy or move file operation
+ * @param mode the mode for copy and move operation
+ */
+ public static void logFileOperationCopyMoveMode(int fileOp, int mode) {
+ StatsLog.write(StatsLog.DOCS_UI_FILE_OP_COPY_MOVE_MODE_REPORTED, fileOp, mode);
+ }
+
+ /**
+ * Logs file sub operation stats. Call this when a file operation has failed.
+ *
+ * @param authority the authority of the source document
+ * @param subOp the sub-file operation
+ */
+ public static void logFileOperationFailure(int authority, int subOp) {
+ StatsLog.write(StatsLog.DOCS_UI_FILE_OP_FAILURE, authority, subOp);
+ }
+
+ /**
+ * Logs the cancellation of a file operation. Call this when a job is canceled
+ *
+ * @param fileOp the file operation.
+ */
+ public static void logFileOperationCanceled(int fileOp) {
+ StatsLog.write(StatsLog.DOCS_UI_FILE_OP_CANCELED, fileOp);
+ }
+
+ /**
+ * Logs startup time in milliseconds.
+ *
+ * @param startupMs
+ */
+ public static void logStartupMs(int startupMs) {
+ StatsLog.write(StatsLog.DOCS_UI_STARTUP_MS, startupMs);
+ }
+
+ /**
+ * Logs the action that was started by user.
+ *
+ * @param userAction
+ */
+ public static void logUserAction(int userAction) {
+ StatsLog.write(StatsLog.DOCS_UI_USER_ACTION_REPORTED, userAction);
+ }
+
+ /**
+ * Logs the invalid type when invalid scoped access is requested.
+ *
+ * @param type the type of invalid scoped access request.
+ */
+ public static void logInvalidScopedAccessRequest(int type) {
+ StatsLog.write(StatsLog.DOCS_UI_INVALID_SCOPED_ACCESS_REQUEST, type);
+ }
+
+ /**
+ * Logs the package name that launches docsui picker mode.
+ *
+ * @param packageName
+ */
+ public static void logPickerLaunchedFrom(String packageName) {
+ StatsLog.write(StatsLog.DOCS_UI_PICKER_LAUNCHED_FROM_REPORTED, packageName);
+ }
+
+ /**
+ * Logs the search type.
+ *
+ * @param searchType
+ */
+ public static void logSearchType(int searchType) {
+ StatsLog.write(StatsLog.DOCS_UI_SEARCH_TYPE_REPORTED, searchType);
+ }
+
+ /**
+ * Logs the search mode.
+ *
+ * @param searchMode
+ */
+ public static void logSearchMode(int searchMode) {
+ StatsLog.write(StatsLog.DOCS_UI_SEARCH_MODE_REPORTED, searchMode);
+ }
+
+ /**
+ * Logs the pick result information.
+ *
+ * @param actionCount total user action count during pick process.
+ * @param duration total time spent on pick process.
+ * @param fileCount number of picked files.
+ * @param isSearching are the picked files found by search.
+ * @param root the root where the picked files located.
+ * @param mimeType the mime type of the picked file. Only for single-select case.
+ * @param repeatedlyPickTimes number of times that the file has been picked before. Only for
+ * single-select case.
+ */
+ public static void logFilePick(int actionCount, long duration, int fileCount,
+ boolean isSearching, int root, int mimeType, int repeatedlyPickTimes) {
+ StatsLog.write(StatsLog.DOCS_UI_PICK_RESULT_REPORTED, actionCount, duration, fileCount,
+ isSearching, root, mimeType, repeatedlyPickTimes);
+ }
+}
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 33b3ff4fef59..7d9ec70a4599 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -237,6 +237,16 @@ public class GestureDetector {
private static final int LONG_PRESS = 2;
private static final int TAP = 3;
+ /**
+ * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain actions, such as
+ * scrolling, will be inhibited. However, to account for the possibility of incorrect
+ * classification, the default scrolling will only be inhibited if the gesture moves beyond
+ * (default touch slop * AMBIGUOUS_GESTURE_MULTIPLIER). Likewise, the default long press
+ * timeout will be increased for some situations where the default behaviour
+ * is to cancel it.
+ */
+ private static final int AMBIGUOUS_GESTURE_MULTIPLIER = 2;
+
private final Handler mHandler;
@UnsupportedAppUsage
private final OnGestureListener mListener;
@@ -292,27 +302,27 @@ public class GestureDetector {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case SHOW_PRESS:
- mListener.onShowPress(mCurrentDownEvent);
- break;
-
- case LONG_PRESS:
- dispatchLongPress();
- break;
-
- case TAP:
- // If the user's finger is still down, do not count it as a tap
- if (mDoubleTapListener != null) {
- if (!mStillDown) {
- mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
- } else {
- mDeferConfirmSingleTap = true;
+ case SHOW_PRESS:
+ mListener.onShowPress(mCurrentDownEvent);
+ break;
+
+ case LONG_PRESS:
+ dispatchLongPress();
+ break;
+
+ case TAP:
+ // If the user's finger is still down, do not count it as a tap
+ if (mDoubleTapListener != null) {
+ if (!mStillDown) {
+ mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
+ } else {
+ mDeferConfirmSingleTap = true;
+ }
}
- }
- break;
+ break;
- default:
- throw new RuntimeException("Unknown message " + msg); //never
+ default:
+ throw new RuntimeException("Unknown message " + msg); //never
}
}
}
@@ -427,7 +437,7 @@ public class GestureDetector {
if (context == null) {
//noinspection deprecation
touchSlop = ViewConfiguration.getTouchSlop();
- doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
+ doubleTapTouchSlop = touchSlop; // Hack rather than adding a hidden method for this
doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
//noinspection deprecation
mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
@@ -605,6 +615,10 @@ public class GestureDetector {
if (mInLongPress || mInContextClick) {
break;
}
+
+ final int motionClassification = ev.getClassification();
+ final boolean hasPendingLongPress = mHandler.hasMessages(LONG_PRESS);
+
final float scrollX = mLastFocusX - focusX;
final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
@@ -615,6 +629,31 @@ public class GestureDetector {
final int deltaY = (int) (focusY - mDownFocusY);
int distance = (deltaX * deltaX) + (deltaY * deltaY);
int slopSquare = isGeneratedGesture ? 0 : mTouchSlopSquare;
+
+ final boolean ambiguousGesture =
+ motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE;
+ final boolean shouldInhibitDefaultAction =
+ hasPendingLongPress && ambiguousGesture;
+ if (shouldInhibitDefaultAction) {
+ // Inhibit default long press
+ if (distance > slopSquare) {
+ // The default action here is to remove long press. But if the touch
+ // slop below gets increased, and we never exceed the modified touch
+ // slop while still receiving AMBIGUOUS_GESTURE, we risk that *nothing*
+ // will happen in response to user input. To prevent this,
+ // reschedule long press with a modified timeout.
+ mHandler.removeMessages(LONG_PRESS);
+ final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
+ mHandler.sendEmptyMessageAtTime(LONG_PRESS, ev.getDownTime()
+ + longPressTimeout * AMBIGUOUS_GESTURE_MULTIPLIER);
+ }
+ // Inhibit default scroll. If a gesture is ambiguous, we prevent scroll
+ // until the gesture is resolved.
+ // However, for safety, simply increase the touch slop in case the
+ // classification is erroneous. Since the value is squared, multiply twice.
+ slopSquare *= AMBIGUOUS_GESTURE_MULTIPLIER * AMBIGUOUS_GESTURE_MULTIPLIER;
+ }
+
if (distance > slopSquare) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
@@ -633,6 +672,12 @@ public class GestureDetector {
mLastFocusX = focusX;
mLastFocusY = focusY;
}
+ final boolean deepPress =
+ motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;
+ if (deepPress && hasPendingLongPress) {
+ mHandler.removeMessages(LONG_PRESS);
+ mHandler.sendEmptyMessage(LONG_PRESS);
+ }
break;
case MotionEvent.ACTION_UP:
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 330d72f139db..42ac8801629f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -51,6 +51,7 @@ import android.view.IInputFilter;
import android.view.AppTransitionAnimationSpec;
import android.view.WindowContentFrameStats;
import android.view.WindowManager;
+import android.view.SurfaceControl;
/**
* System private interface to the window manager.
@@ -555,8 +556,8 @@ interface IWindowManager
* display content info to any SurfaceControl, as this would be a security issue.
*
* @param displayId The id of the display.
- * @param surfaceControlHandle The SurfaceControl handle that the top level layers for the
+ * @param surfaceControlHandle The SurfaceControl that the top level layers for the
* display should be re-parented to.
*/
- void reparentDisplayContent(int displayId, in IBinder surfaceControlHandle);
+ void reparentDisplayContent(int displayId, in SurfaceControl sc);
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index ce71b07da805..7c1465b443e4 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -161,8 +161,9 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
private Insets getInsetsFromState(InsetsState state, Rect frame,
@Nullable @InsetSide SparseIntArray typeSideMap) {
return state.calculateInsets(frame, false /* isScreenRound */,
- false /* alwaysConsumerNavBar */, null /* displayCutout */, typeSideMap)
- .getInsets(mTypes);
+ false /* alwaysConsumerNavBar */, null /* displayCutout */,
+ null /* legacyContentInsets */, null /* legacyStableInsets */, typeSideMap)
+ .getInsets(mTypes);
}
private Insets sanitize(Insets insets) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c2ade764ca81..4b1d1ec15750 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -56,6 +56,9 @@ public class InsetsController implements WindowInsetsController {
private final Runnable mAnimCallback;
+ private final Rect mLastLegacyContentInsets = new Rect();
+ private final Rect mLastLegacyStableInsets = new Rect();
+
public InsetsController(ViewRootImpl viewRoot) {
mViewRoot = viewRoot;
mAnimCallback = () -> {
@@ -70,6 +73,7 @@ public class InsetsController implements WindowInsetsController {
}
WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(),
+ mLastLegacyContentInsets, mLastLegacyStableInsets,
null /* typeSideMap */);
mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);
};
@@ -102,8 +106,12 @@ public class InsetsController implements WindowInsetsController {
*/
@VisibleForTesting
public WindowInsets calculateInsets(boolean isScreenRound,
- boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+ boolean alwaysConsumeNavBar, DisplayCutout cutout, Rect legacyContentInsets,
+ Rect legacyStableInsets) {
+ mLastLegacyContentInsets.set(legacyContentInsets);
+ mLastLegacyStableInsets.set(legacyStableInsets);
mLastInsets = mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
+ legacyContentInsets, legacyStableInsets,
null /* typeSideMap */);
return mLastInsets;
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index cf8c0707828d..529776e542ee 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.WindowInsets.Type.indexOf;
import android.annotation.IntDef;
@@ -119,11 +120,17 @@ public class InsetsState implements Parcelable {
*/
public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
boolean alwaysConsumeNavBar, DisplayCutout cutout,
+ @Nullable Rect legacyContentInsets, @Nullable Rect legacyStableInsets,
@Nullable @InsetSide SparseIntArray typeSideMap) {
Insets[] typeInsetsMap = new Insets[Type.SIZE];
Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
final Rect relativeFrame = new Rect(frame);
final Rect relativeFrameMax = new Rect(frame);
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_IME
+ && legacyContentInsets != null && legacyStableInsets != null) {
+ WindowInsets.assignCompatInsets(typeInsetsMap, legacyContentInsets);
+ WindowInsets.assignCompatInsets(typeMaxInsetsMap, legacyStableInsets);
+ }
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
InsetsSource source = mSources.get(type);
if (source == null) {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index f3cb3767ec9d..7fcb2afa48a9 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -185,6 +185,18 @@ public class Surface implements Parcelable {
}
/**
+ * Create a Surface assosciated with a given {@link SurfaceControl}. Buffers submitted to this
+ * surface will be displayed by the system compositor according to the parameters
+ * specified by the control. Multiple surfaces may be constructed from one SurfaceControl,
+ * but only one can be connected (e.g. have an active EGL context) at a time.
+ *
+ * @param from The SurfaceControl to assosciate this Surface with
+ */
+ public Surface(SurfaceControl from) {
+ copyFrom(from);
+ }
+
+ /**
* Create Surface from a {@link SurfaceTexture}.
*
* Images drawn to the Surface will be made available to the {@link
@@ -494,7 +506,6 @@ public class Surface implements Parcelable {
* in to it.
*
* @param other {@link SurfaceControl} to copy from.
- *
* @hide
*/
@UnsupportedAppUsage
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5e98236f7535..863b717008d2 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -27,6 +27,10 @@ import static android.view.Surface.ROTATION_90;
import static android.view.SurfaceControlProto.HASH_CODE;
import static android.view.SurfaceControlProto.NAME;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
@@ -58,10 +62,16 @@ import libcore.util.NativeAllocationRegistry;
import java.io.Closeable;
/**
- * SurfaceControl
- * @hide
+ * Handle to an on-screen Surface managed by the system compositor. The SurfaceControl is
+ * a combination of a buffer source, and metadata about how to display the buffers.
+ * By constructing a {@link Surface} from this SurfaceControl you can submit buffers to be
+ * composited. Using {@link SurfaceControl.Transaction} you can manipulate various
+ * properties of how the buffer will be displayed on-screen. SurfaceControl's are
+ * arranged into a scene-graph like hierarchy, and as such any SurfaceControl may have
+ * a parent. Geometric properties like transform, crop, and Z-ordering will be inherited
+ * from the parent, as if the child were content in the parents buffer stream.
*/
-public class SurfaceControl implements Parcelable {
+public final class SurfaceControl implements Parcelable {
private static final String TAG = "SurfaceControl";
private static native long nativeCreate(SurfaceSession session, String name,
@@ -103,6 +113,8 @@ public class SurfaceControl implements Parcelable {
float dtdy, float dsdy);
private static native void nativeSetColorTransform(long transactionObj, long nativeObject,
float[] matrix, float[] translation);
+ private static native void nativeSetGeometry(long transactionObj, long nativeObject,
+ Rect sourceCrop, Rect dest, long orientation);
private static native void nativeSetColor(long transactionObj, long nativeObject, float[] color);
private static native void nativeSetFlags(long transactionObj, long nativeObject,
int flags, int mask);
@@ -156,7 +168,7 @@ public class SurfaceControl implements Parcelable {
private static native void nativeReparentChildren(long transactionObj, long nativeObject,
IBinder handle);
private static native void nativeReparent(long transactionObj, long nativeObject,
- IBinder parentHandle);
+ long newParentNativeObject);
private static native void nativeSeverChildren(long transactionObj, long nativeObject);
private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
int scalingMode);
@@ -331,8 +343,7 @@ public class SurfaceControl implements Parcelable {
*/
public static final int BUILT_IN_DISPLAY_ID_HDMI = 1;
- /* Display power modes * /
-
+ // Display power modes.
/**
* Display power mode off: used while blanking the screen.
* Use only with {@link SurfaceControl#setDisplayPowerMode}.
@@ -403,7 +414,6 @@ public class SurfaceControl implements Parcelable {
/**
* Builder class for {@link SurfaceControl} objects.
- * @hide
*/
public static class Builder {
private SurfaceSession mSession;
@@ -427,8 +437,14 @@ public class SurfaceControl implements Parcelable {
}
/**
- * Construct a new {@link SurfaceControl} with the set parameters.
- * @hide
+ * Begin building a SurfaceControl.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Construct a new {@link SurfaceControl} with the set parameters. The builder
+ * remains valid.
*/
public SurfaceControl build() {
if (mWidth < 0 || mHeight < 0) {
@@ -447,7 +463,6 @@ public class SurfaceControl implements Parcelable {
* Set a debugging-name for the SurfaceControl.
*
* @param name A name to identify the Surface in debugging.
- * @hide
*/
public Builder setName(String name) {
mName = name;
@@ -459,9 +474,9 @@ public class SurfaceControl implements Parcelable {
*
* @param width The buffer width in pixels.
* @param height The buffer height in pixels.
- * @hide
*/
- public Builder setBufferSize(int width, int height) {
+ public Builder setBufferSize(@IntRange(from = 0) int width,
+ @IntRange(from = 0) int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException(
"width and height must be positive");
@@ -474,8 +489,8 @@ public class SurfaceControl implements Parcelable {
/**
* Set the pixel format of the controlled surface's buffers, using constants from
* {@link android.graphics.PixelFormat}.
- * @hide
*/
+ @NonNull
public Builder setFormat(@PixelFormat.Format int format) {
mFormat = format;
return this;
@@ -490,6 +505,7 @@ public class SurfaceControl implements Parcelable {
* @param protectedContent Whether to require a protected sink.
* @hide
*/
+ @NonNull
public Builder setProtected(boolean protectedContent) {
if (protectedContent) {
mFlags |= PROTECTED_APP;
@@ -506,6 +522,7 @@ public class SurfaceControl implements Parcelable {
* not a complete prevention of readback as {@link #setProtected}.
* @hide
*/
+ @NonNull
public Builder setSecure(boolean secure) {
if (secure) {
mFlags |= SECURE;
@@ -537,8 +554,8 @@ public class SurfaceControl implements Parcelable {
* If the underlying buffer lacks an alpha channel, it is as if setOpaque(true)
* were set automatically.
* @param opaque Whether the Surface is OPAQUE.
- * @hide
*/
+ @NonNull
public Builder setOpaque(boolean opaque) {
if (opaque) {
mFlags |= OPAQUE;
@@ -556,9 +573,9 @@ public class SurfaceControl implements Parcelable {
* of the parent.
*
* @param parent The parent control.
- * @hide
*/
- public Builder setParent(SurfaceControl parent) {
+ @NonNull
+ public Builder setParent(@Nullable SurfaceControl parent) {
mParent = parent;
return this;
}
@@ -673,9 +690,6 @@ public class SurfaceControl implements Parcelable {
private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
SurfaceControl parent, int windowType, int ownerUid)
throws OutOfResourcesException, IllegalArgumentException {
- if (session == null) {
- throw new IllegalArgumentException("session must not be null");
- }
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
@@ -729,9 +743,6 @@ public class SurfaceControl implements Parcelable {
mCloseGuard.open("release");
}
- /**
- * @hide
- */
public void readFromParcel(Parcel in) {
if (in == null) {
throw new IllegalArgumentException("source must not be null");
@@ -748,17 +759,11 @@ public class SurfaceControl implements Parcelable {
assignNativeObject(object);
}
- /**
- * @hide
- */
@Override
public int describeContents() {
return 0;
}
- /**
- * @hide
- */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mName);
@@ -791,9 +796,6 @@ public class SurfaceControl implements Parcelable {
proto.end(token);
}
- /**
- * @hide
- */
public static final Creator<SurfaceControl> CREATOR
= new Creator<SurfaceControl>() {
public SurfaceControl createFromParcel(Parcel in) {
@@ -823,10 +825,12 @@ public class SurfaceControl implements Parcelable {
}
/**
- * Release the local reference to the server-side surface.
- * Always call release() when you're done with a Surface.
- * This will make the surface invalid.
- * @hide
+ * Release the local reference to the server-side surface. The surface
+ * may continue to exist on-screen as long as its parent continues
+ * to exist. To explicitly remove a surface from the screen use
+ * {@link Transaction#reparent} with a null-parent.
+ *
+ * Always call release() when you're done with a SurfaceControl.
*/
public void release() {
if (mNativeObject != 0) {
@@ -866,7 +870,10 @@ public class SurfaceControl implements Parcelable {
}
/**
- * @hide
+ * Check whether this instance points to a valid layer with the system-compositor. For
+ * example this may be false if construction failed, or the layer was released.
+ *
+ * @return Whether this SurfaceControl is valid.
*/
public boolean isValid() {
return mNativeObject != 0;
@@ -962,9 +969,9 @@ public class SurfaceControl implements Parcelable {
/**
* @hide
*/
- public void reparent(IBinder newParentHandle) {
+ public void reparent(SurfaceControl newParent) {
synchronized(SurfaceControl.class) {
- sGlobalTransaction.reparent(this, newParentHandle);
+ sGlobalTransaction.reparent(this, newParent);
}
}
@@ -1270,9 +1277,6 @@ public class SurfaceControl implements Parcelable {
}
}
- /**
- * @hide
- */
@Override
public String toString() {
return "Surface(name=" + mName + ")/@0x" +
@@ -1286,6 +1290,7 @@ public class SurfaceControl implements Parcelable {
/**
* Describes the properties of a physical display known to surface flinger.
+ * @hide
*/
public static final class PhysicalDisplayInfo {
/**
@@ -1777,9 +1782,12 @@ public class SurfaceControl implements Parcelable {
}
/**
- * @hide
+ * An atomic set of changes to a set of SurfaceControl.
*/
public static class Transaction implements Closeable {
+ /**
+ * @hide
+ */
public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
Transaction.class.getClassLoader(),
nativeGetNativeTransactionFinalizer(), 512);
@@ -1789,9 +1797,12 @@ public class SurfaceControl implements Parcelable {
Runnable mFreeNativeResources;
/**
- * @hide
+ * Open a new transaction object. The transaction may be filed with commands to
+ * manipulate {@link SurfaceControl} instances, and then applied atomically with
+ * {@link #apply}. Eventually the user should invoke {@link #close}, when the object
+ * is no longer required. Note however that re-using a transaction after a call to apply
+ * is allowed as a convenience.
*/
- @UnsupportedAppUsage
public Transaction() {
mNativeObject = nativeCreateTransaction();
mFreeNativeResources
@@ -1801,9 +1812,7 @@ public class SurfaceControl implements Parcelable {
/**
* Apply the transaction, clearing it's state, and making it usable
* as a new transaction.
- * @hide
*/
- @UnsupportedAppUsage
public void apply() {
apply(false);
}
@@ -1811,7 +1820,6 @@ public class SurfaceControl implements Parcelable {
/**
* Close the transaction, if the transaction was not already applied this will cancel the
* transaction.
- * @hide
*/
@Override
public void close() {
@@ -1841,6 +1849,27 @@ public class SurfaceControl implements Parcelable {
}
/**
+ * Toggle the visibility of a given Layer and it's sub-tree.
+ *
+ * @param sc The SurfaceControl for which to set the visibility
+ * @param visible The new visibility
+ * @return This transaction object.
+ */
+ @NonNull
+ public Transaction setVisibility(@NonNull SurfaceControl sc, boolean visible) {
+ sc.checkNotReleased();
+ if (visible) {
+ return show(sc);
+ } else {
+ return hide(sc);
+ }
+ }
+
+ /**
+ * Request that a given surface and it's sub-tree be shown.
+ *
+ * @param sc The surface to show.
+ * @return This transaction.
* @hide
*/
@UnsupportedAppUsage
@@ -1851,6 +1880,10 @@ public class SurfaceControl implements Parcelable {
}
/**
+ * Request that a given surface and it's sub-tree be hidden.
+ *
+ * @param sc The surface to hidden.
+ * @return This transaction.
* @hide
*/
@UnsupportedAppUsage
@@ -1871,10 +1904,17 @@ public class SurfaceControl implements Parcelable {
}
/**
- * @hide
+ * Set the default buffer size for the SurfaceControl, if there is an
+ * {@link Surface} assosciated with the control, then
+ * this will be the default size for buffers dequeued from it.
+ * @param sc The surface to set the buffer size for.
+ * @param w The default width
+ * @param h The default height
+ * @return This Transaction
*/
- @UnsupportedAppUsage
- public Transaction setBufferSize(SurfaceControl sc, int w, int h) {
+ @NonNull
+ public Transaction setBufferSize(@NonNull SurfaceControl sc,
+ @IntRange(from = 0) int w, @IntRange(from = 0) int h) {
sc.checkNotReleased();
mResizedSurfaces.put(sc, new Point(w, h));
nativeSetSize(mNativeObject, sc.mNativeObject, w, h);
@@ -1882,10 +1922,17 @@ public class SurfaceControl implements Parcelable {
}
/**
- * @hide
+ * Set the Z-order for a given SurfaceControl, relative to it's siblings.
+ * If two siblings share the same Z order the ordering is undefined. Surfaces
+ * with a negative Z will be placed below the parent surface.
+ *
+ * @param sc The SurfaceControl to set the Z order on
+ * @param z The Z-order
+ * @return This Transaction.
*/
- @UnsupportedAppUsage
- public Transaction setLayer(SurfaceControl sc, int z) {
+ @NonNull
+ public Transaction setLayer(@NonNull SurfaceControl sc,
+ @IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) int z) {
sc.checkNotReleased();
nativeSetLayer(mNativeObject, sc.mNativeObject, z);
return this;
@@ -1912,10 +1959,15 @@ public class SurfaceControl implements Parcelable {
}
/**
- * @hide
+ * Set the alpha for a given surface. If the alpha is non-zero the SurfaceControl
+ * will be blended with the Surfaces under it according to the specified ratio.
+ *
+ * @param sc The given SurfaceControl.
+ * @param alpha The alpha to set.
*/
- @UnsupportedAppUsage
- public Transaction setAlpha(SurfaceControl sc, float alpha) {
+ @NonNull
+ public Transaction setAlpha(@NonNull SurfaceControl sc,
+ @FloatRange(from = 0.0, to = 1.0) float alpha) {
sc.checkNotReleased();
nativeSetAlpha(mNativeObject, sc.mNativeObject, alpha);
return this;
@@ -1947,6 +1999,25 @@ public class SurfaceControl implements Parcelable {
}
/**
+ * Specify how the buffer assosciated with this Surface is mapped in to the
+ * parent coordinate space. The source frame will be scaled to fit the destination
+ * frame, after being rotated according to the orientation parameter.
+ *
+ * @param sc The SurfaceControl to specify the geometry of
+ * @param sourceCrop The source rectangle in buffer space. Or null for the entire buffer.
+ * @param destFrame The destination rectangle in parent space. Or null for the source frame.
+ * @param orientation The buffer rotation
+ * @return This transaction object.
+ */
+ @NonNull
+ public Transaction setGeometry(@NonNull SurfaceControl sc, @Nullable Rect sourceCrop,
+ @Nullable Rect destFrame, @Surface.Rotation int orientation) {
+ sc.checkNotReleased();
+ nativeSetGeometry(mNativeObject, sc.mNativeObject, sourceCrop, destFrame, orientation);
+ return this;
+ }
+
+ /**
* @hide
*/
@UnsupportedAppUsage
@@ -2023,20 +2094,20 @@ public class SurfaceControl implements Parcelable {
return this;
}
- @UnsupportedAppUsage
/**
* @hide
*/
+ @UnsupportedAppUsage
public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
sc.checkNotReleased();
nativeSetLayerStack(mNativeObject, sc.mNativeObject, layerStack);
return this;
}
- @UnsupportedAppUsage
/**
* @hide
*/
+ @UnsupportedAppUsage
public Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle,
long frameNumber) {
if (frameNumber < 0) {
@@ -2047,10 +2118,10 @@ public class SurfaceControl implements Parcelable {
return this;
}
- @UnsupportedAppUsage
/**
* @hide
*/
+ @UnsupportedAppUsage
public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
long frameNumber) {
if (frameNumber < 0) {
@@ -2071,13 +2142,25 @@ public class SurfaceControl implements Parcelable {
return this;
}
- /** Re-parents a specific child layer to a new parent
- * @hide
+ /**
+ * Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
+ * crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
+ * parent Surface.
+ *
+ * @param sc The SurfaceControl to reparent
+ * @param newParent The new parent for the given control.
+ * @return This Transaction
*/
- public Transaction reparent(SurfaceControl sc, IBinder newParentHandle) {
+ @NonNull
+ public Transaction reparent(@NonNull SurfaceControl sc,
+ @Nullable SurfaceControl newParent) {
sc.checkNotReleased();
- nativeReparent(mNativeObject, sc.mNativeObject,
- newParentHandle);
+ long otherObject = 0;
+ if (newParent != null) {
+ newParent.checkNotReleased();
+ otherObject = newParent.mNativeObject;
+ }
+ nativeReparent(mNativeObject, sc.mNativeObject, otherObject);
return this;
}
@@ -2245,9 +2328,12 @@ public class SurfaceControl implements Parcelable {
/**
* Merge the other transaction into this transaction, clearing the
* other transaction as if it had been applied.
- * @hide
+ *
+ * @param other The transaction to merge in to this one.
+ * @return This transaction.
*/
- public Transaction merge(Transaction other) {
+ @NonNull
+ public Transaction merge(@NonNull Transaction other) {
mResizedSurfaces.putAll(other.mResizedSurfaces);
other.mResizedSurfaces.clear();
nativeMergeTransaction(mNativeObject, other.mNativeObject);
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 61fb00d3fe59..45e6c50d9ada 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -120,10 +120,11 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
final Rect mScreenRect = new Rect();
SurfaceSession mSurfaceSession;
- SurfaceControlWithBackground mSurfaceControl;
+ SurfaceControl mSurfaceControl;
// In the case of format changes we switch out the surface in-place
// we need to preserve the old one until the new one has drawn.
SurfaceControl mDeferredDestroySurfaceControl;
+ SurfaceControl mBackgroundControl;
final Rect mTmpRect = new Rect();
final Configuration mConfiguration = new Configuration();
@@ -487,6 +488,29 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
}
}
+ private void updateBackgroundVisibilityInTransaction() {
+ if (mBackgroundControl == null) {
+ return;
+ }
+ if ((mSurfaceFlags & PixelFormat.OPAQUE) == 0) {
+ mBackgroundControl.show();
+ mBackgroundControl.setLayer(Integer.MIN_VALUE);
+ } else {
+ mBackgroundControl.hide();
+ }
+ }
+
+ private void releaseSurfaces() {
+ if (mSurfaceControl != null) {
+ mSurfaceControl.destroy();
+ mSurfaceControl = null;
+ }
+ if (mBackgroundControl != null) {
+ mBackgroundControl.destroy();
+ mBackgroundControl = null;
+ }
+ }
+
/** @hide */
protected void updateSurface() {
if (!mHaveFrame) {
@@ -553,14 +577,21 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
updateOpaqueFlag();
final String name = "SurfaceView - " + viewRoot.getTitle().toString();
- mSurfaceControl = new SurfaceControlWithBackground(
- name,
- (mSurfaceFlags & SurfaceControl.OPAQUE) != 0,
- new SurfaceControl.Builder(mSurfaceSession)
- .setBufferSize(mSurfaceWidth, mSurfaceHeight)
- .setFormat(mFormat)
- .setParent(viewRoot.getSurfaceControl())
- .setFlags(mSurfaceFlags));
+ mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName(name)
+ .setOpaque((mSurfaceFlags & SurfaceControl.OPAQUE) != 0)
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
+ .setFormat(mFormat)
+ .setParent(viewRoot.getSurfaceControl())
+ .setFlags(mSurfaceFlags)
+ .build();
+ mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName("Background for -" + name)
+ .setOpaque(true)
+ .setColorLayer(true)
+ .setParent(mSurfaceControl)
+ .build();
+
} else if (mSurfaceControl == null) {
return;
}
@@ -577,11 +608,13 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
SurfaceControl.openTransaction();
try {
mSurfaceControl.setLayer(mSubLayer);
+
if (mViewVisibility) {
mSurfaceControl.show();
} else {
mSurfaceControl.hide();
}
+ updateBackgroundVisibilityInTransaction();
// While creating the surface, we will set it's initial
// geometry. Outside of that though, we should generally
@@ -724,8 +757,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
if (mSurfaceControl != null && !mSurfaceCreated) {
mSurface.release();
- mSurfaceControl.destroy();
- mSurfaceControl = null;
+ releaseSurfaces();
}
}
} catch (Exception ex) {
@@ -823,7 +855,6 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
final ViewRootImpl viewRoot = getViewRootImpl();
applySurfaceTransforms(mSurfaceControl, position, frameNumber);
- applySurfaceTransforms(mSurfaceControl.mBackgroundControl, position, frameNumber);
applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface,
frameNumber);
@@ -950,7 +981,19 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
* @hide
*/
public void setResizeBackgroundColor(int bgColor) {
- mSurfaceControl.setBackgroundColor(bgColor);
+ if (mBackgroundControl == null) {
+ return;
+ }
+
+ final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
+ Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
+
+ SurfaceControl.openTransaction();
+ try {
+ mBackgroundControl.setColor(colorComponents);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
}
@UnsupportedAppUsage
@@ -1128,154 +1171,12 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
};
/**
- * @hide
+ * Return a SurfaceControl which can be used for parenting Surfaces to
+ * this SurfaceView.
+ *
+ * @return The SurfaceControl for this SurfaceView.
*/
public SurfaceControl getSurfaceControl() {
return mSurfaceControl;
}
-
- class SurfaceControlWithBackground extends SurfaceControl {
- SurfaceControl mBackgroundControl;
- private boolean mOpaque = true;
- public boolean mVisible = false;
-
- public SurfaceControlWithBackground(String name, boolean opaque, SurfaceControl.Builder b)
- throws Exception {
- super(b.setName(name).build());
-
- mBackgroundControl = b.setName("Background for -" + name)
- .setFormat(OPAQUE)
- // Unset the buffer size of the background color layer.
- .setBufferSize(0, 0)
- .setColorLayer(true)
- .build();
- mOpaque = opaque;
- }
-
- @Override
- public void setAlpha(float alpha) {
- super.setAlpha(alpha);
- mBackgroundControl.setAlpha(alpha);
- }
-
- @Override
- public void setLayer(int zorder) {
- super.setLayer(zorder);
- // -3 is below all other child layers as SurfaceView never goes below -2
- mBackgroundControl.setLayer(-3);
- }
-
- @Override
- public void setPosition(float x, float y) {
- super.setPosition(x, y);
- mBackgroundControl.setPosition(x, y);
- }
-
- @Override
- public void setBufferSize(int w, int h) {
- super.setBufferSize(w, h);
- // The background surface is a color layer so we do not set a size.
- }
-
- @Override
- public void setWindowCrop(Rect crop) {
- super.setWindowCrop(crop);
- mBackgroundControl.setWindowCrop(crop);
- }
-
- @Override
- public void setWindowCrop(int width, int height) {
- super.setWindowCrop(width, height);
- mBackgroundControl.setWindowCrop(width, height);
- }
-
- @Override
- public void setLayerStack(int layerStack) {
- super.setLayerStack(layerStack);
- mBackgroundControl.setLayerStack(layerStack);
- }
-
- @Override
- public void setOpaque(boolean isOpaque) {
- super.setOpaque(isOpaque);
- mOpaque = isOpaque;
- updateBackgroundVisibility();
- }
-
- @Override
- public void setSecure(boolean isSecure) {
- super.setSecure(isSecure);
- }
-
- @Override
- public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- super.setMatrix(dsdx, dtdx, dsdy, dtdy);
- mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
- }
-
- @Override
- public void hide() {
- super.hide();
- mVisible = false;
- updateBackgroundVisibility();
- }
-
- @Override
- public void show() {
- super.show();
- mVisible = true;
- updateBackgroundVisibility();
- }
-
- @Override
- public void destroy() {
- super.destroy();
- mBackgroundControl.destroy();
- }
-
- @Override
- public void release() {
- super.release();
- mBackgroundControl.release();
- }
-
- @Override
- public void setTransparentRegionHint(Region region) {
- super.setTransparentRegionHint(region);
- mBackgroundControl.setTransparentRegionHint(region);
- }
-
- @Override
- public void deferTransactionUntil(IBinder handle, long frame) {
- super.deferTransactionUntil(handle, frame);
- mBackgroundControl.deferTransactionUntil(handle, frame);
- }
-
- @Override
- public void deferTransactionUntil(Surface barrier, long frame) {
- super.deferTransactionUntil(barrier, frame);
- mBackgroundControl.deferTransactionUntil(barrier, frame);
- }
-
- /** Set the color to fill the background with. */
- private void setBackgroundColor(int bgColor) {
- final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
- Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
-
- SurfaceControl.openTransaction();
- try {
- mBackgroundControl.setColor(colorComponents);
- } finally {
- SurfaceControl.closeTransaction();
- }
- }
-
- void updateBackgroundVisibility() {
- if (mOpaque && mVisible) {
- mBackgroundControl.show();
- } else {
- mBackgroundControl.hide();
- }
- }
- }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a1aa06e901dc..2014ec2417ac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static java.lang.Math.max;
@@ -5123,7 +5124,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
- sBrokenInsetsDispatch = !ViewRootImpl.USE_NEW_INSETS
+ sBrokenInsetsDispatch = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
|| targetSdkVersion < Build.VERSION_CODES.Q;
sCompatibilityDone = true;
@@ -8226,7 +8227,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* </ul>
*/
public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
- onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+ "onProvideContentCaptureStructure() for " + getClass().getSimpleName());
+ }
+ try {
+ onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
}
/** @hide */
@@ -9016,6 +9025,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* </ol>
*/
private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+ "notifyContentCapture(" + appeared + ") for " + getClass().getSimpleName());
+ }
+ try {
+ notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(appeared);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+
+ private void notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(boolean appeared) {
// First check if context has client, so it saves a service lookup when it doesn't
if (!mContext.isContentCaptureSupported()) return;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 27d4ea4a777a..a031b704627f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -163,13 +163,16 @@ public final class ViewRootImpl implements ViewParent,
private static final boolean MT_RENDERER_AVAILABLE = true;
/**
- * If set to true, the view system will switch from using rectangles retrieved from window to
+ * If set to 2, the view system will switch from using rectangles retrieved from window to
* dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
* directly from the full configuration, enabling richer information about the insets state, as
* well as new APIs to control it frame-by-frame, and synchronize animations with it.
* <p>
- * Only switch this to true once the new insets system is productionized and the old APIs are
+ * Only set this to 2 once the new insets system is productionized and the old APIs are
* fully migrated over.
+ * <p>
+ * If set to 1, this will switch to a mode where we only use the new approach for IME, but not
+ * for the status/navigation bar.
*/
private static final String USE_NEW_INSETS_PROPERTY = "persist.wm.new_insets";
@@ -177,8 +180,26 @@ public final class ViewRootImpl implements ViewParent,
* @see #USE_NEW_INSETS_PROPERTY
* @hide
*/
- public static final boolean USE_NEW_INSETS =
- SystemProperties.getBoolean(USE_NEW_INSETS_PROPERTY, false);
+ public static final int sNewInsetsMode =
+ SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, 0);
+
+ /**
+ * @see #USE_NEW_INSETS_PROPERTY
+ * @hide
+ */
+ public static final int NEW_INSETS_MODE_NONE = 0;
+
+ /**
+ * @see #USE_NEW_INSETS_PROPERTY
+ * @hide
+ */
+ public static final int NEW_INSETS_MODE_IME = 1;
+
+ /**
+ * @see #USE_NEW_INSETS_PROPERTY
+ * @hide
+ */
+ public static final int NEW_INSETS_MODE_FULL = 2;
/**
* Set this system property to true to force the view hierarchy to render
@@ -1367,7 +1388,7 @@ public final class ViewRootImpl implements ViewParent,
}
void notifyInsetsChanged() {
- if (!USE_NEW_INSETS) {
+ if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
return;
}
mApplyInsetsRequested = true;
@@ -1855,10 +1876,11 @@ public final class ViewRootImpl implements ViewParent,
}
contentInsets = ensureInsetsNonNegative(contentInsets, "content");
stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
- if (USE_NEW_INSETS) {
+ if (sNewInsetsMode != NEW_INSETS_MODE_NONE) {
mLastWindowInsets = mInsetsController.calculateInsets(
mContext.getResources().getConfiguration().isScreenRound(),
- mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+ mAttachInfo.mAlwaysConsumeNavBar, displayCutout,
+ contentInsets, stableInsets);
} else {
mLastWindowInsets = new WindowInsets(contentInsets, stableInsets,
mContext.getResources().getConfiguration().isScreenRound(),
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 9ac0ca96ce63..e8088303eac7 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -217,7 +217,10 @@ public final class WindowInsets {
return typeInsetMap;
}
- private static void assignCompatInsets(Insets[] typeInsetMap, Rect insets) {
+ /**
+ * @hide
+ */
+ static void assignCompatInsets(Insets[] typeInsetMap, Rect insets) {
typeInsetMap[indexOf(TOP_BAR)] = Insets.of(0, insets.top, 0, 0);
typeInsetMap[indexOf(SIDE_BARS)] = Insets.of(insets.left, 0, insets.right, insets.bottom);
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 86c5f188ffab..10b99eca0fa8 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -922,19 +922,6 @@ public final class InputMethodManager {
}
}
- /**
- * Returns a list of VR InputMethod currently installed.
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
- public List<InputMethodInfo> getVrInputMethodList() {
- try {
- return mService.getVrInputMethodList();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
public List<InputMethodInfo> getEnabledInputMethodList() {
try {
return mService.getEnabledInputMethodList();
diff --git a/core/java/android/webkit/WebViewRenderer.java b/core/java/android/webkit/WebViewRenderer.java
index 532825485ed3..fc38cd9bd4a2 100644
--- a/core/java/android/webkit/WebViewRenderer.java
+++ b/core/java/android/webkit/WebViewRenderer.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.annotation.SystemApi;
+
/**
* WebViewRenderer provides an opaque handle to a {@link WebView} renderer.
*/
@@ -40,6 +42,7 @@ public abstract class WebViewRenderer {
* This class cannot be created by applications.
* @hide
*/
+ @SystemApi
public WebViewRenderer() {
}
}
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index a27dbeae2f3a..18c4b467dfba 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -74,6 +74,16 @@ public abstract class FileSystemProvider extends DocumentsProvider {
private static final boolean LOG_INOTIFY = false;
+ protected static final String SUPPORTED_QUERY_ARGS = joinNewline(
+ DocumentsContract.QUERY_ARG_DISPLAY_NAME,
+ DocumentsContract.QUERY_ARG_FILE_SIZE_OVER,
+ DocumentsContract.QUERY_ARG_LAST_MODIFIED_AFTER,
+ DocumentsContract.QUERY_ARG_MIME_TYPES);
+
+ private static String joinNewline(String... args) {
+ return TextUtils.join("\n", args);
+ }
+
private String[] mDefaultProjection;
@GuardedBy("mObservers")
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index c4aa1d7583a3..a691a2403f37 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -25,7 +25,13 @@ import android.view.View;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
/**
- * Log all the things.
+ * Writes sysui_multi_event records to the system event log.
+ *
+ * Prefer the methods write(LogMaker), or count() or histogram(). Replace legacy methods with
+ * their current equivalents when the opportunity arises.
+ *
+ * This class is a lightweight dependency barrier - it is cheap and easy to construct.
+ * Logging is also cheap, so it is not normally necessary to move logging off of the UI thread.
*
* @hide
*/
@@ -52,6 +58,7 @@ public class MetricsLogger {
public static final int VIEW_UNKNOWN = MetricsEvent.VIEW_UNKNOWN;
public static final int LOGTAG = EventLogTags.SYSUI_MULTI_ACTION;
+ /** Write an event log record, consisting of content.serialize(). */
@UnsupportedAppUsage
public void write(LogMaker content) {
if (content.getType() == MetricsEvent.TYPE_UNKNOWN) {
@@ -60,128 +67,145 @@ public class MetricsLogger {
saveLog(content);
}
+ /** Add an integer value to the monotonically increasing counter with the given name. */
+ public void count(String name, int value) {
+ saveLog(new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER)
+ .setCounterName(name)
+ .setCounterValue(value));
+ }
+
+ /** Increment the bucket with the integer label on the histogram with the given name. */
+ public void histogram(String name, int bucket) {
+ // see LogHistogram in system/core/libmetricslogger/metrics_logger.cpp
+ saveLog(new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
+ .setCounterName(name)
+ .setCounterBucket(bucket)
+ .setCounterValue(1));
+ }
+
+ /* Legacy logging methods follow. These are all simple shorthands and can be replaced
+ * with an equivalent write(). */
+
+ /** Logs an OPEN event on the category.
+ * Equivalent to write(new LogMaker(category).setType(MetricsEvent.TYPE_OPEN)) */
public void visible(int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
throw new IllegalArgumentException("Must define metric category");
}
- EventLogTags.writeSysuiViewVisibility(category, 100);
saveLog(new LogMaker(category).setType(MetricsEvent.TYPE_OPEN));
}
+ /** Logs a CLOSE event on the category.
+ * Equivalent to write(new LogMaker(category).setType(MetricsEvent.TYPE_CLOSE)) */
public void hidden(int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
throw new IllegalArgumentException("Must define metric category");
}
- EventLogTags.writeSysuiViewVisibility(category, 0);
saveLog(new LogMaker(category).setType(MetricsEvent.TYPE_CLOSE));
}
- public void visibility(int category, boolean visibile)
+ /** Logs an OPEN or CLOSE event on the category, depending on visible.
+ * Equivalent to write(new LogMaker(category)
+ * .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)) */
+ public void visibility(int category, boolean visible)
throws IllegalArgumentException {
- if (visibile) {
+ if (visible) {
visible(category);
} else {
hidden(category);
}
}
+ /** Logs an OPEN or CLOSE event on the category, depending on vis.
+ * Equivalent to write(new LogMaker(category)
+ .setType(vis == View.VISIBLE ?
+ MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)) */
public void visibility(int category, int vis)
throws IllegalArgumentException {
visibility(category, vis == View.VISIBLE);
}
+ /** Logs an ACTION event on the category.
+ * Equivalent to write(new LogMaker(category).setType(MetricsEvent.TYPE_ACTION)) */
public void action(int category) {
- EventLogTags.writeSysuiAction(category, "");
saveLog(new LogMaker(category).setType(MetricsEvent.TYPE_ACTION));
}
+ /** Logs an ACTION event on the category.
+ * Equivalent to write(new LogMaker(category).setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(value) */
public void action(int category, int value) {
- EventLogTags.writeSysuiAction(category, Integer.toString(value));
saveLog(new LogMaker(category).setType(MetricsEvent.TYPE_ACTION).setSubtype(value));
}
+ /** Logs an ACTION event on the category.
+ * Equivalent to write(new LogMaker(category).setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(value ? 1 : 0) */
public void action(int category, boolean value) {
- EventLogTags.writeSysuiAction(category, Boolean.toString(value));
saveLog(new LogMaker(category).setType(MetricsEvent.TYPE_ACTION).setSubtype(value ? 1 : 0));
}
+ /** Logs an ACTION event on the category.
+ * Equivalent to write(new LogMaker(category).setType(MetricsEvent.TYPE_ACTION)
+ .setPackageName(value ? 1 : 0) */
public void action(int category, String pkg) {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
throw new IllegalArgumentException("Must define metric category");
}
- EventLogTags.writeSysuiAction(category, pkg);
saveLog(new LogMaker(category).setType(MetricsEvent.TYPE_ACTION).setPackageName(pkg));
}
- /** Add an integer value to the monotonically increasing counter with the given name. */
- public void count(String name, int value) {
- EventLogTags.writeSysuiCount(name, value);
- saveLog(new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER)
- .setCounterName(name)
- .setCounterValue(value));
- }
-
- /** Increment the bucket with the integer label on the histogram with the given name. */
- public void histogram(String name, int bucket) {
- // see LogHistogram in system/core/libmetricslogger/metrics_logger.cpp
- EventLogTags.writeSysuiHistogram(name, bucket);
- saveLog(new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
- .setCounterName(name)
- .setCounterBucket(bucket)
- .setCounterValue(1));
- }
-
- /** @deprecated use {@link #visible(int)} */
+ /** @deprecated because untestable; use {@link #visible(int)} */
@Deprecated
public static void visible(Context context, int category) throws IllegalArgumentException {
getLogger().visible(category);
}
- /** @deprecated use {@link #hidden(int)} */
+ /** @deprecated because untestable; use {@link #hidden(int)} */
@Deprecated
public static void hidden(Context context, int category) throws IllegalArgumentException {
getLogger().hidden(category);
}
- /** @deprecated use {@link #visibility(int, boolean)} */
+ /** @deprecated because untestable; use {@link #visibility(int, boolean)} */
@Deprecated
public static void visibility(Context context, int category, boolean visibile)
throws IllegalArgumentException {
getLogger().visibility(category, visibile);
}
- /** @deprecated use {@link #visibility(int, int)} */
+ /** @deprecated because untestable; use {@link #visibility(int, int)} */
@Deprecated
public static void visibility(Context context, int category, int vis)
throws IllegalArgumentException {
visibility(context, category, vis == View.VISIBLE);
}
- /** @deprecated use {@link #action(int)} */
+ /** @deprecated because untestable; use {@link #action(int)} */
@Deprecated
public static void action(Context context, int category) {
getLogger().action(category);
}
- /** @deprecated use {@link #action(int, int)} */
+ /** @deprecated because untestable; use {@link #action(int, int)} */
@Deprecated
public static void action(Context context, int category, int value) {
getLogger().action(category, value);
}
- /** @deprecated use {@link #action(int, boolean)} */
+ /** @deprecated because untestable; use {@link #action(int, boolean)} */
@Deprecated
public static void action(Context context, int category, boolean value) {
getLogger().action(category, value);
}
- /** @deprecated use {@link #write(LogMaker)} */
+ /** @deprecated because untestable; use {@link #write(LogMaker)} */
@Deprecated
public static void action(LogMaker content) {
getLogger().write(content);
}
- /** @deprecated use {@link #action(int, String)} */
+ /** @deprecated because untestable; use {@link #action(int, String)} */
@Deprecated
public static void action(Context context, int category, String pkg) {
getLogger().action(category, pkg);
@@ -189,7 +213,7 @@ public class MetricsLogger {
/**
* Add an integer value to the monotonically increasing counter with the given name.
- * @deprecated use {@link #count(String, int)}
+ * @deprecated because untestable; use {@link #count(String, int)}
*/
@Deprecated
public static void count(Context context, String name, int value) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 17cc6afcd8f0..534361e13c7d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -87,6 +87,10 @@ import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.location.gnssmetrics.GnssMetrics;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FastXmlSerializer;
@@ -187,18 +191,19 @@ public class BatteryStatsImpl extends BatteryStats {
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
@VisibleForTesting
- protected KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
+ protected KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader =
+ new KernelCpuUidUserSysTimeReader(true);
@VisibleForTesting
protected KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@VisibleForTesting
- protected KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader =
- new KernelUidCpuFreqTimeReader();
+ protected KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader =
+ new KernelCpuUidFreqTimeReader(true);
@VisibleForTesting
- protected KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader =
- new KernelUidCpuActiveTimeReader();
+ protected KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader =
+ new KernelCpuUidActiveTimeReader(true);
@VisibleForTesting
- protected KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader =
- new KernelUidCpuClusterTimeReader();
+ protected KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader =
+ new KernelCpuUidClusterTimeReader(true);
@VisibleForTesting
protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
@@ -248,9 +253,9 @@ public class BatteryStatsImpl extends BatteryStats {
/** Last time that RPM stats were updated by updateRpmStatsLocked. */
private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS;
/**
- * Use a queue to delay removing UIDs from {@link KernelUidCpuTimeReader},
- * {@link KernelUidCpuActiveTimeReader}, {@link KernelUidCpuClusterTimeReader},
- * {@link KernelUidCpuFreqTimeReader} and from the Kernel.
+ * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader},
+ * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader},
+ * {@link KernelCpuUidFreqTimeReader} and from the Kernel.
*
* Isolated and invalid UID info must be removed to conserve memory. However, STATSD and
* Batterystats both need to access UID cpu time. To resolve this race condition, only
@@ -281,22 +286,22 @@ public class BatteryStatsImpl extends BatteryStats {
void remove() {
if (startUid == endUid) {
- mKernelUidCpuTimeReader.removeUid(startUid);
- mKernelUidCpuFreqTimeReader.removeUid(startUid);
+ mCpuUidUserSysTimeReader.removeUid(startUid);
+ mCpuUidFreqTimeReader.removeUid(startUid);
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
- mKernelUidCpuActiveTimeReader.removeUid(startUid);
- mKernelUidCpuClusterTimeReader.removeUid(startUid);
+ mCpuUidActiveTimeReader.removeUid(startUid);
+ mCpuUidClusterTimeReader.removeUid(startUid);
}
if (mKernelSingleUidTimeReader != null) {
mKernelSingleUidTimeReader.removeUid(startUid);
}
mNumUidsRemoved++;
} else if (startUid < endUid) {
- mKernelUidCpuFreqTimeReader.removeUidsInRange(startUid, endUid);
- mKernelUidCpuTimeReader.removeUidsInRange(startUid, endUid);
+ mCpuUidFreqTimeReader.removeUidsInRange(startUid, endUid);
+ mCpuUidUserSysTimeReader.removeUidsInRange(startUid, endUid);
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
- mKernelUidCpuActiveTimeReader.removeUidsInRange(startUid, endUid);
- mKernelUidCpuClusterTimeReader.removeUidsInRange(startUid, endUid);
+ mCpuUidActiveTimeReader.removeUidsInRange(startUid, endUid);
+ mCpuUidClusterTimeReader.removeUidsInRange(startUid, endUid);
}
if (mKernelSingleUidTimeReader != null) {
mKernelSingleUidTimeReader.removeUidsInRange(startUid, endUid);
@@ -496,7 +501,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
final SparseArray<long[]> allUidCpuFreqTimesMs =
- mKernelUidCpuFreqTimeReader.getAllUidCpuFreqTimeMs();
+ mCpuUidFreqTimeReader.getAllUidCpuFreqTimeMs();
// If the KernelSingleUidTimeReader has stale cpu times, then we shouldn't try to
// compute deltas since it might result in mis-attributing cpu times to wrong states.
if (mIsPerProcessStateCpuDataStale) {
@@ -553,16 +558,16 @@ public class BatteryStatsImpl extends BatteryStats {
return false;
}
if (mCpuFreqs == null) {
- mCpuFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mPowerProfile);
+ mCpuFreqs = mCpuUidFreqTimeReader.readFreqs(mPowerProfile);
}
if (mCpuFreqs != null) {
mKernelSingleUidTimeReader = new KernelSingleUidTimeReader(mCpuFreqs.length);
} else {
- mPerProcStateCpuTimesAvailable = mKernelUidCpuFreqTimeReader.allUidTimesAvailable();
+ mPerProcStateCpuTimesAvailable = mCpuUidFreqTimeReader.allUidTimesAvailable();
return false;
}
}
- mPerProcStateCpuTimesAvailable = mKernelUidCpuFreqTimeReader.allUidTimesAvailable()
+ mPerProcStateCpuTimesAvailable = mCpuUidFreqTimeReader.allUidTimesAvailable()
&& mKernelSingleUidTimeReader.singleUidCpuTimesAvailable();
return true;
}
@@ -11926,7 +11931,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
if (mCpuFreqs == null) {
- mCpuFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mPowerProfile);
+ mCpuFreqs = mCpuUidFreqTimeReader.readFreqs(mPowerProfile);
}
// Calculate the wakelocks we have to distribute amongst. The system is excluded as it is
@@ -11952,12 +11957,12 @@ public class BatteryStatsImpl extends BatteryStats {
// When the battery is not on, we don't attribute the cpu times to any timers but we still
// need to take the snapshots.
if (!onBattery) {
- mKernelUidCpuTimeReader.readDelta(null);
- mKernelUidCpuFreqTimeReader.readDelta(null);
+ mCpuUidUserSysTimeReader.readDelta(null);
+ mCpuUidFreqTimeReader.readDelta(null);
mNumAllUidCpuTimeReads += 2;
if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
- mKernelUidCpuActiveTimeReader.readDelta(null);
- mKernelUidCpuClusterTimeReader.readDelta(null);
+ mCpuUidActiveTimeReader.readDelta(null);
+ mCpuUidClusterTimeReader.readDelta(null);
mNumAllUidCpuTimeReads += 2;
}
for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
@@ -11967,7 +11972,7 @@ public class BatteryStatsImpl extends BatteryStats {
}
mUserInfoProvider.refreshUserIds();
- final SparseLongArray updatedUids = mKernelUidCpuFreqTimeReader.perClusterTimesAvailable()
+ final SparseLongArray updatedUids = mCpuUidFreqTimeReader.perClusterTimesAvailable()
? null : new SparseLongArray();
readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids, onBattery);
// updatedUids=null means /proc/uid_time_in_state provides snapshots of per-cluster cpu
@@ -12084,18 +12089,20 @@ public class BatteryStatsImpl extends BatteryStats {
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
final long startTimeMs = mClocks.uptimeMillis();
- mKernelUidCpuTimeReader.readDelta((uid, userTimeUs, systemTimeUs) -> {
+ mCpuUidUserSysTimeReader.readDelta((uid, timesUs) -> {
+ long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
+
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
// This could happen if the isolated uid mapping was removed before that process
// was actually killed.
- mKernelUidCpuTimeReader.removeUid(uid);
+ mCpuUidUserSysTimeReader.removeUid(uid);
Slog.d(TAG, "Got readings for an isolated uid with no mapping: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
- mKernelUidCpuTimeReader.removeUid(uid);
+ mCpuUidUserSysTimeReader.removeUid(uid);
return;
}
final Uid u = getUidStatsLocked(uid);
@@ -12189,21 +12196,21 @@ public class BatteryStatsImpl extends BatteryStats {
public void readKernelUidCpuFreqTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers,
boolean onBattery, boolean onBatteryScreenOff) {
final boolean perClusterTimesAvailable =
- mKernelUidCpuFreqTimeReader.perClusterTimesAvailable();
+ mCpuUidFreqTimeReader.perClusterTimesAvailable();
final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
final int numClusters = mPowerProfile.getNumCpuClusters();
mWakeLockAllocationsUs = null;
final long startTimeMs = mClocks.uptimeMillis();
- mKernelUidCpuFreqTimeReader.readDelta((uid, cpuFreqTimeMs) -> {
+ mCpuUidFreqTimeReader.readDelta((uid, cpuFreqTimeMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
- mKernelUidCpuFreqTimeReader.removeUid(uid);
+ mCpuUidFreqTimeReader.removeUid(uid);
Slog.d(TAG, "Got freq readings for an isolated uid with no mapping: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
- mKernelUidCpuFreqTimeReader.removeUid(uid);
+ mCpuUidFreqTimeReader.removeUid(uid);
return;
}
final Uid u = getUidStatsLocked(uid);
@@ -12307,16 +12314,16 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting
public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
final long startTimeMs = mClocks.uptimeMillis();
- mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> {
+ mCpuUidActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
- mKernelUidCpuActiveTimeReader.removeUid(uid);
+ mCpuUidActiveTimeReader.removeUid(uid);
Slog.w(TAG, "Got active times for an isolated uid with no mapping: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
- mKernelUidCpuActiveTimeReader.removeUid(uid);
+ mCpuUidActiveTimeReader.removeUid(uid);
return;
}
final Uid u = getUidStatsLocked(uid);
@@ -12336,16 +12343,16 @@ public class BatteryStatsImpl extends BatteryStats {
@VisibleForTesting
public void readKernelUidCpuClusterTimesLocked(boolean onBattery) {
final long startTimeMs = mClocks.uptimeMillis();
- mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> {
+ mCpuUidClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> {
uid = mapUid(uid);
if (Process.isIsolated(uid)) {
- mKernelUidCpuClusterTimeReader.removeUid(uid);
+ mCpuUidClusterTimeReader.removeUid(uid);
Slog.w(TAG, "Got cluster times for an isolated uid with no mapping: " + uid);
return;
}
if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
- mKernelUidCpuClusterTimeReader.removeUid(uid);
+ mCpuUidClusterTimeReader.removeUid(uid);
return;
}
final Uid u = getUidStatsLocked(uid);
@@ -13344,7 +13351,7 @@ public class BatteryStatsImpl extends BatteryStats {
private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = false;
private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
- private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
+ private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 1_000;
private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
private static final long DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS = 300_000;
@@ -13357,7 +13364,9 @@ public class BatteryStatsImpl extends BatteryStats {
public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
public long PROC_STATE_CPU_TIMES_READ_DELAY_MS = DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS;
- public long KERNEL_UID_READERS_THROTTLE_TIME = DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME;
+ /* Do not set default value for KERNEL_UID_READERS_THROTTLE_TIME. Need to trigger an
+ * update when startObserving. */
+ public long KERNEL_UID_READERS_THROTTLE_TIME;
public long UID_REMOVE_DELAY_MS = DEFAULT_UID_REMOVE_DELAY_MS;
public long EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS
= DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
@@ -13464,11 +13473,11 @@ public class BatteryStatsImpl extends BatteryStats {
private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) {
KERNEL_UID_READERS_THROTTLE_TIME = newTimeMs;
if (oldTimeMs != newTimeMs) {
- mKernelUidCpuTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
- mKernelUidCpuFreqTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
- mKernelUidCpuActiveTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
- mKernelUidCpuClusterTimeReader
- .setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
+ mCpuUidUserSysTimeReader.setThrottle(KERNEL_UID_READERS_THROTTLE_TIME);
+ mCpuUidFreqTimeReader.setThrottle(KERNEL_UID_READERS_THROTTLE_TIME);
+ mCpuUidActiveTimeReader.setThrottle(KERNEL_UID_READERS_THROTTLE_TIME);
+ mCpuUidClusterTimeReader
+ .setThrottle(KERNEL_UID_READERS_THROTTLE_TIME);
}
}
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 051a96c39f28..64543522893e 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -53,6 +53,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
public static final boolean DETAILED_TRACKING_DEFAULT = true;
public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 100;
public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000;
+ private static final String DEBUG_ENTRY_PREFIX = "__DEBUG_";
private static class OverflowBinder extends Binder {}
@@ -347,7 +348,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
callStat.callingUid = uid;
callStat.recordedCallCount = 1;
callStat.callCount = 1;
- callStat.methodName = "__DEBUG_" + variableName;
+ callStat.methodName = DEBUG_ENTRY_PREFIX + variableName;
callStat.latencyMicros = value;
return callStat;
}
@@ -398,6 +399,10 @@ public class BinderCallsStats implements BinderInternal.Observer {
final List<ExportedCallStat> exportedCallStats = getExportedCallStats();
exportedCallStats.sort(BinderCallsStats::compareByCpuDesc);
for (ExportedCallStat e : exportedCallStats) {
+ if (e.methodName.startsWith(DEBUG_ENTRY_PREFIX)) {
+ // Do not dump debug entries.
+ continue;
+ }
sb.setLength(0);
sb.append(" ")
.append(packageMap.mapUid(e.callingUid))
diff --git a/core/java/com/android/internal/os/KernelCpuProcReader.java b/core/java/com/android/internal/os/KernelCpuProcReader.java
deleted file mode 100644
index c233ea8e78b7..000000000000
--- a/core/java/com/android/internal/os/KernelCpuProcReader.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.os;
-
-import android.os.StrictMode;
-import android.os.SystemClock;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-
-/**
- * Reads cpu time proc files with throttling (adjustable interval).
- *
- * KernelCpuProcReader is implemented as singletons for built-in kernel proc files. Get___Instance()
- * method will return corresponding reader instance. In order to prevent frequent GC,
- * KernelCpuProcReader reuses a {@link ByteBuffer} to store data read from proc files.
- *
- * A KernelCpuProcReader instance keeps an error counter. When the number of read errors within that
- * instance accumulates to 5, this instance will reject all further read requests.
- *
- * Each KernelCpuProcReader instance also has a throttler. Throttle interval can be adjusted via
- * {@link #setThrottleInterval(long)} method. Default throttle interval is 3000ms. If current
- * timestamp based on {@link SystemClock#elapsedRealtime()} is less than throttle interval from
- * the last read timestamp, {@link #readBytes()} will return previous result.
- *
- * A KernelCpuProcReader instance is thread-unsafe. Caller needs to hold a lock on this object while
- * accessing its instance methods or digesting the return values.
- */
-public class KernelCpuProcReader {
- private static final String TAG = "KernelCpuProcReader";
- private static final int ERROR_THRESHOLD = 5;
- // Throttle interval in milliseconds
- private static final long DEFAULT_THROTTLE_INTERVAL = 3000L;
- private static final int MAX_BUFFER_SIZE = 1024 * 1024;
- private static final String PROC_UID_FREQ_TIME = "/proc/uid_cpupower/time_in_state";
- private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_cpupower/concurrent_active_time";
- private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_cpupower/concurrent_policy_time";
-
- private static final KernelCpuProcReader mFreqTimeReader = new KernelCpuProcReader(
- PROC_UID_FREQ_TIME);
- private static final KernelCpuProcReader mActiveTimeReader = new KernelCpuProcReader(
- PROC_UID_ACTIVE_TIME);
- private static final KernelCpuProcReader mClusterTimeReader = new KernelCpuProcReader(
- PROC_UID_CLUSTER_TIME);
-
- public static KernelCpuProcReader getFreqTimeReaderInstance() {
- return mFreqTimeReader;
- }
-
- public static KernelCpuProcReader getActiveTimeReaderInstance() {
- return mActiveTimeReader;
- }
-
- public static KernelCpuProcReader getClusterTimeReaderInstance() {
- return mClusterTimeReader;
- }
-
- private int mErrors;
- private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
- private long mLastReadTime = Long.MIN_VALUE;
- private final Path mProc;
- private byte[] mBuffer = new byte[8 * 1024];
- private int mContentSize;
-
- @VisibleForTesting
- public KernelCpuProcReader(String procFile) {
- mProc = Paths.get(procFile);
- }
-
- /**
- * Reads all bytes from the corresponding proc file.
- *
- * If elapsed time since last call to this method is less than the throttle interval, it will
- * return previous result. When IOException accumulates to 5, it will always return null. This
- * method is thread-unsafe, so is the return value. Caller needs to hold a lock on this
- * object while calling this method and digesting its return value.
- *
- * @return a {@link ByteBuffer} containing all bytes from the proc file.
- */
- public ByteBuffer readBytes() {
- if (mErrors >= ERROR_THRESHOLD) {
- return null;
- }
- if (SystemClock.elapsedRealtime() < mLastReadTime + mThrottleInterval) {
- if (mContentSize > 0) {
- return ByteBuffer.wrap(mBuffer, 0, mContentSize).asReadOnlyBuffer()
- .order(ByteOrder.nativeOrder());
- }
- return null;
- }
- mLastReadTime = SystemClock.elapsedRealtime();
- mContentSize = 0;
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (InputStream in = Files.newInputStream(mProc)) {
- int numBytes = 0;
- int curr;
- while ((curr = in.read(mBuffer, numBytes, mBuffer.length - numBytes)) >= 0) {
- numBytes += curr;
- if (numBytes == mBuffer.length) {
- // Hit the limit. Resize mBuffer.
- if (mBuffer.length == MAX_BUFFER_SIZE) {
- mErrors++;
- Slog.e(TAG, "Proc file is too large: " + mProc);
- return null;
- }
- mBuffer = Arrays.copyOf(mBuffer,
- Math.min(mBuffer.length << 1, MAX_BUFFER_SIZE));
- }
- }
- mContentSize = numBytes;
- return ByteBuffer.wrap(mBuffer, 0, mContentSize).asReadOnlyBuffer()
- .order(ByteOrder.nativeOrder());
- } catch (NoSuchFileException | FileNotFoundException e) {
- // Happens when the kernel does not provide this file. Not a big issue. Just log it.
- mErrors++;
- Slog.w(TAG, "File not exist: " + mProc);
- } catch (IOException e) {
- mErrors++;
- Slog.e(TAG, "Error reading: " + mProc, e);
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
- }
- return null;
- }
-
- /**
- * Sets the throttle interval. Set to 0 will disable throttling. Thread-unsafe, holding a lock
- * on this object is recommended.
- *
- * @param throttleInterval throttle interval in milliseconds
- */
- public void setThrottleInterval(long throttleInterval) {
- if (throttleInterval >= 0) {
- mThrottleInterval = throttleInterval;
- }
- }
-}
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index 7021b5781347..e6d044f4722b 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -177,6 +177,9 @@ public abstract class KernelCpuUidTimeReader<T> {
* The file contains a monotonically increasing count of time for a single boot. This class
* maintains the previous results of a call to {@link #readDelta} in order to provide a proper
* delta.
+ *
+ * The second parameter of the callback is a long[] with 2 elements, [user time in us, system
+ * time in us].
*/
public static class KernelCpuUidUserSysTimeReader extends KernelCpuUidTimeReader<long[]> {
private static final String REMOVE_UID_PROC_FILE = "/proc/uid_cputime/remove_uid_range";
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index ad628524f443..3c43a11a8e9f 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -16,7 +16,6 @@
package com.android.internal.os;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
-import static com.android.internal.os.KernelUidCpuFreqTimeReader.UID_TIMES_PROC_FILE;
import android.annotation.NonNull;
import android.util.Slog;
@@ -34,11 +33,12 @@ import java.util.Arrays;
@VisibleForTesting(visibility = PACKAGE)
public class KernelSingleUidTimeReader {
- private final String TAG = KernelUidCpuFreqTimeReader.class.getName();
- private final boolean DBG = false;
+ private static final String TAG = KernelSingleUidTimeReader.class.getName();
+ private static final boolean DBG = false;
- private final String PROC_FILE_DIR = "/proc/uid/";
- private final String PROC_FILE_NAME = "/time_in_state";
+ private static final String PROC_FILE_DIR = "/proc/uid/";
+ private static final String PROC_FILE_NAME = "/time_in_state";
+ private static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
@VisibleForTesting
public static final int TOTAL_READ_ERROR_COUNT = 5;
diff --git a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
deleted file mode 100644
index bd8a67a8bd0e..000000000000
--- a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
+++ /dev/null
@@ -1,179 +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.
- */
-
-package com.android.internal.os;
-
-import android.annotation.Nullable;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
-import java.util.function.Consumer;
-
-/**
- * Reads binary proc file /proc/uid_cpupower/concurrent_active_time and reports CPU active time to
- * BatteryStats to compute {@link PowerProfile#POWER_CPU_ACTIVE}.
- *
- * concurrent_active_time is an array of u32's in the following format:
- * [n, uid0, time0a, time0b, ..., time0n,
- * uid1, time1a, time1b, ..., time1n,
- * uid2, time2a, time2b, ..., time2n, etc.]
- * where n is the total number of cpus (num_possible_cpus)
- * ...
- * timeXn means the CPU time that a UID X spent running concurrently with n other processes.
- * The file contains a monotonically increasing count of time for a single boot. This class
- * maintains the previous results of a call to {@link #readDelta} in order to provide a
- * proper delta.
- *
- * This class uses a throttler to reject any {@link #readDelta} call within
- * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
- * which has a shorter throttle interval and returns cached result from last read when the request
- * is throttled.
- *
- * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each
- * caller has its own view of delta.
- */
-public class KernelUidCpuActiveTimeReader extends
- KernelUidCpuTimeReaderBase<KernelUidCpuActiveTimeReader.Callback> {
- private static final String TAG = KernelUidCpuActiveTimeReader.class.getSimpleName();
-
- private final KernelCpuProcReader mProcReader;
- private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>();
- private int mCores;
-
- public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
- /**
- * Notifies when new data is available.
- *
- * @param uid uid int
- * @param cpuActiveTimeMs cpu active time spent by this uid in milliseconds
- */
- void onUidCpuActiveTime(int uid, long cpuActiveTimeMs);
- }
-
- public KernelUidCpuActiveTimeReader() {
- mProcReader = KernelCpuProcReader.getActiveTimeReaderInstance();
- }
-
- @VisibleForTesting
- public KernelUidCpuActiveTimeReader(KernelCpuProcReader procReader) {
- mProcReader = procReader;
- }
-
- @Override
- protected void readDeltaImpl(@Nullable Callback callback) {
- readImpl((buf) -> {
- int uid = buf.get();
- double activeTime = sumActiveTime(buf);
- if (activeTime > 0) {
- double delta = activeTime - mLastUidCpuActiveTimeMs.get(uid, 0.0);
- if (delta > 0) {
- mLastUidCpuActiveTimeMs.put(uid, activeTime);
- if (callback != null) {
- callback.onUidCpuActiveTime(uid, (long) delta);
- }
- } else if (delta < 0) {
- Slog.e(TAG, "Negative delta from active time proc: " + delta);
- }
- }
- });
- }
-
- public void readAbsolute(Callback callback) {
- readImpl((buf) -> {
- int uid = buf.get();
- double activeTime = sumActiveTime(buf);
- if (activeTime > 0) {
- callback.onUidCpuActiveTime(uid, (long) activeTime);
- }
- });
- }
-
- private double sumActiveTime(IntBuffer buffer) {
- double sum = 0;
- boolean corrupted = false;
- for (int j = 1; j <= mCores; j++) {
- int time = buffer.get();
- if (time < 0) {
- // Even if error happens, we still need to continue reading.
- // Buffer cannot be skipped.
- Slog.e(TAG, "Negative time from active time proc: " + time);
- corrupted = true;
- } else {
- sum += (double) time * 10 / j; // Unit is 10ms.
- }
- }
- return corrupted ? -1 : sum;
- }
-
- /**
- * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
- * seen results while processing the buffer, while readAbsolute returns the absolute value read
- * from the buffer without storing. So readImpl contains the common logic of the two, leaving
- * the difference to a processUid function.
- *
- * @param processUid the callback function to process the uid entry in the buffer.
- */
- private void readImpl(Consumer<IntBuffer> processUid) {
- synchronized (mProcReader) {
- final ByteBuffer bytes = mProcReader.readBytes();
- if (bytes == null || bytes.remaining() <= 4) {
- // Error already logged in mProcReader.
- return;
- }
- if ((bytes.remaining() & 3) != 0) {
- Slog.wtf(TAG,
- "Cannot parse active time proc bytes to int: " + bytes.remaining());
- return;
- }
- final IntBuffer buf = bytes.asIntBuffer();
- final int cores = buf.get();
- if (mCores != 0 && cores != mCores) {
- Slog.wtf(TAG, "Cpu active time wrong # cores: " + cores);
- return;
- }
- mCores = cores;
- if (cores <= 0 || buf.remaining() % (cores + 1) != 0) {
- Slog.wtf(TAG,
- "Cpu active time format error: " + buf.remaining() + " / " + (cores
- + 1));
- return;
- }
- int numUids = buf.remaining() / (cores + 1);
- for (int i = 0; i < numUids; i++) {
- processUid.accept(buf);
- }
- if (DEBUG) {
- Slog.d(TAG, "Read uids: " + numUids);
- }
- }
- }
-
- public void removeUid(int uid) {
- mLastUidCpuActiveTimeMs.delete(uid);
- }
-
- public void removeUidsInRange(int startUid, int endUid) {
- mLastUidCpuActiveTimeMs.put(startUid, null);
- mLastUidCpuActiveTimeMs.put(endUid, null);
- final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid);
- final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid);
- mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
- }
-}
diff --git a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
deleted file mode 100644
index 3cbfaead4779..000000000000
--- a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
+++ /dev/null
@@ -1,239 +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.
- */
-
-package com.android.internal.os;
-
-import android.annotation.Nullable;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
-import java.util.function.Consumer;
-
-/**
- * Reads binary proc file /proc/uid_cpupower/concurrent_policy_time and reports CPU cluster times
- * to BatteryStats to compute cluster power. See
- * {@link PowerProfile#getAveragePowerForCpuCluster(int)}.
- *
- * concurrent_policy_time is an array of u32's in the following format:
- * [n, x0, ..., xn, uid0, time0a, time0b, ..., time0n,
- * uid1, time1a, time1b, ..., time1n,
- * uid2, time2a, time2b, ..., time2n, etc.]
- * where n is the number of policies
- * xi is the number cpus on a particular policy
- * Each uidX is followed by x0 time entries corresponding to the time UID X spent on cluster0
- * running concurrently with 0, 1, 2, ..., x0 - 1 other processes, then followed by x1, ..., xn
- * time entries.
- *
- * The file contains a monotonically increasing count of time for a single boot. This class
- * maintains the previous results of a call to {@link #readDelta} in order to provide a
- * proper delta.
- *
- * This class uses a throttler to reject any {@link #readDelta} call within
- * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
- * which has a shorter throttle interval and returns cached result from last read when the request
- * is throttled.
- *
- * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each
- * caller has its own view of delta.
- */
-public class KernelUidCpuClusterTimeReader extends
- KernelUidCpuTimeReaderBase<KernelUidCpuClusterTimeReader.Callback> {
- private static final String TAG = KernelUidCpuClusterTimeReader.class.getSimpleName();
-
- private final KernelCpuProcReader mProcReader;
- private SparseArray<double[]> mLastUidPolicyTimeMs = new SparseArray<>();
-
- private int mNumClusters = -1;
- private int mNumCores;
- private int[] mNumCoresOnCluster;
-
- private double[] mCurTime; // Reuse to avoid GC.
- private long[] mDeltaTime; // Reuse to avoid GC.
- private long[] mCurTimeRounded; // Reuse to avoid GC.
-
- public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
- /**
- * Notifies when new data is available.
- *
- * @param uid uid int
- * @param cpuClusterTimeMs an array of times spent by this uid on corresponding clusters.
- * The array index is the cluster index.
- */
- void onUidCpuPolicyTime(int uid, long[] cpuClusterTimeMs);
- }
-
- public KernelUidCpuClusterTimeReader() {
- mProcReader = KernelCpuProcReader.getClusterTimeReaderInstance();
- }
-
- @VisibleForTesting
- public KernelUidCpuClusterTimeReader(KernelCpuProcReader procReader) {
- mProcReader = procReader;
- }
-
- @Override
- protected void readDeltaImpl(@Nullable Callback cb) {
- readImpl((buf) -> {
- int uid = buf.get();
- double[] lastTimes = mLastUidPolicyTimeMs.get(uid);
- if (lastTimes == null) {
- lastTimes = new double[mNumClusters];
- mLastUidPolicyTimeMs.put(uid, lastTimes);
- }
- if (!sumClusterTime(buf, mCurTime)) {
- return;
- }
- boolean valid = true;
- boolean notify = false;
- for (int i = 0; i < mNumClusters; i++) {
- mDeltaTime[i] = (long) (mCurTime[i] - lastTimes[i]);
- if (mDeltaTime[i] < 0) {
- Slog.e(TAG, "Negative delta from cluster time proc: " + mDeltaTime[i]);
- valid = false;
- }
- notify |= mDeltaTime[i] > 0;
- }
- if (notify && valid) {
- System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
- if (cb != null) {
- cb.onUidCpuPolicyTime(uid, mDeltaTime);
- }
- }
- });
- }
-
- public void readAbsolute(Callback callback) {
- readImpl((buf) -> {
- int uid = buf.get();
- if (sumClusterTime(buf, mCurTime)) {
- for (int i = 0; i < mNumClusters; i++) {
- mCurTimeRounded[i] = (long) mCurTime[i];
- }
- callback.onUidCpuPolicyTime(uid, mCurTimeRounded);
- }
- });
- }
-
- private boolean sumClusterTime(IntBuffer buffer, double[] clusterTime) {
- boolean valid = true;
- for (int i = 0; i < mNumClusters; i++) {
- clusterTime[i] = 0;
- for (int j = 1; j <= mNumCoresOnCluster[i]; j++) {
- int time = buffer.get();
- if (time < 0) {
- Slog.e(TAG, "Negative time from cluster time proc: " + time);
- valid = false;
- }
- clusterTime[i] += (double) time * 10 / j; // Unit is 10ms.
- }
- }
- return valid;
- }
-
- /**
- * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
- * seen results while processing the buffer, while readAbsolute returns the absolute value read
- * from the buffer without storing. So readImpl contains the common logic of the two, leaving
- * the difference to a processUid function.
- *
- * @param processUid the callback function to process the uid entry in the buffer.
- */
- private void readImpl(Consumer<IntBuffer> processUid) {
- synchronized (mProcReader) {
- ByteBuffer bytes = mProcReader.readBytes();
- if (bytes == null || bytes.remaining() <= 4) {
- // Error already logged in mProcReader.
- return;
- }
- if ((bytes.remaining() & 3) != 0) {
- Slog.wtf(TAG,
- "Cannot parse cluster time proc bytes to int: " + bytes.remaining());
- return;
- }
- IntBuffer buf = bytes.asIntBuffer();
- final int numClusters = buf.get();
- if (numClusters <= 0) {
- Slog.wtf(TAG, "Cluster time format error: " + numClusters);
- return;
- }
- if (mNumClusters == -1) {
- mNumClusters = numClusters;
- }
- if (buf.remaining() < numClusters) {
- Slog.wtf(TAG, "Too few data left in the buffer: " + buf.remaining());
- return;
- }
- if (mNumCores <= 0) {
- if (!readCoreInfo(buf, numClusters)) {
- return;
- }
- } else {
- buf.position(buf.position() + numClusters);
- }
-
- if (buf.remaining() % (mNumCores + 1) != 0) {
- Slog.wtf(TAG,
- "Cluster time format error: " + buf.remaining() + " / " + (mNumCores
- + 1));
- return;
- }
- int numUids = buf.remaining() / (mNumCores + 1);
-
- for (int i = 0; i < numUids; i++) {
- processUid.accept(buf);
- }
- if (DEBUG) {
- Slog.d(TAG, "Read uids: " + numUids);
- }
- }
- }
-
- // Returns if it has read valid info.
- private boolean readCoreInfo(IntBuffer buf, int numClusters) {
- int numCores = 0;
- int[] numCoresOnCluster = new int[numClusters];
- for (int i = 0; i < numClusters; i++) {
- numCoresOnCluster[i] = buf.get();
- numCores += numCoresOnCluster[i];
- }
- if (numCores <= 0) {
- Slog.e(TAG, "Invalid # cores from cluster time proc file: " + numCores);
- return false;
- }
- mNumCores = numCores;
- mNumCoresOnCluster = numCoresOnCluster;
- mCurTime = new double[numClusters];
- mDeltaTime = new long[numClusters];
- mCurTimeRounded = new long[numClusters];
- return true;
- }
-
- public void removeUid(int uid) {
- mLastUidPolicyTimeMs.delete(uid);
- }
-
- public void removeUidsInRange(int startUid, int endUid) {
- mLastUidPolicyTimeMs.put(startUid, null);
- mLastUidPolicyTimeMs.put(endUid, null);
- final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid);
- final int lastIndex = mLastUidPolicyTimeMs.indexOfKey(endUid);
- mLastUidPolicyTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
- }
-}
diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
deleted file mode 100644
index 5b46d0f29c20..000000000000
--- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
+++ /dev/null
@@ -1,296 +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.
- */
-
-package com.android.internal.os;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.StrictMode;
-import android.util.IntArray;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
-import java.util.function.Consumer;
-
-/**
- * Reads /proc/uid_time_in_state which has the format:
- *
- * uid: [freq1] [freq2] [freq3] ...
- * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
- * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
- * ...
- *
- * Binary variation reads /proc/uid_cpupower/time_in_state in the following format:
- * [n, uid0, time0a, time0b, ..., time0n,
- * uid1, time1a, time1b, ..., time1n,
- * uid2, time2a, time2b, ..., time2n, etc.]
- * where n is the total number of frequencies.
- *
- * This provides the times a UID's processes spent executing at each different cpu frequency.
- * The file contains a monotonically increasing count of time for a single boot. This class
- * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
- * delta.
- *
- * This class uses a throttler to reject any {@link #readDelta} call within
- * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
- * which has a shorter throttle interval and returns cached result from last read when the request
- * is throttled.
- *
- * This class is NOT thread-safe and NOT designed to be accessed by more than one caller since each
- * caller has its own view of delta.
- */
-public class KernelUidCpuFreqTimeReader extends
- KernelUidCpuTimeReaderBase<KernelUidCpuFreqTimeReader.Callback> {
- private static final String TAG = KernelUidCpuFreqTimeReader.class.getSimpleName();
- static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
-
- public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
- void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs);
- }
-
- private long[] mCpuFreqs;
- private long[] mCurTimes; // Reuse to prevent GC.
- private long[] mDeltaTimes; // Reuse to prevent GC.
- private int mCpuFreqsCount;
- private final KernelCpuProcReader mProcReader;
-
- private SparseArray<long[]> mLastUidCpuFreqTimeMs = new SparseArray<>();
-
- // We check the existence of proc file a few times (just in case it is not ready yet when we
- // start reading) and if it is not available, we simply ignore further read requests.
- private static final int TOTAL_READ_ERROR_COUNT = 5;
- private int mReadErrorCounter;
- private boolean mPerClusterTimesAvailable;
- private boolean mAllUidTimesAvailable = true;
-
- public KernelUidCpuFreqTimeReader() {
- mProcReader = KernelCpuProcReader.getFreqTimeReaderInstance();
- }
-
- @VisibleForTesting
- public KernelUidCpuFreqTimeReader(KernelCpuProcReader procReader) {
- mProcReader = procReader;
- }
-
- public boolean perClusterTimesAvailable() {
- return mPerClusterTimesAvailable;
- }
-
- public boolean allUidTimesAvailable() {
- return mAllUidTimesAvailable;
- }
-
- public SparseArray<long[]> getAllUidCpuFreqTimeMs() {
- return mLastUidCpuFreqTimeMs;
- }
-
- public long[] readFreqs(@NonNull PowerProfile powerProfile) {
- checkNotNull(powerProfile);
- if (mCpuFreqs != null) {
- // No need to read cpu freqs more than once.
- return mCpuFreqs;
- }
- if (!mAllUidTimesAvailable) {
- return null;
- }
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
- return readFreqs(reader, powerProfile);
- } catch (IOException e) {
- if (++mReadErrorCounter >= TOTAL_READ_ERROR_COUNT) {
- mAllUidTimesAvailable = false;
- }
- Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
- return null;
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
- }
- }
-
- @VisibleForTesting
- public long[] readFreqs(BufferedReader reader, PowerProfile powerProfile)
- throws IOException {
- final String line = reader.readLine();
- if (line == null) {
- return null;
- }
- final String[] freqStr = line.split(" ");
- // First item would be "uid: " which needs to be ignored.
- mCpuFreqsCount = freqStr.length - 1;
- mCpuFreqs = new long[mCpuFreqsCount];
- mCurTimes = new long[mCpuFreqsCount];
- mDeltaTimes = new long[mCpuFreqsCount];
- for (int i = 0; i < mCpuFreqsCount; ++i) {
- mCpuFreqs[i] = Long.parseLong(freqStr[i + 1], 10);
- }
-
- // Check if the freqs in the proc file correspond to per-cluster freqs.
- final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
- final int numClusters = powerProfile.getNumCpuClusters();
- if (numClusterFreqs.size() == numClusters) {
- mPerClusterTimesAvailable = true;
- for (int i = 0; i < numClusters; ++i) {
- if (numClusterFreqs.get(i) != powerProfile.getNumSpeedStepsInCpuCluster(i)) {
- mPerClusterTimesAvailable = false;
- break;
- }
- }
- } else {
- mPerClusterTimesAvailable = false;
- }
- Slog.i(TAG, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
- return mCpuFreqs;
- }
-
- @Override
- @VisibleForTesting
- public void readDeltaImpl(@Nullable Callback callback) {
- if (mCpuFreqs == null) {
- return;
- }
- readImpl((buf) -> {
- int uid = buf.get();
- long[] lastTimes = mLastUidCpuFreqTimeMs.get(uid);
- if (lastTimes == null) {
- lastTimes = new long[mCpuFreqsCount];
- mLastUidCpuFreqTimeMs.put(uid, lastTimes);
- }
- if (!getFreqTimeForUid(buf, mCurTimes)) {
- return;
- }
- boolean notify = false;
- boolean valid = true;
- for (int i = 0; i < mCpuFreqsCount; i++) {
- mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
- if (mDeltaTimes[i] < 0) {
- Slog.e(TAG, "Negative delta from freq time proc: " + mDeltaTimes[i]);
- valid = false;
- }
- notify |= mDeltaTimes[i] > 0;
- }
- if (notify && valid) {
- System.arraycopy(mCurTimes, 0, lastTimes, 0, mCpuFreqsCount);
- if (callback != null) {
- callback.onUidCpuFreqTime(uid, mDeltaTimes);
- }
- }
- });
- }
-
- public void readAbsolute(Callback callback) {
- readImpl((buf) -> {
- int uid = buf.get();
- if (getFreqTimeForUid(buf, mCurTimes)) {
- callback.onUidCpuFreqTime(uid, mCurTimes);
- }
- });
- }
-
- private boolean getFreqTimeForUid(IntBuffer buffer, long[] freqTime) {
- boolean valid = true;
- for (int i = 0; i < mCpuFreqsCount; i++) {
- freqTime[i] = (long) buffer.get() * 10; // Unit is 10ms.
- if (freqTime[i] < 0) {
- Slog.e(TAG, "Negative time from freq time proc: " + freqTime[i]);
- valid = false;
- }
- }
- return valid;
- }
-
- /**
- * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
- * seen results while processing the buffer, while readAbsolute returns the absolute value read
- * from the buffer without storing. So readImpl contains the common logic of the two, leaving
- * the difference to a processUid function.
- *
- * @param processUid the callback function to process the uid entry in the buffer.
- */
- private void readImpl(Consumer<IntBuffer> processUid) {
- synchronized (mProcReader) {
- ByteBuffer bytes = mProcReader.readBytes();
- if (bytes == null || bytes.remaining() <= 4) {
- // Error already logged in mProcReader.
- return;
- }
- if ((bytes.remaining() & 3) != 0) {
- Slog.wtf(TAG, "Cannot parse freq time proc bytes to int: " + bytes.remaining());
- return;
- }
- IntBuffer buf = bytes.asIntBuffer();
- final int freqs = buf.get();
- if (freqs != mCpuFreqsCount) {
- Slog.wtf(TAG, "Cpu freqs expect " + mCpuFreqsCount + " , got " + freqs);
- return;
- }
- if (buf.remaining() % (freqs + 1) != 0) {
- Slog.wtf(TAG, "Freq time format error: " + buf.remaining() + " / " + (freqs + 1));
- return;
- }
- int numUids = buf.remaining() / (freqs + 1);
- for (int i = 0; i < numUids; i++) {
- processUid.accept(buf);
- }
- if (DEBUG) {
- Slog.d(TAG, "Read uids: #" + numUids);
- }
- }
- }
-
- public void removeUid(int uid) {
- mLastUidCpuFreqTimeMs.delete(uid);
- }
-
- public void removeUidsInRange(int startUid, int endUid) {
- mLastUidCpuFreqTimeMs.put(startUid, null);
- mLastUidCpuFreqTimeMs.put(endUid, null);
- final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid);
- final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid);
- mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
- }
-
- /**
- * Extracts no. of cpu clusters and no. of freqs in each of these clusters from the freqs
- * read from the proc file.
- *
- * We need to assume that freqs in each cluster are strictly increasing.
- * For e.g. if the freqs read from proc file are: 12, 34, 15, 45, 12, 15, 52. Then it means
- * there are 3 clusters: (12, 34), (15, 45), (12, 15, 52)
- *
- * @return an IntArray filled with no. of freqs in each cluster.
- */
- private IntArray extractClusterInfoFromProcFileFreqs() {
- final IntArray numClusterFreqs = new IntArray();
- int freqsFound = 0;
- for (int i = 0; i < mCpuFreqsCount; ++i) {
- freqsFound++;
- if (i + 1 == mCpuFreqsCount || mCpuFreqs[i + 1] <= mCpuFreqs[i]) {
- numClusterFreqs.add(freqsFound);
- freqsFound = 0;
- }
- }
- return numClusterFreqs;
- }
-}
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
deleted file mode 100644
index 97b7211e5e87..000000000000
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.internal.os;
-
-import android.annotation.Nullable;
-import android.os.StrictMode;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.Slog;
-import android.util.SparseLongArray;
-import android.util.TimeUtils;
-
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-
-/**
- * Reads /proc/uid_cputime/show_uid_stat which has the line format:
- *
- * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
- *
- * This provides the time a UID's processes spent executing in user-space and kernel-space.
- * The file contains a monotonically increasing count of time for a single boot. This class
- * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
- * delta.
- */
-public class KernelUidCpuTimeReader extends
- KernelUidCpuTimeReaderBase<KernelUidCpuTimeReader.Callback> {
- private static final String TAG = KernelUidCpuTimeReader.class.getSimpleName();
- private static final String sProcFile = "/proc/uid_cputime/show_uid_stat";
- private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range";
-
- /**
- * Callback interface for processing each line of the proc file.
- */
- public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
- /**
- * @param uid UID of the app
- * @param userTimeUs time spent executing in user space in microseconds
- * @param systemTimeUs time spent executing in kernel space in microseconds
- */
- void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs);
- }
-
- private SparseLongArray mLastUserTimeUs = new SparseLongArray();
- private SparseLongArray mLastSystemTimeUs = new SparseLongArray();
- private long mLastTimeReadUs = 0;
-
- /**
- * Reads the proc file, calling into the callback with a delta of time for each UID.
- *
- * @param callback The callback to invoke for each line of the proc file. If null,
- * the data is consumed and subsequent calls to readDelta will provide
- * a fresh delta.
- */
- @Override
- protected void readDeltaImpl(@Nullable Callback callback) {
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- long nowUs = SystemClock.elapsedRealtime() * 1000;
- try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
- TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
- String line;
- while ((line = reader.readLine()) != null) {
- splitter.setString(line);
- final String uidStr = splitter.next();
- final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
- final long userTimeUs = Long.parseLong(splitter.next(), 10);
- final long systemTimeUs = Long.parseLong(splitter.next(), 10);
-
- boolean notifyCallback = false;
- long userTimeDeltaUs = userTimeUs;
- long systemTimeDeltaUs = systemTimeUs;
- // Only report if there is a callback and if this is not the first read.
- if (callback != null && mLastTimeReadUs != 0) {
- int index = mLastUserTimeUs.indexOfKey(uid);
- if (index >= 0) {
- userTimeDeltaUs -= mLastUserTimeUs.valueAt(index);
- systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index);
-
- final long timeDiffUs = nowUs - mLastTimeReadUs;
- if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0) {
- StringBuilder sb = new StringBuilder("Malformed cpu data for UID=");
- sb.append(uid).append("!\n");
- sb.append("Time between reads: ");
- TimeUtils.formatDuration(timeDiffUs / 1000, sb);
- sb.append("\n");
- sb.append("Previous times: u=");
- TimeUtils.formatDuration(mLastUserTimeUs.valueAt(index) / 1000, sb);
- sb.append(" s=");
- TimeUtils.formatDuration(mLastSystemTimeUs.valueAt(index) / 1000, sb);
-
- sb.append("\nCurrent times: u=");
- TimeUtils.formatDuration(userTimeUs / 1000, sb);
- sb.append(" s=");
- TimeUtils.formatDuration(systemTimeUs / 1000, sb);
- sb.append("\nDelta: u=");
- TimeUtils.formatDuration(userTimeDeltaUs / 1000, sb);
- sb.append(" s=");
- TimeUtils.formatDuration(systemTimeDeltaUs / 1000, sb);
- Slog.e(TAG, sb.toString());
-
- userTimeDeltaUs = 0;
- systemTimeDeltaUs = 0;
- }
- }
-
- notifyCallback = (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0);
- }
- mLastUserTimeUs.put(uid, userTimeUs);
- mLastSystemTimeUs.put(uid, systemTimeUs);
- if (notifyCallback) {
- callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs);
- }
- }
- } catch (IOException e) {
- Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
- }
- mLastTimeReadUs = nowUs;
- }
-
- /**
- * Reads the proc file, calling into the callback with raw absolute value of time for each UID.
- * @param callback The callback to invoke for each line of the proc file.
- */
- public void readAbsolute(Callback callback) {
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) {
- TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
- String line;
- while ((line = reader.readLine()) != null) {
- splitter.setString(line);
- final String uidStr = splitter.next();
- final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10);
- final long userTimeUs = Long.parseLong(splitter.next(), 10);
- final long systemTimeUs = Long.parseLong(splitter.next(), 10);
- callback.onUidCpuTime(uid, userTimeUs, systemTimeUs);
- }
- } catch (IOException e) {
- Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage());
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
- }
- }
-
- /**
- * Removes the UID from the kernel module and from internal accounting data. Only
- * {@link BatteryStatsImpl} and its child processes should call this, as the change on Kernel is
- * visible system wide.
- *
- * @param uid The UID to remove.
- */
- public void removeUid(int uid) {
- final int index = mLastSystemTimeUs.indexOfKey(uid);
- if (index >= 0) {
- mLastSystemTimeUs.removeAt(index);
- mLastUserTimeUs.removeAt(index);
- }
- removeUidsFromKernelModule(uid, uid);
- }
-
- /**
- * Removes UIDs in a given range from the kernel module and internal accounting data. Only
- * {@link BatteryStatsImpl} and its child processes should call this, as the change on Kernel is
- * visible system wide.
- *
- * @param startUid the first uid to remove
- * @param endUid the last uid to remove
- */
- public void removeUidsInRange(int startUid, int endUid) {
- if (endUid < startUid) {
- return;
- }
- mLastSystemTimeUs.put(startUid, 0);
- mLastUserTimeUs.put(startUid, 0);
- mLastSystemTimeUs.put(endUid, 0);
- mLastUserTimeUs.put(endUid, 0);
- final int startIndex = mLastSystemTimeUs.indexOfKey(startUid);
- final int endIndex = mLastSystemTimeUs.indexOfKey(endUid);
- mLastSystemTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1);
- mLastUserTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1);
- removeUidsFromKernelModule(startUid, endUid);
- }
-
- private void removeUidsFromKernelModule(int startUid, int endUid) {
- Slog.d(TAG, "Removing uids " + startUid + "-" + endUid);
- final int oldMask = StrictMode.allowThreadDiskWritesMask();
- try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) {
- writer.write(startUid + "-" + endUid);
- writer.flush();
- } catch (IOException e) {
- Slog.e(TAG, "failed to remove uids " + startUid + " - " + endUid
- + " from uid_cputime module", e);
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
- }
- }
-}
diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReaderBase.java b/core/java/com/android/internal/os/KernelUidCpuTimeReaderBase.java
deleted file mode 100644
index 11e50e1ecb95..000000000000
--- a/core/java/com/android/internal/os/KernelUidCpuTimeReaderBase.java
+++ /dev/null
@@ -1,60 +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.
- */
-
-package com.android.internal.os;
-
-import android.annotation.Nullable;
-import android.os.SystemClock;
-import android.util.Slog;
-
-/**
- * The base class of all KernelUidCpuTimeReaders.
- *
- * This class is NOT designed to be thread-safe or accessed by more than one caller (due to
- * the nature of {@link #readDelta(Callback)}).
- */
-public abstract class KernelUidCpuTimeReaderBase<T extends KernelUidCpuTimeReaderBase.Callback> {
- protected static final boolean DEBUG = false;
- // Throttle interval in milliseconds
- private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L;
-
- private final String TAG = this.getClass().getSimpleName();
- private long mLastTimeReadMs = Long.MIN_VALUE;
- private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
-
- // A generic Callback interface (used by readDelta) to be extended by subclasses.
- public interface Callback {
- }
-
- public void readDelta(@Nullable T cb) {
- if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) {
- if (DEBUG) {
- Slog.d(TAG, "Throttle");
- }
- return;
- }
- readDeltaImpl(cb);
- mLastTimeReadMs = SystemClock.elapsedRealtime();
- }
-
- protected abstract void readDeltaImpl(@Nullable T cb);
-
- public void setThrottleInterval(long throttleInterval) {
- if (throttleInterval >= 0) {
- mThrottleInterval = throttleInterval;
- }
- }
-}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index 9a7fb9f2717b..0f0eeddf9a47 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -37,6 +37,7 @@ import java.util.concurrent.ThreadLocalRandom;
* @hide Only for use within the system server.
*/
public class LooperStats implements Looper.Observer {
+ public static final String DEBUG_ENTRY_PREFIX = "__DEBUG_";
private static final int SESSION_POOL_SIZE = 50;
@GuardedBy("mLock")
@@ -165,7 +166,7 @@ public class LooperStats implements Looper.Observer {
}
private ExportedEntry createDebugEntry(String variableName, long value) {
- final Entry entry = new Entry("__DEBUG_" + variableName);
+ final Entry entry = new Entry(DEBUG_ENTRY_PREFIX + variableName);
entry.messageCount = 1;
entry.recordedMessageCount = 1;
entry.totalLatencyMicro = value;
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 705bae4f24ef..b28f4cdd5a87 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -126,7 +126,7 @@ public final class Zygote {
private Zygote() {}
/** Called for some security initialization before any fork. */
- native static void nativeSecurityInit();
+ static native void nativeSecurityInit();
/**
* Forks a new VM instance. The current VM must have been started
@@ -170,9 +170,9 @@ public final class Zygote {
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
- uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
- fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName,
- packagesForUid, visibleVolIds);
+ uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
+ fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName,
+ packagesForUid, visibleVolIds);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -184,15 +184,20 @@ public final class Zygote {
return pid;
}
- native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
- int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- String packageName, String[] packagesForUid, String[] visibleVolIds);
+ private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
+ int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
+ int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
+ String appDataDir, String packageName, String[] packagesForUid, String[] visibleVolIds);
+
+ private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids,
+ int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
+ boolean startChildZygote, String instructionSet, String appDataDir, String packageName,
+ String[] packagesForUid, String[] visibleVolIds);
/**
* Called to do any initialization before starting an application.
*/
- native static void nativePreApplicationInit();
+ static native void nativePreApplicationInit();
/**
* Special method to start the system server process. In addition to the
@@ -223,7 +228,8 @@ public final class Zygote {
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkSystemServer(
- uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities);
+ uid, gid, gids, runtimeFlags, rlimits,
+ permittedCapabilities, effectiveCapabilities);
// Enable tracing as soon as we enter the system_server.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -232,13 +238,13 @@ public final class Zygote {
return pid;
}
- native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
+ private static native int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
/**
* Lets children of the zygote inherit open file descriptors to this path.
*/
- native protected static void nativeAllowFileAcrossFork(String path);
+ protected static native void nativeAllowFileAcrossFork(String path);
/**
* Installs a seccomp filter that limits setresuid()/setresgid() to the passed-in range
@@ -251,7 +257,22 @@ public final class Zygote {
* Zygote unmount storage space on initializing.
* This method is called once.
*/
- native protected static void nativeUnmountStorageOnInit();
+ protected static native void nativeUnmountStorageOnInit();
+
+ protected static native void nativeGetSocketFDs(boolean isPrimary);
+
+ private static native int nativeGetBlastulaPoolCount();
+
+ private static native int nativeGetBlastulaPoolEventFD();
+
+ private static native int nativeForkBlastula(int readPipeFD,
+ int writePipeFD,
+ int[] sessionSocketRawFDs);
+
+ private static native int[] nativeGetBlastulaPipeFDs();
+
+ private static native boolean nativeRemoveBlastulaTableEntry(int blastulaPID);
+
private static void callPostForkSystemServerHooks() {
// SystemServer specific post fork hooks run before child post fork hooks.
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 197e873a18bc..d61f10e7a195 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -65,7 +65,7 @@ interface IStatusBarService
int dismissalSurface, int dismissalSentiment, in NotificationVisibility nv);
void onNotificationVisibilityChanged( in NotificationVisibility[] newlyVisibleKeys,
in NotificationVisibility[] noLongerVisibleKeys);
- void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
+ void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded, in int notificationLocation);
void onNotificationDirectReplied(String key);
void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
boolean generatedByAsssistant);
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 605df040da59..f91b837410ba 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -327,10 +327,6 @@ public class CollectionUtils {
}
}
- public static @NonNull <T> List<T> defeatNullable(@Nullable List<T> val) {
- return (val != null) ? val : Collections.emptyList();
- }
-
/**
* @return the first element if not empty/null, null otherwise
*/
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 1aa32cce11e8..276cad9f6d6d 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -53,7 +53,6 @@ public class Protocol {
public static final int BASE_WIFI_PASSPOINT_MANAGER = 0x00028000;
public static final int BASE_WIFI_PASSPOINT_SERVICE = 0x00028100;
public static final int BASE_WIFI_LOGGER = 0x00028300;
- public static final int BASE_DHCP = 0x00030000;
public static final int BASE_DATA_CONNECTION = 0x00040000;
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000;
@@ -62,7 +61,6 @@ public class Protocol {
public static final int BASE_NETWORK_STATE_TRACKER = 0x00070000;
public static final int BASE_CONNECTIVITY_MANAGER = 0x00080000;
public static final int BASE_NETWORK_AGENT = 0x00081000;
- public static final int BASE_NETWORK_MONITOR = 0x00082000;
public static final int BASE_NETWORK_FACTORY = 0x00083000;
public static final int BASE_ETHERNET = 0x00084000;
public static final int BASE_LOWPAN = 0x00085000;
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 356d178cc4eb..8194a920d331 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -35,7 +35,6 @@ interface IInputMethodManager {
// TODO: Use ParceledListSlice instead
List<InputMethodInfo> getInputMethodList();
- List<InputMethodInfo> getVrInputMethodList();
// TODO: Use ParceledListSlice instead
List<InputMethodInfo> getEnabledInputMethodList();
List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId,
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d5dc703408e4..8d3c482104f7 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -34,6 +34,7 @@ import android.app.trust.TrustManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.AsyncTask;
import android.os.Handler;
@@ -176,6 +177,7 @@ public class LockPatternUtils {
private UserManager mUserManager;
private final Handler mHandler;
private final SparseLongArray mLockoutDeadlines = new SparseLongArray();
+ private Boolean mHasSecureLockScreen;
/**
* Use {@link TrustManager#isTrustUsuallyManaged(int)}.
@@ -706,6 +708,10 @@ public class LockPatternUtils {
* @param userId the user whose pattern is to be saved.
*/
public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) {
+ if (!hasSecureLockScreen()) {
+ throw new UnsupportedOperationException(
+ "This operation requires the lock screen feature.");
+ }
if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
throw new IllegalArgumentException("pattern must not be null and at least "
+ MIN_LOCK_PATTERN_SIZE + " dots long.");
@@ -801,6 +807,10 @@ public class LockPatternUtils {
/** Update the encryption password if it is enabled **/
private void updateEncryptionPassword(final int type, final String password) {
+ if (!hasSecureLockScreen()) {
+ throw new UnsupportedOperationException(
+ "This operation requires the lock screen feature.");
+ }
if (!isDeviceEncryptionEnabled()) {
return;
}
@@ -835,6 +845,10 @@ public class LockPatternUtils {
*/
public void saveLockPassword(String password, String savedPassword, int requestedQuality,
int userHandle) {
+ if (!hasSecureLockScreen()) {
+ throw new UnsupportedOperationException(
+ "This operation requires the lock screen feature.");
+ }
if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) {
throw new IllegalArgumentException("password must not be null and at least "
+ "of length " + MIN_LOCK_PASSWORD_SIZE);
@@ -1621,6 +1635,10 @@ public class LockPatternUtils {
*/
public boolean setLockCredentialWithToken(String credential, int type, int requestedQuality,
long tokenHandle, byte[] token, int userId) {
+ if (!hasSecureLockScreen()) {
+ throw new UnsupportedOperationException(
+ "This operation requires the lock screen feature.");
+ }
LockSettingsInternal localService = getLockSettingsInternal();
if (type != CREDENTIAL_TYPE_NONE) {
if (TextUtils.isEmpty(credential) || credential.length() < MIN_LOCK_PASSWORD_SIZE) {
@@ -1854,6 +1872,17 @@ public class LockPatternUtils {
return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM) != 0;
}
+ /**
+ * Return true if the device supports the lock screen feature, false otherwise.
+ */
+ public boolean hasSecureLockScreen() {
+ if (mHasSecureLockScreen == null) {
+ mHasSecureLockScreen = Boolean.valueOf(mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN));
+ }
+ return mHasSecureLockScreen.booleanValue();
+ }
+
public static boolean userOwnsFrpCredential(Context context, UserInfo info) {
return info != null && info.isPrimary() && info.isAdmin() && frpCredentialEnabled(context);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 5be70ef46d31..be127009a21d 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -61,8 +61,11 @@ cc_library_shared {
"android_database_SQLiteConnection.cpp",
"android_database_SQLiteGlobal.cpp",
"android_database_SQLiteDebug.cpp",
+ "android_graphics_Canvas.cpp",
+ "android_graphics_ColorSpace.cpp",
"android_graphics_drawable_AnimatedVectorDrawable.cpp",
"android_graphics_drawable_VectorDrawable.cpp",
+ "android_graphics_Picture.cpp",
"android_view_DisplayEventReceiver.cpp",
"android_view_DisplayListCanvas.cpp",
"android_view_TextureLayer.cpp",
@@ -117,8 +120,6 @@ cc_library_shared {
"android_util_StringBlock.cpp",
"android_util_XmlBlock.cpp",
"android_util_jar_StrictJarFile.cpp",
- "android_graphics_Canvas.cpp",
- "android_graphics_Picture.cpp",
"android/graphics/AnimatedImageDrawable.cpp",
"android/graphics/Bitmap.cpp",
"android/graphics/BitmapFactory.cpp",
@@ -185,6 +186,7 @@ cc_library_shared {
"android_hardware_UsbDevice.cpp",
"android_hardware_UsbDeviceConnection.cpp",
"android_hardware_UsbRequest.cpp",
+ "android_hardware_location_ActivityRecognitionHardware.cpp",
"android_util_FileObserver.cpp",
"android/opengl/poly_clip.cpp", // TODO: .arm
"android/opengl/util.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a586dc1e6d7e..18d9b5aeb1c8 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -101,6 +101,7 @@ extern int register_android_hardware_SoundTrigger(JNIEnv *env);
extern int register_android_hardware_UsbDevice(JNIEnv *env);
extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env);
extern int register_android_hardware_UsbRequest(JNIEnv *env);
+extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env);
extern int register_android_media_AudioEffectDescriptor(JNIEnv *env);
extern int register_android_media_AudioRecord(JNIEnv *env);
@@ -130,6 +131,7 @@ extern int register_android_content_res_ApkAssets(JNIEnv* env);
extern int register_android_graphics_Canvas(JNIEnv* env);
extern int register_android_graphics_CanvasProperty(JNIEnv* env);
extern int register_android_graphics_ColorFilter(JNIEnv* env);
+extern int register_android_graphics_ColorSpace(JNIEnv* env);
extern int register_android_graphics_DrawFilter(JNIEnv* env);
extern int register_android_graphics_FontFamily(JNIEnv* env);
extern int register_android_graphics_Matrix(JNIEnv* env);
@@ -1360,6 +1362,9 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_VintfRuntimeInfo),
REG_JNI(register_android_nio_utils),
REG_JNI(register_android_graphics_Canvas),
+ // This needs to be before register_android_graphics_Graphics, or the latter
+ // will not be able to find the jmethodID for ColorSpace.get().
+ REG_JNI(register_android_graphics_ColorSpace),
REG_JNI(register_android_graphics_Graphics),
REG_JNI(register_android_view_DisplayEventReceiver),
REG_JNI(register_android_view_RenderNode),
@@ -1456,6 +1461,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_hardware_UsbDevice),
REG_JNI(register_android_hardware_UsbDeviceConnection),
REG_JNI(register_android_hardware_UsbRequest),
+ REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
REG_JNI(register_android_media_AudioEffectDescriptor),
REG_JNI(register_android_media_AudioSystem),
REG_JNI(register_android_media_AudioRecord),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 876bd4fbfce4..ad51c4701d84 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -334,7 +334,7 @@ static int getPremulBitmapCreateFlags(bool isMutable) {
static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
jint offset, jint stride, jint width, jint height,
jint configHandle, jboolean isMutable,
- jfloatArray xyzD50, jobject transferParameters) {
+ jlong colorSpacePtr) {
SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
if (NULL != jColors) {
size_t n = env->GetArrayLength(jColors);
@@ -350,17 +350,8 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
}
SkBitmap bitmap;
- sk_sp<SkColorSpace> colorSpace;
-
- if (xyzD50 == nullptr || transferParameters == nullptr) {
- colorSpace = SkColorSpace::MakeSRGB();
- } else {
- skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
- skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
- colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
- }
-
- bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType, colorSpace));
+ bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType,
+ GraphicsJNI::getNativeColorSpace(colorSpacePtr)));
sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap);
if (!nativeBitmap) {
@@ -582,17 +573,14 @@ static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
bitmapErase(skBitmap, SkColor4f::FromColor(color), SkColorSpace::MakeSRGB());
}
-static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle, jobject jColorSpace,
- jfloat r, jfloat g, jfloat b, jfloat a) {
- sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
- if (GraphicsJNI::hasException(env)) {
- return;
- }
-
+static void Bitmap_eraseLong(JNIEnv* env, jobject, jlong bitmapHandle,
+ jlong colorSpaceHandle, jlong colorLong) {
LocalScopedBitmap bitmap(bitmapHandle);
SkBitmap skBitmap;
bitmap->getSkBitmap(&skBitmap);
- SkColor4f color = SkColor4f{r, g, b, a};
+
+ SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
+ sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
bitmapErase(skBitmap, color, cs);
}
@@ -1141,14 +1129,12 @@ static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphic
}
static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer,
- jfloatArray xyzD50, jobject transferParameters) {
- skcms_TransferFunction p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
- skcms_Matrix3x3 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
- sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
+ jlong colorSpacePtr) {
AHardwareBuffer* hwBuf = android_hardware_HardwareBuffer_getNativeHardwareBuffer(env,
hardwareBuffer);
sp<GraphicBuffer> buffer(AHardwareBuffer_to_GraphicBuffer(hwBuf));
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, colorSpace);
+ sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer,
+ GraphicsJNI::getNativeColorSpace(colorSpacePtr));
if (!bitmap.get()) {
ALOGW("failed to create hardware bitmap from hardware buffer");
return NULL;
@@ -1190,7 +1176,7 @@ static void Bitmap_setImmutable(JNIEnv* env, jobject, jlong bitmapHandle) {
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gBitmapMethods[] = {
- { "nativeCreate", "([IIIIIIZ[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/Bitmap;",
+ { "nativeCreate", "([IIIIIIZJ)Landroid/graphics/Bitmap;",
(void*)Bitmap_creator },
{ "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;",
(void*)Bitmap_copy },
@@ -1204,7 +1190,7 @@ static const JNINativeMethod gBitmapMethods[] = {
{ "nativeCompress", "(JIILjava/io/OutputStream;[B)Z",
(void*)Bitmap_compress },
{ "nativeErase", "(JI)V", (void*)Bitmap_erase },
- { "nativeErase", "(JLandroid/graphics/ColorSpace;FFFF)V", (void*)Bitmap_eraseLong },
+ { "nativeErase", "(JJJ)V", (void*)Bitmap_eraseLong },
{ "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes },
{ "nativeConfig", "(J)I", (void*)Bitmap_config },
{ "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha },
@@ -1236,7 +1222,7 @@ static const JNINativeMethod gBitmapMethods[] = {
(void*)Bitmap_copyPreserveInternalConfig },
{ "nativeCreateHardwareBitmap", "(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;",
(void*) Bitmap_createHardwareBitmap },
- { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/Bitmap;",
+ { "nativeWrapHardwareBufferBitmap", "(Landroid/hardware/HardwareBuffer;J)Landroid/graphics/Bitmap;",
(void*) Bitmap_wrapHardwareBufferBitmap },
{ "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;",
(void*) Bitmap_createGraphicBufferHandle },
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 4b0ab5b2d6ce..7d0d3d8442a1 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -179,7 +179,7 @@ static bool needsFineScale(const SkISize fullSize, const SkISize decodedSize,
}
static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
- jobject padding, jobject options) {
+ jobject padding, jobject options, jlong colorSpaceHandle) {
// Set default values for the options parameters.
int sampleSize = 1;
bool onlyDecodeSize = false;
@@ -189,7 +189,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
float scale = 1.0f;
bool requireUnpremultiplied = false;
jobject javaBitmap = NULL;
- sk_sp<SkColorSpace> prefColorSpace = nullptr;
+ sk_sp<SkColorSpace> prefColorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
// Update with options supplied by the client.
if (options != NULL) {
@@ -213,8 +213,6 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
- jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID);
- prefColorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
@@ -515,7 +513,7 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream,
}
static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
- jobject padding, jobject options) {
+ jobject padding, jobject options, jlong colorSpaceHandle) {
jobject bitmap = NULL;
std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));
@@ -524,13 +522,13 @@ static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteA
std::unique_ptr<SkStreamRewindable> bufferedStream(
SkFrontBufferedStream::Make(std::move(stream), SkCodec::MinBufferedBytesNeeded()));
SkASSERT(bufferedStream.get() != NULL);
- bitmap = doDecode(env, std::move(bufferedStream), padding, options);
+ bitmap = doDecode(env, std::move(bufferedStream), padding, options, colorSpaceHandle);
}
return bitmap;
}
static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,
- jobject padding, jobject bitmapFactoryOptions) {
+ jobject padding, jobject bitmapFactoryOptions, jlong colorSpaceHandle) {
NPE_CHECK_RETURN_ZERO(env, fileDescriptor);
@@ -566,7 +564,8 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi
// If there is no offset for the file descriptor, we use SkFILEStream directly.
if (::lseek(descriptor, 0, SEEK_CUR) == 0) {
assert(isSeekable(dupDescriptor));
- return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions);
+ return doDecode(env, std::move(fileStream), padding, bitmapFactoryOptions,
+ colorSpaceHandle);
}
// Use a buffered stream. Although an SkFILEStream can be rewound, this
@@ -575,24 +574,25 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi
std::unique_ptr<SkStreamRewindable> stream(SkFrontBufferedStream::Make(std::move(fileStream),
SkCodec::MinBufferedBytesNeeded()));
- return doDecode(env, std::move(stream), padding, bitmapFactoryOptions);
+ return doDecode(env, std::move(stream), padding, bitmapFactoryOptions, colorSpaceHandle);
}
static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
- jobject padding, jobject options) {
+ jobject padding, jobject options, jlong colorSpaceHandle) {
Asset* asset = reinterpret_cast<Asset*>(native_asset);
// since we know we'll be done with the asset when we return, we can
// just use a simple wrapper
- return doDecode(env, skstd::make_unique<AssetStreamAdaptor>(asset), padding, options);
+ return doDecode(env, skstd::make_unique<AssetStreamAdaptor>(asset), padding, options,
+ colorSpaceHandle);
}
static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
- jint offset, jint length, jobject options) {
+ jint offset, jint length, jobject options, jlong colorSpaceHandle) {
AutoJavaByteArray ar(env, byteArray);
return doDecode(env, skstd::make_unique<SkMemoryStream>(ar.ptr() + offset, length, false),
- nullptr, options);
+ nullptr, options, colorSpaceHandle);
}
static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
@@ -600,31 +600,26 @@ static jboolean nativeIsSeekable(JNIEnv* env, jobject, jobject fileDescriptor) {
return isSeekable(descriptor) ? JNI_TRUE : JNI_FALSE;
}
-jobject decodeBitmap(JNIEnv* env, void* data, size_t size) {
- return doDecode(env, skstd::make_unique<SkMemoryStream>(data, size),
- nullptr, nullptr);
-}
-
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gMethods[] = {
{ "nativeDecodeStream",
- "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+ "(Ljava/io/InputStream;[BLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;",
(void*)nativeDecodeStream
},
{ "nativeDecodeFileDescriptor",
- "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+ "(Ljava/io/FileDescriptor;Landroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;",
(void*)nativeDecodeFileDescriptor
},
{ "nativeDecodeAsset",
- "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+ "(JLandroid/graphics/Rect;Landroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;",
(void*)nativeDecodeAsset
},
{ "nativeDecodeByteArray",
- "([BIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+ "([BIILandroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;",
(void*)nativeDecodeByteArray
},
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
index 1ee49fa0af77..e37c98dc66ff 100644
--- a/core/jni/android/graphics/BitmapFactory.h
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -28,6 +28,4 @@ extern jmethodID gBitmapConfig_nativeToConfigMethodID;
jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format);
-jobject decodeBitmap(JNIEnv* env, void* data, size_t size);
-
#endif // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index f831c051182e..b4ba749b75bf 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -124,7 +124,7 @@ static jobject nativeNewInstanceFromAsset(JNIEnv* env, jobject clazz,
* reportSizeToVM not supported
*/
static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint inputX,
- jint inputY, jint inputWidth, jint inputHeight, jobject options) {
+ jint inputY, jint inputWidth, jint inputHeight, jobject options, jlong colorSpaceHandle) {
// Set default options.
int sampleSize = 1;
@@ -132,14 +132,12 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, jlong brdHandle, jint in
bool requireUnpremul = false;
jobject javaBitmap = NULL;
bool isHardware = false;
- sk_sp<SkColorSpace> colorSpace = nullptr;
+ sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
// Update the default options with any options supplied by the client.
if (NULL != options) {
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
- jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID);
- colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
@@ -255,7 +253,7 @@ static void nativeClean(JNIEnv* env, jobject, jlong brdHandle) {
static const JNINativeMethod gBitmapRegionDecoderMethods[] = {
{ "nativeDecodeRegion",
- "(JIIIILandroid/graphics/BitmapFactory$Options;)Landroid/graphics/Bitmap;",
+ "(JIIIILandroid/graphics/BitmapFactory$Options;J)Landroid/graphics/Bitmap;",
(void*)nativeDecodeRegion},
{ "nativeGetHeight", "(J)I", (void*)nativeGetHeight},
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 9e74b883a298..6570992b4b23 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -178,23 +178,11 @@ static jclass gVMRuntime_class;
static jmethodID gVMRuntime_newNonMovableArray;
static jmethodID gVMRuntime_addressOf;
-static jfieldID gTransferParams_aFieldID;
-static jfieldID gTransferParams_bFieldID;
-static jfieldID gTransferParams_cFieldID;
-static jfieldID gTransferParams_dFieldID;
-static jfieldID gTransferParams_eFieldID;
-static jfieldID gTransferParams_fFieldID;
-static jfieldID gTransferParams_gFieldID;
-
static jclass gColorSpace_class;
-static jfieldID gColorSpace_IlluminantD50FieldID;
-static jmethodID gColorSpace_adaptMethodID;
static jmethodID gColorSpace_getMethodID;
static jmethodID gColorSpace_matchMethodID;
static jclass gColorSpaceRGB_class;
-static jmethodID gColorSpaceRGB_getTransferParametersMethodID;
-static jmethodID gColorSpaceRGB_getTransformMethodID;
static jmethodID gColorSpaceRGB_constructorMethodID;
static jclass gColorSpace_Named_class;
@@ -424,63 +412,6 @@ jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
///////////////////////////////////////////////////////////////////////////////
-skcms_TransferFunction GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
- skcms_TransferFunction p;
- p.a = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
- p.b = (float) env->GetDoubleField(transferParams, gTransferParams_bFieldID);
- p.c = (float) env->GetDoubleField(transferParams, gTransferParams_cFieldID);
- p.d = (float) env->GetDoubleField(transferParams, gTransferParams_dFieldID);
- p.e = (float) env->GetDoubleField(transferParams, gTransferParams_eFieldID);
- p.f = (float) env->GetDoubleField(transferParams, gTransferParams_fFieldID);
- p.g = (float) env->GetDoubleField(transferParams, gTransferParams_gFieldID);
- return p;
-}
-
-skcms_Matrix3x3 GraphicsJNI::getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
- skcms_Matrix3x3 xyzMatrix;
- jfloat* array = env->GetFloatArrayElements(xyzD50, NULL);
- xyzMatrix.vals[0][0] = array[0];
- xyzMatrix.vals[1][0] = array[1];
- xyzMatrix.vals[2][0] = array[2];
- xyzMatrix.vals[0][1] = array[3];
- xyzMatrix.vals[1][1] = array[4];
- xyzMatrix.vals[2][1] = array[5];
- xyzMatrix.vals[0][2] = array[6];
- xyzMatrix.vals[1][2] = array[7];
- xyzMatrix.vals[2][2] = array[8];
- env->ReleaseFloatArrayElements(xyzD50, array, 0);
- return xyzMatrix;
-}
-
-sk_sp<SkColorSpace> GraphicsJNI::getNativeColorSpace(JNIEnv* env, jobject colorSpace) {
- if (colorSpace == nullptr) return nullptr;
- if (!env->IsInstanceOf(colorSpace, gColorSpaceRGB_class)) {
- doThrowIAE(env, "The color space must be an RGB color space");
- return nullptr;
- }
-
- jobject transferParams = env->CallObjectMethod(colorSpace,
- gColorSpaceRGB_getTransferParametersMethodID);
- if (transferParams == nullptr) {
- doThrowIAE(env, "The color space must use an ICC parametric transfer function");
- return nullptr;
- }
-
- jfloatArray illuminantD50 = (jfloatArray) env->GetStaticObjectField(gColorSpace_class,
- gColorSpace_IlluminantD50FieldID);
- jobject colorSpaceD50 = env->CallStaticObjectMethod(gColorSpace_class,
- gColorSpace_adaptMethodID, colorSpace, illuminantD50);
-
- jfloatArray xyzD50 = (jfloatArray) env->CallObjectMethod(colorSpaceD50,
- gColorSpaceRGB_getTransformMethodID);
-
- skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
- skcms_TransferFunction transferFunction = getNativeTransferParameters(env, transferParams);
-
- return SkColorSpace::MakeRGB(transferFunction, xyzMatrix);
-}
-
-
jobject GraphicsJNI::getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
SkColorType decodeColorType) {
jobject colorSpace = nullptr;
@@ -712,20 +643,7 @@ int register_android_graphics_Graphics(JNIEnv* env)
"(Ljava/lang/Class;I)Ljava/lang/Object;");
gVMRuntime_addressOf = GetMethodIDOrDie(env, gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J");
- jclass transfer_params_class = FindClassOrDie(env, "android/graphics/ColorSpace$Rgb$TransferParameters");
- gTransferParams_aFieldID = GetFieldIDOrDie(env, transfer_params_class, "a", "D");
- gTransferParams_bFieldID = GetFieldIDOrDie(env, transfer_params_class, "b", "D");
- gTransferParams_cFieldID = GetFieldIDOrDie(env, transfer_params_class, "c", "D");
- gTransferParams_dFieldID = GetFieldIDOrDie(env, transfer_params_class, "d", "D");
- gTransferParams_eFieldID = GetFieldIDOrDie(env, transfer_params_class, "e", "D");
- gTransferParams_fFieldID = GetFieldIDOrDie(env, transfer_params_class, "f", "D");
- gTransferParams_gFieldID = GetFieldIDOrDie(env, transfer_params_class, "g", "D");
-
gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace"));
- gColorSpace_IlluminantD50FieldID = GetStaticFieldIDOrDie(env,
- gColorSpace_class, "ILLUMINANT_D50", "[F");
- gColorSpace_adaptMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "adapt",
- "(Landroid/graphics/ColorSpace;[F)Landroid/graphics/ColorSpace;");
gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class,
"get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;");
gColorSpace_matchMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "match",
@@ -735,10 +653,6 @@ int register_android_graphics_Graphics(JNIEnv* env)
FindClassOrDie(env, "android/graphics/ColorSpace$Rgb"));
gColorSpaceRGB_constructorMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
"<init>", "(Ljava/lang/String;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V");
- gColorSpaceRGB_getTransferParametersMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
- "getTransferParameters", "()Landroid/graphics/ColorSpace$Rgb$TransferParameters;");
- gColorSpaceRGB_getTransformMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
- "getTransform", "()[F");
gColorSpace_Named_class = MakeGlobalRefOrDie(env,
FindClassOrDie(env, "android/graphics/ColorSpace$Named"));
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 699d153874a2..dc0d022d94c0 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -100,12 +100,27 @@ public:
int srcStride, int x, int y, int width, int height,
SkBitmap* dstBitmap);
- static skcms_TransferFunction getNativeTransferParameters(JNIEnv* env, jobject transferParams);
- static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50);
- static sk_sp<SkColorSpace> getNativeColorSpace(JNIEnv* env, jobject colorSpace);
+ /**
+ * Convert the native SkColorSpace retrieved from ColorSpace.Rgb.getNativeInstance().
+ *
+ * This will never throw an Exception. If the ColorSpace is one that Skia cannot
+ * use, ColorSpace.Rgb.getNativeInstance() would have thrown an Exception. It may,
+ * however, be nullptr, which may be acceptable.
+ */
+ static sk_sp<SkColorSpace> getNativeColorSpace(jlong colorSpaceHandle);
static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
SkColorType decodeColorType);
+
+ /**
+ * Convert from a Java @ColorLong to an SkColor4f that Skia can use directly.
+ *
+ * This ignores the encoded ColorSpace, besides checking to see if it is sRGB,
+ * which is encoded differently. The color space should be passed down separately
+ * via ColorSpace#getNativeInstance(), and converted with getNativeColorSpace(),
+ * above.
+ */
+ static SkColor4f convertColorLong(jlong color);
};
class HeapAllocator : public SkBRDAllocator {
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index df735ae12feb..5f126653600e 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -202,7 +202,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
jint desiredWidth, jint desiredHeight, jobject jsubset,
jboolean requireMutable, jint allocator,
jboolean requireUnpremul, jboolean preferRamOverQuality,
- jboolean asAlphaMask, jobject jcolorSpace) {
+ jboolean asAlphaMask, jlong colorSpaceHandle) {
auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
SkAndroidCodec* codec = decoder->mCodec.get();
const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight);
@@ -256,7 +256,7 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong
// This is currently the only way to know that we should decode to F16.
colorType = codec->computeOutputColorType(colorType);
}
- sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
+ sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
colorSpace = codec->computeOutputColorSpace(colorType, colorSpace);
decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
@@ -508,7 +508,7 @@ static const JNINativeMethod gImageDecoderMethods[] = {
{ "nCreate", "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
{ "nCreate", "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
{ "nCreate", "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
- { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZLandroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;",
+ { "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJ)Landroid/graphics/Bitmap;",
(void*) ImageDecoder_nDecodeBitmap },
{ "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize },
{ "nGetPadding", "(JLandroid/graphics/Rect;)V", (void*) ImageDecoder_nGetPadding },
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index e2e3042ee5b9..7679c5b63274 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -557,41 +557,6 @@ namespace PaintGlue {
return result;
}
- // FIXME: Make this CriticalNative when we no longer need to use JNIEnv. b/122514935 will allow
- // passing the SkColorSpace directly from JNI.
- static void setColor(JNIEnv* env, jobject clazz, jlong paintHandle, jobject jColorSpace,
- jfloat r, jfloat g, jfloat b, jfloat a) {
- sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
- if (GraphicsJNI::hasException(env)) {
- return;
- }
-
- SkColor4f color = SkColor4f{r, g, b, a};
- reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get());
- }
-
- // FIXME: Make this CriticalNative when we no longer need to use JNIEnv. b/122514935 will allow
- // passing the SkColorSpace directly from JNI.
- static void setShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius,
- jfloat dx, jfloat dy, jobject jColorSpace,
- jfloat r, jfloat g, jfloat b, jfloat a) {
- sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(env, jColorSpace);
- if (GraphicsJNI::hasException(env)) {
- return;
- }
-
- SkColor4f color = SkColor4f{r, g, b, a};
-
- Paint* paint = reinterpret_cast<Paint*>(paintHandle);
- if (radius <= 0) {
- paint->setLooper(nullptr);
- }
- else {
- SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
- paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
- }
- }
-
// ------------------ @FastNative ---------------------------
static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
@@ -787,6 +752,13 @@ namespace PaintGlue {
obj->setStyle(style);
}
+ static void setColor(jlong paintHandle, jlong colorSpaceHandle,
+ jfloat r, jfloat g, jfloat b, jfloat a) {
+ sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+ SkColor4f color = SkColor4f{r, g, b, a};
+ reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get());
+ }
+
static void setAlpha(jlong paintHandle, jint a) {
reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
}
@@ -1034,6 +1006,22 @@ namespace PaintGlue {
return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize);
}
+ static void setShadowLayer(jlong paintHandle, jfloat radius,
+ jfloat dx, jfloat dy, jlong colorSpaceHandle,
+ jfloat r, jfloat g, jfloat b, jfloat a) {
+ sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
+ SkColor4f color = SkColor4f{r, g, b, a};
+
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ if (radius <= 0) {
+ paint->setLooper(nullptr);
+ }
+ else {
+ SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
+ paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
+ }
+ }
+
static jboolean hasShadowLayer(jlong paintHandle) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
@@ -1082,9 +1070,6 @@ static const JNINativeMethod methods[] = {
{"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
{"nGetOffsetForAdvance", "(J[CIIIIZF)I",
(void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
- {"nSetColor","(JLandroid/graphics/ColorSpace;FFFF)V", (void*) PaintGlue::setColor},
- {"nSetShadowLayer", "(JFFFLandroid/graphics/ColorSpace;FFFF)V",
- (void*)PaintGlue::setShadowLayer},
// --------------- @FastNative ----------------------
@@ -1114,6 +1099,7 @@ static const JNINativeMethod methods[] = {
{"nSetDither","(JZ)V", (void*) PaintGlue::setDither},
{"nGetStyle","(J)I", (void*) PaintGlue::getStyle},
{"nSetStyle","(JI)V", (void*) PaintGlue::setStyle},
+ {"nSetColor","(JJFFFF)V", (void*) PaintGlue::setColor},
{"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha},
{"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth},
{"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth},
@@ -1154,6 +1140,7 @@ static const JNINativeMethod methods[] = {
{"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness},
{"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
{"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
+ {"nSetShadowLayer", "(JFFFJFFFF)V", (void*)PaintGlue::setShadowLayer},
{"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
{"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
};
diff --git a/core/jni/android_app_ActivityThread.cpp b/core/jni/android_app_ActivityThread.cpp
index d56e4c51124d..93f2525eb29d 100644
--- a/core/jni/android_app_ActivityThread.cpp
+++ b/core/jni/android_app_ActivityThread.cpp
@@ -24,6 +24,8 @@
#include "core_jni_helpers.h"
#include <unistd.h>
+#include <bionic_malloc.h>
+
namespace android {
static void android_app_ActivityThread_purgePendingResources(JNIEnv* env, jobject clazz) {
@@ -38,13 +40,18 @@ android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject java
minikin::Layout::dumpMinikinStats(fd);
}
+static void android_app_ActivityThread_initZygoteChildHeapProfiling(JNIEnv* env, jobject clazz) {
+ android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0);
+}
static JNINativeMethod gActivityThreadMethods[] = {
// ------------ Regular JNI ------------------
{ "nPurgePendingResources", "()V",
(void*) android_app_ActivityThread_purgePendingResources },
{ "nDumpGraphicsInfo", "(Ljava/io/FileDescriptor;)V",
- (void*) android_app_ActivityThread_dumpGraphics }
+ (void*) android_app_ActivityThread_dumpGraphics },
+ { "nInitZygoteChildHeapProfiling", "()V",
+ (void*) android_app_ActivityThread_initZygoteChildHeapProfiling }
};
int register_android_app_ActivityThread(JNIEnv* env) {
diff --git a/core/jni/android_graphics_ColorSpace.cpp b/core/jni/android_graphics_ColorSpace.cpp
new file mode 100644
index 000000000000..7648fd021d18
--- /dev/null
+++ b/core/jni/android_graphics_ColorSpace.cpp
@@ -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.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+
+#include "SkColor.h"
+#include "SkColorSpace.h"
+
+using namespace android;
+
+static skcms_Matrix3x3 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
+ skcms_Matrix3x3 xyzMatrix;
+ jfloat* array = env->GetFloatArrayElements(xyzD50, NULL);
+ xyzMatrix.vals[0][0] = array[0];
+ xyzMatrix.vals[1][0] = array[1];
+ xyzMatrix.vals[2][0] = array[2];
+ xyzMatrix.vals[0][1] = array[3];
+ xyzMatrix.vals[1][1] = array[4];
+ xyzMatrix.vals[2][1] = array[5];
+ xyzMatrix.vals[0][2] = array[6];
+ xyzMatrix.vals[1][2] = array[7];
+ xyzMatrix.vals[2][2] = array[8];
+ env->ReleaseFloatArrayElements(xyzD50, array, 0);
+ return xyzMatrix;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static float halfToFloat(uint16_t bits) {
+ __fp16 h;
+ memcpy(&h, &bits, 2);
+ return (float)h;
+}
+
+SkColor4f GraphicsJNI::convertColorLong(jlong color) {
+ if ((color & 0x3f) == 0) {
+ // This corresponds to sRGB, which is treated differently than the rest.
+ uint8_t a = color >> 56 & 0xff;
+ uint8_t r = color >> 48 & 0xff;
+ uint8_t g = color >> 40 & 0xff;
+ uint8_t b = color >> 32 & 0xff;
+ SkColor c = SkColorSetARGB(a, r, g, b);
+ return SkColor4f::FromColor(c);
+ }
+
+ // These match the implementation of android.graphics.Color#red(long) etc.
+ float r = halfToFloat((uint16_t)(color >> 48 & 0xffff));
+ float g = halfToFloat((uint16_t)(color >> 32 & 0xffff));
+ float b = halfToFloat((uint16_t)(color >> 16 & 0xffff));
+ float a = (color >> 6 & 0x3ff) / 1023.0f;
+
+ return SkColor4f{r, g, b, a};
+}
+
+sk_sp<SkColorSpace> GraphicsJNI::getNativeColorSpace(jlong colorSpaceHandle) {
+ if (colorSpaceHandle == 0) return nullptr;
+ return sk_ref_sp(reinterpret_cast<SkColorSpace*>(colorSpaceHandle));
+}
+
+static void unref_colorSpace(SkColorSpace* cs) {
+ SkSafeUnref(cs);
+}
+
+static jlong ColorSpace_getNativeFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&unref_colorSpace));
+}
+
+static jlong ColorSpace_creator(JNIEnv* env, jobject, jfloat a, jfloat b, jfloat c,
+ jfloat d, jfloat e, jfloat f, jfloat g, jfloatArray xyzD50) {
+ skcms_TransferFunction p;
+ p.a = a;
+ p.b = b;
+ p.c = c;
+ p.d = d;
+ p.e = e;
+ p.f = f;
+ p.g = g;
+ skcms_Matrix3x3 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
+
+ return reinterpret_cast<jlong>(SkColorSpace::MakeRGB(p, xyzMatrix).release());
+}
+
+static const JNINativeMethod gColorSpaceRgbMethods[] = {
+ { "nativeGetNativeFinalizer", "()J", (void*)ColorSpace_getNativeFinalizer },
+ { "nativeCreate", "(FFFFFFF[F)J", (void*)ColorSpace_creator }
+};
+
+namespace android {
+
+int register_android_graphics_ColorSpace(JNIEnv* env) {
+ return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb",
+ gColorSpaceRgbMethods, NELEM(gColorSpaceRgbMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
new file mode 100644
index 000000000000..1c9ab9403298
--- /dev/null
+++ b/core/jni/android_hardware_location_ActivityRecognitionHardware.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright 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_TAG "ActivityRecognitionHardware"
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+
+// #include <hardware/activity_recognition.h>
+// The activity recognition HAL is being deprecated. This means -
+// i) Android framework code shall not depend on activity recognition
+// being provided through the activity_recognition.h interface.
+// ii) activity recognition HAL will not be binderized as the other HALs.
+//
+
+/**
+ * Initializes the ActivityRecognitionHardware class from the native side.
+ */
+static void class_init(JNIEnv* /*env*/, jclass /*clazz*/) {
+ ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op",
+ __FUNCTION__);
+}
+
+/**
+ * Initializes and connect the callbacks handlers in the HAL.
+ */
+static void initialize(JNIEnv* /*env*/, jobject /*obj*/) {
+ ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op",
+ __FUNCTION__);
+}
+
+/**
+ * De-initializes the ActivityRecognitionHardware from the native side.
+ */
+static void release(JNIEnv* /*env*/, jobject /*obj*/) {
+ ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op",
+ __FUNCTION__);
+}
+
+/**
+ * Returns true if ActivityRecognition HAL is supported, false otherwise.
+ */
+static jboolean is_supported(JNIEnv* /*env*/, jclass /*clazz*/) {
+ ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op",
+ __FUNCTION__);
+ return JNI_FALSE;
+}
+
+/**
+ * Gets an array representing the supported activities.
+ */
+static jobjectArray get_supported_activities(JNIEnv* /*env*/, jobject /*obj*/) {
+ ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op",
+ __FUNCTION__);
+ return NULL;
+}
+
+/**
+ * Enables a given activity event to be actively monitored.
+ */
+static int enable_activity_event(
+ JNIEnv* /*env*/,
+ jobject /*obj*/,
+ jint /*activity_handle*/,
+ jint /*event_type*/,
+ jlong /*report_latency_ns*/) {
+ ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op",
+ __FUNCTION__);
+ return android::NO_INIT;
+}
+
+/**
+ * Disables a given activity event from being actively monitored.
+ */
+static int disable_activity_event(
+ JNIEnv* /*env*/,
+ jobject /*obj*/,
+ jint /*activity_handle*/,
+ jint /*event_type*/) {
+ ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op",
+ __FUNCTION__);
+ return android::NO_INIT;
+}
+
+/**
+ * Request flush for al batch buffers.
+ */
+static int flush(JNIEnv* /*env*/, jobject /*obj*/) {
+ ALOGE("activity_recognition HAL is deprecated. %s is effectively a no-op",
+ __FUNCTION__);
+ return android::NO_INIT;
+}
+
+
+static const JNINativeMethod sMethods[] = {
+ // {"name", "signature", (void*) functionPointer },
+ { "nativeClassInit", "()V", (void*) class_init },
+ { "nativeInitialize", "()V", (void*) initialize },
+ { "nativeRelease", "()V", (void*) release },
+ { "nativeIsSupported", "()Z", (void*) is_supported },
+ { "nativeGetSupportedActivities", "()[Ljava/lang/String;", (void*) get_supported_activities },
+ { "nativeEnableActivityEvent", "(IIJ)I", (void*) enable_activity_event },
+ { "nativeDisableActivityEvent", "(II)I", (void*) disable_activity_event },
+ { "nativeFlush", "()I", (void*) flush },
+};
+
+/**
+ * Registration method invoked in JNI load.
+ */
+int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env) {
+ return jniRegisterNativeMethods(
+ env,
+ "android/hardware/location/ActivityRecognitionHardware",
+ sMethods,
+ NELEM(sMethods));
+}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 9b138ebb760a..7eddcfe425d3 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -16,8 +16,11 @@
#define LOG_TAG "NetUtils"
+#include <vector>
+
#include "jni.h"
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
#include "NetdClient.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
@@ -55,6 +58,31 @@ static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udp
static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest);
static const uint16_t kDhcpClientPort = 68;
+constexpr int MAXPACKETSIZE = 8 * 1024;
+// FrameworkListener limits the size of commands to 1024 bytes. TODO: fix this.
+constexpr int MAXCMDSIZE = 1024;
+
+static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
+ ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
+ if (detailMessage.get() == NULL) {
+ // Not really much we can do here. We're probably dead in the water,
+ // but let's try to stumble on...
+ env->ExceptionClear();
+ }
+ static jclass errnoExceptionClass =
+ MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/system/ErrnoException"));
+
+ static jmethodID errnoExceptionCtor =
+ GetMethodIDOrDie(env, errnoExceptionClass,
+ "<init>", "(Ljava/lang/String;I)V");
+
+ jobject exception = env->NewObject(errnoExceptionClass,
+ errnoExceptionCtor,
+ detailMessage.get(),
+ error);
+ env->Throw(reinterpret_cast<jthrowable>(exception));
+}
+
static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
struct sock_filter filter_code[] = {
@@ -372,6 +400,63 @@ static void android_net_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray
}
}
+static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jint netId,
+ jstring dname, jint ns_class, jint ns_type, jint flags) {
+ const jsize javaCharsCount = env->GetStringLength(dname);
+ const jsize byteCountUTF8 = env->GetStringUTFLength(dname);
+
+ // Only allow dname which could be simply formatted to UTF8.
+ // In native layer, res_mkquery would re-format the input char array to packet.
+ std::vector<char> queryname(byteCountUTF8 + 1, 0);
+
+ env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname.data());
+ int fd = resNetworkQuery(netId, queryname.data(), ns_class, ns_type, flags);
+
+ if (fd < 0) {
+ throwErrnoException(env, "resNetworkQuery", -fd);
+ return nullptr;
+ }
+
+ return jniCreateFileDescriptor(env, fd);
+}
+
+static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jint netId,
+ jbyteArray msg, jint msgLen, jint flags) {
+ uint8_t data[MAXCMDSIZE];
+
+ checkLenAndCopy(env, msg, msgLen, data);
+ int fd = resNetworkSend(netId, data, msgLen, flags);
+
+ if (fd < 0) {
+ throwErrnoException(env, "resNetworkSend", -fd);
+ return nullptr;
+ }
+
+ return jniCreateFileDescriptor(env, fd);
+}
+
+static jbyteArray android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int rcode;
+ std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
+
+ int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
+ if (res < 0) {
+ throwErrnoException(env, "resNetworkResult", -res);
+ return nullptr;
+ }
+
+ jbyteArray answer = env->NewByteArray(res);
+ if (answer == nullptr) {
+ throwErrnoException(env, "resNetworkResult", ENOMEM);
+ return nullptr;
+ } else {
+ env->SetByteArrayRegion(answer, 0, res,
+ reinterpret_cast<jbyte*>(buf.data()));
+ }
+
+ return answer;
+}
// ----------------------------------------------------------------------------
@@ -391,6 +476,9 @@ static const JNINativeMethod gNetworkUtilMethods[] = {
{ "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
{ "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
{ "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
+ { "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
+ { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
+ { "resNetworkResult", "(Ljava/io/FileDescriptor;)[B", (void*) android_net_utils_resNetworkResult },
};
int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 7d63ec9a2ecf..783724853b8a 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -45,6 +45,7 @@
#include <meminfo/sysmeminfo.h>
#include <memtrack/memtrack.h>
#include <memunreachable/memunreachable.h>
+#include <android-base/strings.h>
#include "android_os_Debug.h"
namespace android
@@ -231,244 +232,162 @@ static int read_memtrack_memory(int pid, struct graphics_memory_pss* graphics_me
return err;
}
-static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss)
+static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
{
- char line[1024];
- int len, nameLen;
- bool skip, done = false;
-
- unsigned pss = 0, swappable_pss = 0, rss = 0;
- float sharing_proportion = 0.0;
- unsigned shared_clean = 0, shared_dirty = 0;
- unsigned private_clean = 0, private_dirty = 0;
- unsigned swapped_out = 0, swapped_out_pss = 0;
- bool is_swappable = false;
- unsigned temp;
-
- uint64_t start;
- uint64_t end = 0;
- uint64_t prevEnd = 0;
- char* name;
- int name_pos;
-
- int whichHeap = HEAP_UNKNOWN;
- int subHeap = HEAP_UNKNOWN;
- int prevHeap = HEAP_UNKNOWN;
-
*foundSwapPss = false;
+ uint64_t prev_end = 0;
+ int prev_heap = HEAP_UNKNOWN;
- if(fgets(line, sizeof(line), fp) == 0) return;
-
- while (!done) {
- prevHeap = whichHeap;
- prevEnd = end;
- whichHeap = HEAP_UNKNOWN;
- subHeap = HEAP_UNKNOWN;
- skip = false;
- is_swappable = false;
-
- len = strlen(line);
- if (len < 1) return;
- line[--len] = 0;
-
- if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
- skip = true;
+ std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
+ auto vma_scan = [&](const meminfo::Vma& vma) {
+ int which_heap = HEAP_UNKNOWN;
+ int sub_heap = HEAP_UNKNOWN;
+ bool is_swappable = false;
+ std::string name;
+ if (base::EndsWith(vma.name, " (deleted)")) {
+ name = vma.name.substr(0, vma.name.size() - strlen(" (deleted)"));
} else {
- while (isspace(line[name_pos])) {
- name_pos += 1;
+ name = vma.name;
+ }
+
+ uint32_t namesz = name.size();
+ if (base::StartsWith(name, "[heap]")) {
+ which_heap = HEAP_NATIVE;
+ } else if (base::StartsWith(name, "[anon:libc_malloc]")) {
+ which_heap = HEAP_NATIVE;
+ } else if (base::StartsWith(name, "[stack")) {
+ which_heap = HEAP_NATIVE;
+ } else if (base::EndsWith(name, ".so")) {
+ which_heap = HEAP_SO;
+ is_swappable = true;
+ } else if (base::EndsWith(name, ".jar")) {
+ which_heap = HEAP_JAR;
+ is_swappable = true;
+ } else if (base::EndsWith(name, ".apk")) {
+ which_heap = HEAP_APK;
+ is_swappable = true;
+ } else if (base::EndsWith(name, ".ttf")) {
+ which_heap = HEAP_TTF;
+ is_swappable = true;
+ } else if ((base::EndsWith(name, ".odex")) ||
+ (namesz > 4 && strstr(name.c_str(), ".dex") != nullptr)) {
+ which_heap = HEAP_DEX;
+ sub_heap = HEAP_DEX_APP_DEX;
+ is_swappable = true;
+ } else if (base::EndsWith(name, ".vdex")) {
+ which_heap = HEAP_DEX;
+ // Handle system@framework@boot and system/framework/boot
+ if ((strstr(name.c_str(), "@boot") != nullptr) ||
+ (strstr(name.c_str(), "/boot"))) {
+ sub_heap = HEAP_DEX_BOOT_VDEX;
+ } else {
+ sub_heap = HEAP_DEX_APP_VDEX;
}
- name = line + name_pos;
- nameLen = strlen(name);
- // Trim the end of the line if it is " (deleted)".
- const char* deleted_str = " (deleted)";
- if (nameLen > (int)strlen(deleted_str) &&
- strcmp(name+nameLen-strlen(deleted_str), deleted_str) == 0) {
- nameLen -= strlen(deleted_str);
- name[nameLen] = '\0';
+ is_swappable = true;
+ } else if (base::EndsWith(name, ".oat")) {
+ which_heap = HEAP_OAT;
+ is_swappable = true;
+ } else if (base::EndsWith(name, ".art")) {
+ which_heap = HEAP_ART;
+ // Handle system@framework@boot* and system/framework/boot*
+ if ((strstr(name.c_str(), "@boot") != nullptr) ||
+ (strstr(name.c_str(), "/boot"))) {
+ sub_heap = HEAP_DEX_BOOT_VDEX;
+ } else {
+ sub_heap = HEAP_DEX_APP_VDEX;
}
- if ((strstr(name, "[heap]") == name)) {
- whichHeap = HEAP_NATIVE;
- } else if (strncmp(name, "[anon:libc_malloc]", 18) == 0) {
- whichHeap = HEAP_NATIVE;
- } else if (strncmp(name, "[stack", 6) == 0) {
- whichHeap = HEAP_STACK;
- } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
- whichHeap = HEAP_SO;
- is_swappable = true;
- } else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) {
- whichHeap = HEAP_JAR;
- is_swappable = true;
- } else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) {
- whichHeap = HEAP_APK;
- is_swappable = true;
- } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) {
- whichHeap = HEAP_TTF;
- is_swappable = true;
- } else if ((nameLen > 4 && strstr(name, ".dex") != NULL) ||
- (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) {
- whichHeap = HEAP_DEX;
- subHeap = HEAP_DEX_APP_DEX;
- is_swappable = true;
- } else if (nameLen > 5 && strcmp(name+nameLen-5, ".vdex") == 0) {
- whichHeap = HEAP_DEX;
- // Handle system@framework@boot* and system/framework/boot*
- if (strstr(name, "@boot") != NULL || strstr(name, "/boot") != NULL) {
- subHeap = HEAP_DEX_BOOT_VDEX;
- } else {
- subHeap = HEAP_DEX_APP_VDEX;
- }
- is_swappable = true;
- } else if (nameLen > 4 && strcmp(name+nameLen-4, ".oat") == 0) {
- whichHeap = HEAP_OAT;
- is_swappable = true;
- } else if (nameLen > 4 && strcmp(name+nameLen-4, ".art") == 0) {
- whichHeap = HEAP_ART;
- // Handle system@framework@boot* and system/framework/boot*
- if (strstr(name, "@boot") != NULL || strstr(name, "/boot") != NULL) {
- subHeap = HEAP_ART_BOOT;
+ is_swappable = true;
+ } else if (base::StartsWith(name, "/dev/")) {
+ which_heap = HEAP_UNKNOWN_DEV;
+ if (base::StartsWith(name, "/dev/kgsl-3d0")) {
+ which_heap = HEAP_GL_DEV;
+ } else if (base::StartsWith(name, "/dev/ashmem/CursorWindow")) {
+ which_heap = HEAP_CURSOR;
+ } else if (base::StartsWith(name, "/dev/ashmem")) {
+ which_heap = HEAP_ASHMEM;
+ }
+ } else if (base::StartsWith(name, "[anon:")) {
+ which_heap = HEAP_UNKNOWN;
+ if (base::StartsWith(name, "[anon:dalvik-")) {
+ which_heap = HEAP_DALVIK_OTHER;
+ if (base::StartsWith(name, "[anon:dalvik-LinearAlloc")) {
+ sub_heap = HEAP_DALVIK_OTHER_LINEARALLOC;
+ } else if (base::StartsWith(name, "[anon:dalvik-alloc space") ||
+ base::StartsWith(name, "[anon:dalvik-main space")) {
+ // This is the regular Dalvik heap.
+ which_heap = HEAP_DALVIK;
+ sub_heap = HEAP_DALVIK_NORMAL;
+ } else if (base::StartsWith(name,
+ "[anon:dalvik-large object space") ||
+ base::StartsWith(
+ name, "[anon:dalvik-free list large object space")) {
+ which_heap = HEAP_DALVIK;
+ sub_heap = HEAP_DALVIK_LARGE;
+ } else if (base::StartsWith(name, "[anon:dalvik-non moving space")) {
+ which_heap = HEAP_DALVIK;
+ sub_heap = HEAP_DALVIK_NON_MOVING;
+ } else if (base::StartsWith(name, "[anon:dalvik-zygote space")) {
+ which_heap = HEAP_DALVIK;
+ sub_heap = HEAP_DALVIK_ZYGOTE;
+ } else if (base::StartsWith(name, "[anon:dalvik-indirect ref")) {
+ sub_heap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE;
+ } else if (base::StartsWith(name, "[anon:dalvik-jit-code-cache") ||
+ base::StartsWith(name, "[anon:dalvik-data-code-cache")) {
+ sub_heap = HEAP_DALVIK_OTHER_CODE_CACHE;
+ } else if (base::StartsWith(name, "[anon:dalvik-CompilerMetadata")) {
+ sub_heap = HEAP_DALVIK_OTHER_COMPILER_METADATA;
} else {
- subHeap = HEAP_ART_APP;
- }
- is_swappable = true;
- } else if (strncmp(name, "/dev/", 5) == 0) {
- whichHeap = HEAP_UNKNOWN_DEV;
- if (strncmp(name, "/dev/kgsl-3d0", 13) == 0) {
- whichHeap = HEAP_GL_DEV;
- } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) {
- whichHeap = HEAP_CURSOR;
- } else if (strncmp(name, "/dev/ashmem", 11)) {
- whichHeap = HEAP_ASHMEM;
- }
- } else if (strncmp(name, "[anon:", 6) == 0) {
- whichHeap = HEAP_UNKNOWN;
- if (strncmp(name, "[anon:dalvik-", 13) == 0) {
- whichHeap = HEAP_DALVIK_OTHER;
- if (strstr(name, "[anon:dalvik-LinearAlloc") == name) {
- subHeap = HEAP_DALVIK_OTHER_LINEARALLOC;
- } else if ((strstr(name, "[anon:dalvik-alloc space") == name) ||
- (strstr(name, "[anon:dalvik-main space") == name)) {
- // This is the regular Dalvik heap.
- whichHeap = HEAP_DALVIK;
- subHeap = HEAP_DALVIK_NORMAL;
- } else if (strstr(name, "[anon:dalvik-large object space") == name ||
- strstr(name, "[anon:dalvik-free list large object space")
- == name) {
- whichHeap = HEAP_DALVIK;
- subHeap = HEAP_DALVIK_LARGE;
- } else if (strstr(name, "[anon:dalvik-non moving space") == name) {
- whichHeap = HEAP_DALVIK;
- subHeap = HEAP_DALVIK_NON_MOVING;
- } else if (strstr(name, "[anon:dalvik-zygote space") == name) {
- whichHeap = HEAP_DALVIK;
- subHeap = HEAP_DALVIK_ZYGOTE;
- } else if (strstr(name, "[anon:dalvik-indirect ref") == name) {
- subHeap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE;
- } else if (strstr(name, "[anon:dalvik-jit-code-cache") == name ||
- strstr(name, "[anon:dalvik-data-code-cache") == name) {
- subHeap = HEAP_DALVIK_OTHER_CODE_CACHE;
- } else if (strstr(name, "[anon:dalvik-CompilerMetadata") == name) {
- subHeap = HEAP_DALVIK_OTHER_COMPILER_METADATA;
- } else {
- subHeap = HEAP_DALVIK_OTHER_ACCOUNTING; // Default to accounting.
- }
+ sub_heap = HEAP_DALVIK_OTHER_ACCOUNTING; // Default to accounting.
}
- } else if (nameLen > 0) {
- whichHeap = HEAP_UNKNOWN_MAP;
- } else if (start == prevEnd && prevHeap == HEAP_SO) {
- // bss section of a shared library.
- whichHeap = HEAP_SO;
}
+ } else if (namesz > 0) {
+ which_heap = HEAP_UNKNOWN_MAP;
+ } else if (vma.start == prev_end && prev_heap == HEAP_SO) {
+ // bss section of a shared library
+ which_heap = HEAP_SO;
}
- //ALOGI("native=%d dalvik=%d sqlite=%d: %s\n", isNativeHeap, isDalvikHeap,
- // isSqliteHeap, line);
+ prev_end = vma.end;
+ prev_heap = which_heap;
- shared_clean = 0;
- shared_dirty = 0;
- private_clean = 0;
- private_dirty = 0;
- swapped_out = 0;
- swapped_out_pss = 0;
-
- while (true) {
- if (fgets(line, 1024, fp) == 0) {
- done = true;
- break;
- }
-
- if (line[0] == 'S' && sscanf(line, "Size: %d kB", &temp) == 1) {
- /* size = temp; */
- } else if (line[0] == 'R' && sscanf(line, "Rss: %d kB", &temp) == 1) {
- rss = temp;
- } else if (line[0] == 'P' && sscanf(line, "Pss: %d kB", &temp) == 1) {
- pss = temp;
- } else if (line[0] == 'S' && sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
- shared_clean = temp;
- } else if (line[0] == 'S' && sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
- shared_dirty = temp;
- } else if (line[0] == 'P' && sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
- private_clean = temp;
- } else if (line[0] == 'P' && sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
- private_dirty = temp;
- } else if (line[0] == 'R' && sscanf(line, "Referenced: %d kB", &temp) == 1) {
- /* referenced = temp; */
- } else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
- swapped_out = temp;
- } else if (line[0] == 'S' && sscanf(line, "SwapPss: %d kB", &temp) == 1) {
- *foundSwapPss = true;
- swapped_out_pss = temp;
- } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
- // looks like a new mapping
- // example: "10000000-10001000 ---p 10000000 00:00 0"
- break;
- }
+ const meminfo::MemUsage& usage = vma.usage;
+ if (usage.swap_pss > 0 && *foundSwapPss != true) {
+ *foundSwapPss = true;
}
- if (!skip) {
- if (is_swappable && (pss > 0)) {
- sharing_proportion = 0.0;
- if ((shared_clean > 0) || (shared_dirty > 0)) {
- sharing_proportion = (pss - private_clean
- - private_dirty)/(shared_clean+shared_dirty);
- }
- swappable_pss = (sharing_proportion*shared_clean) + private_clean;
- } else
- swappable_pss = 0;
-
- stats[whichHeap].pss += pss;
- stats[whichHeap].swappablePss += swappable_pss;
- stats[whichHeap].rss += rss;
- stats[whichHeap].privateDirty += private_dirty;
- stats[whichHeap].sharedDirty += shared_dirty;
- stats[whichHeap].privateClean += private_clean;
- stats[whichHeap].sharedClean += shared_clean;
- stats[whichHeap].swappedOut += swapped_out;
- stats[whichHeap].swappedOutPss += swapped_out_pss;
- if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER ||
- whichHeap == HEAP_DEX || whichHeap == HEAP_ART) {
- stats[subHeap].pss += pss;
- stats[subHeap].swappablePss += swappable_pss;
- stats[subHeap].rss += rss;
- stats[subHeap].privateDirty += private_dirty;
- stats[subHeap].sharedDirty += shared_dirty;
- stats[subHeap].privateClean += private_clean;
- stats[subHeap].sharedClean += shared_clean;
- stats[subHeap].swappedOut += swapped_out;
- stats[subHeap].swappedOutPss += swapped_out_pss;
+ uint64_t swapable_pss = 0;
+ if (is_swappable && (usage.pss > 0)) {
+ float sharing_proportion = 0.0;
+ if ((usage.shared_clean > 0) || (usage.shared_dirty > 0)) {
+ sharing_proportion = (usage.pss - usage.uss) / (usage.shared_clean + usage.shared_dirty);
}
+ swapable_pss = (sharing_proportion * usage.shared_clean) + usage.private_clean;
}
- }
-}
-static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
-{
- *foundSwapPss = false;
-
- std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
- UniqueFile fp = MakeUniqueFile(smaps_path.c_str(), "re");
- if (fp == nullptr) return;
+ stats[which_heap].pss += usage.pss;
+ stats[which_heap].swappablePss += swapable_pss;
+ stats[which_heap].rss += usage.rss;
+ stats[which_heap].privateDirty += usage.private_dirty;
+ stats[which_heap].sharedDirty += usage.shared_dirty;
+ stats[which_heap].privateClean += usage.private_clean;
+ stats[which_heap].sharedClean += usage.shared_clean;
+ stats[which_heap].swappedOut += usage.swap;
+ stats[which_heap].swappedOutPss += usage.swap_pss;
+ if (which_heap == HEAP_DALVIK || which_heap == HEAP_DALVIK_OTHER ||
+ which_heap == HEAP_DEX || which_heap == HEAP_ART) {
+ stats[sub_heap].pss += usage.pss;
+ stats[sub_heap].swappablePss += swapable_pss;
+ stats[sub_heap].rss += usage.rss;
+ stats[sub_heap].privateDirty += usage.private_dirty;
+ stats[sub_heap].sharedDirty += usage.shared_dirty;
+ stats[sub_heap].privateClean += usage.private_clean;
+ stats[sub_heap].sharedClean += usage.shared_clean;
+ stats[sub_heap].swappedOut += usage.swap;
+ stats[sub_heap].swappedOutPss += usage.swap_pss;
+ }
+ };
- read_mapinfo(fp.get(), stats, foundSwapPss);
+ meminfo::ForEachVmaFromFile(smaps_path, vma_scan);
}
static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 897427fd8787..0453195e6a1d 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -126,7 +126,12 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
jint windowType, jint ownerUid) {
ScopedUtfChars name(env, nameStr);
- sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
+ sp<SurfaceComposerClient> client;
+ if (sessionObj != NULL) {
+ client = android_view_SurfaceSession_getClient(env, sessionObj);
+ } else {
+ client = SurfaceComposerClient::getDefault();
+ }
SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
sp<SurfaceControl> surface;
status_t err = client->createSurfaceChecked(
@@ -277,6 +282,21 @@ static void nativeSetPosition(JNIEnv* env, jclass clazz, jlong transactionObj,
transaction->setPosition(ctrl, x, y);
}
+static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+ jobject sourceObj, jobject dstObj, jlong orientation) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+
+ Rect source, dst;
+ if (sourceObj != NULL) {
+ source = rectFromObj(env, sourceObj);
+ }
+ if (dstObj != NULL) {
+ dst = rectFromObj(env, dstObj);
+ }
+ transaction->setGeometry(ctrl, source, dst, orientation);
+}
+
static void nativeSetGeometryAppliesWithResize(JNIEnv* env, jclass clazz,
jlong transactionObj,
jlong nativeObject) {
@@ -868,13 +888,13 @@ static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionO
static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject,
- jobject newParentObject) {
+ jlong newParentObject) {
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- sp<IBinder> parentHandle = ibinderForJavaObject(env, newParentObject);
+ auto newParent = reinterpret_cast<SurfaceControl *>(newParentObject);
{
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- transaction->reparent(ctrl, parentHandle);
+ transaction->reparent(ctrl, newParent != NULL ? newParent->getHandle() : NULL);
}
}
@@ -1063,7 +1083,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeDeferTransactionUntilSurface },
{"nativeReparentChildren", "(JJLandroid/os/IBinder;)V",
(void*)nativeReparentChildren } ,
- {"nativeReparent", "(JJLandroid/os/IBinder;)V",
+ {"nativeReparent", "(JJJ)V",
(void*)nativeReparent },
{"nativeSeverChildren", "(JJ)V",
(void*)nativeSeverChildren } ,
@@ -1087,6 +1107,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
{"nativeGetDisplayedContentSample",
"(Landroid/os/IBinder;JJ)Landroid/hardware/display/DisplayedContentSample;",
(void*)nativeGetDisplayedContentSample },
+ {"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V",
+ (void*)nativeSetGeometry }
};
int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index e81b6276ef5f..2e7184b4f0fb 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -29,13 +29,17 @@
#include <sys/mount.h>
#include <linux/fs.h>
+#include <array>
+#include <atomic>
#include <functional>
#include <list>
#include <optional>
#include <sstream>
#include <string>
+#include <string_view>
#include <android/fdsan.h>
+#include <arpa/inet.h>
#include <fcntl.h>
#include <grp.h>
#include <inttypes.h>
@@ -46,9 +50,11 @@
#include <stdlib.h>
#include <sys/capability.h>
#include <sys/cdefs.h>
+#include <sys/eventfd.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -56,10 +62,11 @@
#include <sys/wait.h>
#include <unistd.h>
-#include "android-base/logging.h"
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <cutils/fs.h>
#include <cutils/multiuser.h>
#include <private/android_filesystem_config.h>
@@ -81,6 +88,9 @@
namespace {
+// TODO (chriswailes): Add a function to initialize native Zygote data.
+// TODO (chriswailes): Fix mixed indentation style (2 and 4 spaces).
+
using namespace std::placeholders;
using android::String8;
@@ -92,6 +102,9 @@ using android::base::GetBoolProperty;
#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
append(StringPrintf(__VA_ARGS__))
+// This type is duplicated in fd_utils.h
+typedef const std::function<void(std::string)>& fail_fn_t;
+
static pid_t gSystemServerPid = 0;
static const char kIsolatedStorage[] = "persist.sys.isolated_storage";
@@ -103,6 +116,152 @@ static jmethodID gCallPostForkChildHooks;
static bool g_is_security_enforced = true;
+/**
+ * The maximum number of characters (not including a null terminator) that a
+ * process name may contain.
+ */
+static constexpr size_t MAX_NAME_LENGTH = 15;
+
+/**
+ * The prefix string for environmental variables storing socket FDs created by
+ * init.
+ */
+
+static constexpr std::string_view ANDROID_SOCKET_PREFIX("ANDROID_SOCKET_");
+
+/**
+ * The file descriptor for the Zygote socket opened by init.
+ */
+
+static int gZygoteSocketFD = -1;
+
+/**
+ * The file descriptor for the Blastula pool socket opened by init.
+ */
+
+static int gBlastulaPoolSocketFD = -1;
+
+/**
+ * The number of Blastulas currently in this Zygote's pool.
+ */
+static std::atomic_uint32_t gBlastulaPoolCount = 0;
+
+/**
+ * Event file descriptor used to communicate reaped blastulas to the
+ * ZygoteServer.
+ */
+static int gBlastulaPoolEventFD = -1;
+
+/**
+ * The maximum value that the gBlastulaPoolMax variable may take. This value
+ * is a mirror of Zygote.BLASTULA_POOL_MAX_LIMIT
+ */
+static constexpr int BLASTULA_POOL_MAX_LIMIT = 10;
+
+/**
+ * A helper class containing accounting information for Blastulas.
+ */
+class BlastulaTableEntry {
+ public:
+ struct EntryStorage {
+ int32_t pid;
+ int32_t read_pipe_fd;
+
+ bool operator!=(const EntryStorage& other) {
+ return pid != other.pid || read_pipe_fd != other.read_pipe_fd;
+ }
+ };
+
+ private:
+ static constexpr EntryStorage INVALID_ENTRY_VALUE = {-1, -1};
+
+ std::atomic<EntryStorage> mStorage;
+ static_assert(decltype(mStorage)::is_always_lock_free);
+
+ public:
+ constexpr BlastulaTableEntry() : mStorage(INVALID_ENTRY_VALUE) {}
+
+ /**
+ * If the provided PID matches the one stored in this entry, the entry will
+ * be invalidated and the associated file descriptor will be closed. If the
+ * PIDs don't match nothing will happen.
+ *
+ * @param pid The ID of the process who's entry we want to clear.
+ * @return True if the entry was cleared; false otherwise
+ */
+ bool ClearForPID(int32_t pid) {
+ EntryStorage storage = mStorage.load();
+
+ if (storage.pid == pid) {
+ /*
+ * There are three possible outcomes from this compare-and-exchange:
+ * 1) It succeeds, in which case we close the FD
+ * 2) It fails and the new value is INVALID_ENTRY_VALUE, in which case
+ * the entry has already been cleared.
+ * 3) It fails and the new value isn't INVALID_ENTRY_VALUE, in which
+ * case the entry has already been cleared and re-used.
+ *
+ * In all three cases the goal of the caller has been met and we can
+ * return true.
+ */
+ if (mStorage.compare_exchange_strong(storage, INVALID_ENTRY_VALUE)) {
+ close(storage.read_pipe_fd);
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @return A copy of the data stored in this entry.
+ */
+ std::optional<EntryStorage> GetValues() {
+ EntryStorage storage = mStorage.load();
+
+ if (storage != INVALID_ENTRY_VALUE) {
+ return storage;
+ } else {
+ return std::nullopt;
+ }
+ }
+
+ /**
+ * Sets the entry to the given values if it is currently invalid.
+ *
+ * @param pid The process ID for the new entry.
+ * @param read_pipe_fd The read end of the blastula control pipe for this
+ * process.
+ * @return True if the entry was set; false otherwise.
+ */
+ bool SetIfInvalid(int32_t pid, int32_t read_pipe_fd) {
+ EntryStorage new_value_storage;
+
+ new_value_storage.pid = pid;
+ new_value_storage.read_pipe_fd = read_pipe_fd;
+
+ EntryStorage expected = INVALID_ENTRY_VALUE;
+
+ return mStorage.compare_exchange_strong(expected, new_value_storage);
+ }
+};
+
+/**
+ * A table containing information about the Blastulas currently in the pool.
+ *
+ * Multiple threads may be attempting to modify the table, either from the
+ * signal handler or from the ZygoteServer poll loop. Atomic loads/stores in
+ * the BlastulaTableEntry class prevent data races during these concurrent
+ * operations.
+ */
+static std::array<BlastulaTableEntry, BLASTULA_POOL_MAX_LIMIT> gBlastulaTable;
+
+/**
+ * The list of open zygote file descriptors.
+ */
+static FileDescriptorTable* gOpenFdTable = nullptr;
+
// Must match values in com.android.internal.os.Zygote.
enum MountExternalKind {
MOUNT_EXTERNAL_NONE = 0,
@@ -119,6 +278,9 @@ enum RuntimeFlags : uint32_t {
DEBUG_ENABLE_JDWP = 1,
};
+// Forward declaration so we don't have to move the signal handler.
+static bool RemoveBlastulaTableEntry(pid_t blastula_pid);
+
static void RuntimeAbort(JNIEnv* env, int line, const char* msg) {
std::ostringstream oss;
oss << __FILE__ << ":" << line << ": " << msg;
@@ -129,6 +291,7 @@ static void RuntimeAbort(JNIEnv* env, int line, const char* msg) {
static void SigChldHandler(int /*signal_number*/) {
pid_t pid;
int status;
+ int64_t blastulas_removed = 0;
// It's necessary to save and restore the errno during this function.
// Since errno is stored per thread, changing it here modifies the errno
@@ -162,6 +325,11 @@ static void SigChldHandler(int /*signal_number*/) {
ALOGE("Exit zygote because system server (%d) has terminated", pid);
kill(getpid(), SIGKILL);
}
+
+ // Check to see if the PID is in the blastula pool and remove it if it is.
+ if (RemoveBlastulaTableEntry(pid)) {
+ ++blastulas_removed;
+ }
}
// Note that we shouldn't consider ECHILD an error because
@@ -170,6 +338,15 @@ static void SigChldHandler(int /*signal_number*/) {
ALOGW("Zygote SIGCHLD error in waitpid: %s", strerror(errno));
}
+ if (blastulas_removed > 0) {
+ if (write(gBlastulaPoolEventFD, &blastulas_removed, sizeof(blastulas_removed)) == -1) {
+ // If this write fails something went terribly wrong. We will now kill
+ // the zygote and let the system bring it back up.
+ ALOGE("Zygote failed to write to blastula pool event FD: %s", strerror(errno));
+ kill(getpid(), SIGKILL);
+ }
+ }
+
errno = saved_errno;
}
@@ -194,13 +371,13 @@ static void SetSignalHandlers() {
struct sigaction sig_chld = {};
sig_chld.sa_handler = SigChldHandler;
- if (sigaction(SIGCHLD, &sig_chld, NULL) < 0) {
+ if (sigaction(SIGCHLD, &sig_chld, nullptr) < 0) {
ALOGW("Error setting SIGCHLD handler: %s", strerror(errno));
}
struct sigaction sig_hup = {};
sig_hup.sa_handler = SIG_IGN;
- if (sigaction(SIGHUP, &sig_hup, NULL) < 0) {
+ if (sigaction(SIGHUP, &sig_hup, nullptr) < 0) {
ALOGW("Error setting SIGHUP handler: %s", strerror(errno));
}
}
@@ -211,64 +388,57 @@ static void UnsetChldSignalHandler() {
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
- if (sigaction(SIGCHLD, &sa, NULL) < 0) {
+ if (sigaction(SIGCHLD, &sa, nullptr) < 0) {
ALOGW("Error unsetting SIGCHLD handler: %s", strerror(errno));
}
}
// Calls POSIX setgroups() using the int[] object as an argument.
-// A NULL argument is tolerated.
-static bool SetGids(JNIEnv* env, jintArray javaGids, std::string* error_msg) {
- if (javaGids == NULL) {
- return true;
+// A nullptr argument is tolerated.
+static void SetGids(JNIEnv* env, jintArray managed_gids, fail_fn_t fail_fn) {
+ if (managed_gids == nullptr) {
+ return;
}
- ScopedIntArrayRO gids(env, javaGids);
- if (gids.get() == NULL) {
- *error_msg = CREATE_ERROR("Getting gids int array failed");
- return false;
- }
- int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0]));
- if (rc == -1) {
- *error_msg = CREATE_ERROR("setgroups failed: %s, gids.size=%zu", strerror(errno), gids.size());
- return false;
+ ScopedIntArrayRO gids(env, managed_gids);
+ if (gids.get() == nullptr) {
+ fail_fn(CREATE_ERROR("Getting gids int array failed"));
}
- return true;
+ if (setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0])) == -1) {
+ fail_fn(CREATE_ERROR("setgroups failed: %s, gids.size=%zu", strerror(errno), gids.size()));
+ }
}
// Sets the resource limits via setrlimit(2) for the values in the
// two-dimensional array of integers that's passed in. The second dimension
-// contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
+// contains a tuple of length 3: (resource, rlim_cur, rlim_max). nullptr is
// treated as an empty array.
-static bool SetRLimits(JNIEnv* env, jobjectArray javaRlimits, std::string* error_msg) {
- if (javaRlimits == NULL) {
- return true;
+static void SetRLimits(JNIEnv* env, jobjectArray managed_rlimits, fail_fn_t fail_fn) {
+ if (managed_rlimits == nullptr) {
+ return;
}
rlimit rlim;
memset(&rlim, 0, sizeof(rlim));
- for (int i = 0; i < env->GetArrayLength(javaRlimits); ++i) {
- ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i));
- ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get()));
- if (javaRlimit.size() != 3) {
- *error_msg = CREATE_ERROR("rlimits array must have a second dimension of size 3");
- return false;
+ for (int i = 0; i < env->GetArrayLength(managed_rlimits); ++i) {
+ ScopedLocalRef<jobject>
+ managed_rlimit_object(env, env->GetObjectArrayElement(managed_rlimits, i));
+ ScopedIntArrayRO rlimit_handle(env, reinterpret_cast<jintArray>(managed_rlimit_object.get()));
+
+ if (rlimit_handle.size() != 3) {
+ fail_fn(CREATE_ERROR("rlimits array must have a second dimension of size 3"));
}
- rlim.rlim_cur = javaRlimit[1];
- rlim.rlim_max = javaRlimit[2];
+ rlim.rlim_cur = rlimit_handle[1];
+ rlim.rlim_max = rlimit_handle[2];
- int rc = setrlimit(javaRlimit[0], &rlim);
- if (rc == -1) {
- *error_msg = CREATE_ERROR("setrlimit(%d, {%ld, %ld}) failed", javaRlimit[0], rlim.rlim_cur,
- rlim.rlim_max);
- return false;
+ if (setrlimit(rlimit_handle[0], &rlim) == -1) {
+ fail_fn(CREATE_ERROR("setrlimit(%d, {%ld, %ld}) failed",
+ rlimit_handle[0], rlim.rlim_cur, rlim.rlim_max));
}
}
-
- return true;
}
static void EnableDebugger() {
@@ -323,10 +493,7 @@ static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
// Apply system or app filter based on uid.
if (uid >= AID_APP_START) {
if (is_child_zygote) {
- // set_app_zygote_seccomp_filter();
- // TODO(b/111434506) install the filter; for now, install the app filter
- // which is more restrictive.
- set_app_seccomp_filter();
+ set_app_zygote_seccomp_filter();
} else {
set_app_seccomp_filter();
}
@@ -335,32 +502,26 @@ static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
}
}
-static bool EnableKeepCapabilities(std::string* error_msg) {
- int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
- if (rc == -1) {
- *error_msg = CREATE_ERROR("prctl(PR_SET_KEEPCAPS) failed: %s", strerror(errno));
- return false;
+static void EnableKeepCapabilities(fail_fn_t fail_fn) {
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
+ fail_fn(CREATE_ERROR("prctl(PR_SET_KEEPCAPS) failed: %s", strerror(errno)));
}
- return true;
}
-static bool DropCapabilitiesBoundingSet(std::string* error_msg) {
- for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
- int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
- if (rc == -1) {
+static void DropCapabilitiesBoundingSet(fail_fn_t fail_fn) {
+ for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {;
+ if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) {
if (errno == EINVAL) {
ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
"your kernel is compiled with file capabilities support");
} else {
- *error_msg = CREATE_ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno)));
}
}
}
- return true;
}
-static bool SetInheritable(uint64_t inheritable, std::string* error_msg) {
+static void SetInheritable(uint64_t inheritable, fail_fn_t fail_fn) {
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -368,23 +529,19 @@ static bool SetInheritable(uint64_t inheritable, std::string* error_msg) {
__user_cap_data_struct capdata[2];
if (capget(&capheader, &capdata[0]) == -1) {
- *error_msg = CREATE_ERROR("capget failed: %s", strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("capget failed: %s", strerror(errno)));
}
capdata[0].inheritable = inheritable;
capdata[1].inheritable = inheritable >> 32;
if (capset(&capheader, &capdata[0]) == -1) {
- *error_msg = CREATE_ERROR("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno)));
}
-
- return true;
}
-static bool SetCapabilities(uint64_t permitted, uint64_t effective, uint64_t inheritable,
- std::string* error_msg) {
+static void SetCapabilities(uint64_t permitted, uint64_t effective, uint64_t inheritable,
+ fail_fn_t fail_fn) {
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -400,27 +557,23 @@ static bool SetCapabilities(uint64_t permitted, uint64_t effective, uint64_t inh
capdata[1].inheritable = inheritable >> 32;
if (capset(&capheader, &capdata[0]) == -1) {
- *error_msg = CREATE_ERROR("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") "
- "failed: %s", permitted, effective, inheritable, strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") "
+ "failed: %s", permitted, effective, inheritable, strerror(errno)));
}
- return true;
}
-static bool SetSchedulerPolicy(std::string* error_msg) {
+static void SetSchedulerPolicy(fail_fn_t fail_fn) {
errno = -set_sched_policy(0, SP_DEFAULT);
if (errno != 0) {
- *error_msg = CREATE_ERROR("set_sched_policy(0, SP_DEFAULT) failed: %s", strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("set_sched_policy(0, SP_DEFAULT) failed: %s", strerror(errno)));
}
- return true;
}
static int UnmountTree(const char* path) {
size_t path_len = strlen(path);
FILE* fp = setmntent("/proc/mounts", "r");
- if (fp == NULL) {
+ if (fp == nullptr) {
ALOGE("Error opening /proc/mounts: %s", strerror(errno));
return -errno;
}
@@ -429,7 +582,7 @@ static int UnmountTree(const char* path) {
// reverse order to give us the best chance of success.
std::list<std::string> toUnmount;
mntent* mentry;
- while ((mentry = getmntent(fp)) != NULL) {
+ while ((mentry = getmntent(fp)) != nullptr) {
if (strncmp(mentry->mnt_dir, path, path_len) == 0) {
toUnmount.push_front(std::string(mentry->mnt_dir));
}
@@ -444,56 +597,55 @@ static int UnmountTree(const char* path) {
return 0;
}
-static bool createPkgSandbox(uid_t uid, const std::string& package_name, std::string* error_msg) {
+static void CreatePkgSandbox(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) {
// Create /mnt/user/0/package/<package-name>
userid_t user_id = multiuser_get_user_id(uid);
std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0751, AID_ROOT, AID_ROOT) != 0) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
- return false;
+ fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str()));
}
+
StringAppendF(&pkg_sandbox_dir, "/package");
if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
- return false;
+ fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str()));
}
+
StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str());
if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0755, uid, uid) != 0) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
- return false;
+ fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str()));
}
- return true;
}
-static bool bindMount(const std::string& sourceDir, const std::string& targetDir,
- std::string* error_msg) {
- if (TEMP_FAILURE_RETRY(mount(sourceDir.c_str(), targetDir.c_str(),
- nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
- sourceDir.c_str(), targetDir.c_str(), strerror(errno));
- return false;
+static void BindMount(const std::string& sourceDir, const std::string& targetDir,
+ fail_fn_t fail_fn) {
+ if (TEMP_FAILURE_RETRY(mount(sourceDir.c_str(), targetDir.c_str(), nullptr,
+ MS_BIND | MS_REC, nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to %s: %s",
+ sourceDir.c_str(), targetDir.c_str(), strerror(errno)));
}
- if (TEMP_FAILURE_RETRY(mount(nullptr, targetDir.c_str(),
- nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
- *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", targetDir.c_str());
- return false;
+
+ if (TEMP_FAILURE_RETRY(mount(nullptr, targetDir.c_str(), nullptr,
+ MS_SLAVE | MS_REC, nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to set MS_SLAVE for %s", targetDir.c_str()));
}
- return true;
}
-static bool mountPkgSpecificDir(const std::string& mntSourceRoot,
- const std::string& mntTargetRoot, const std::string& packageName,
- const char* dirName, std::string* error_msg) {
+static void MountPkgSpecificDir(const std::string& mntSourceRoot,
+ const std::string& mntTargetRoot,
+ const std::string& packageName,
+ const char* dirName,
+ fail_fn_t fail_fn) {
std::string mntSourceDir = StringPrintf("%s/Android/%s/%s",
mntSourceRoot.c_str(), dirName, packageName.c_str());
std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
mntTargetRoot.c_str(), dirName, packageName.c_str());
- return bindMount(mntSourceDir, mntTargetDir, error_msg);
+
+ BindMount(mntSourceDir, mntTargetDir, fail_fn);
}
-static bool preparePkgSpecificDirs(const std::vector<std::string>& packageNames,
- const std::vector<std::string>& volumeLabels, bool mountAllObbs,
- userid_t userId, std::string* error_msg) {
+static void PreparePkgSpecificDirs(const std::vector<std::string>& packageNames,
+ const std::vector<std::string>& volumeLabels,
+ bool mountAllObbs, userid_t userId, fail_fn_t fail_fn) {
for (auto& label : volumeLabels) {
std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str());
std::string mntTarget = StringPrintf("/storage/%s", label.c_str());
@@ -501,28 +653,29 @@ static bool preparePkgSpecificDirs(const std::vector<std::string>& packageNames,
StringAppendF(&mntSource, "/%d", userId);
StringAppendF(&mntTarget, "/%d", userId);
}
+
for (auto& package : packageNames) {
- mountPkgSpecificDir(mntSource, mntTarget, package, "data", error_msg);
- mountPkgSpecificDir(mntSource, mntTarget, package, "media", error_msg);
+ MountPkgSpecificDir(mntSource, mntTarget, package, "data", fail_fn);
+ MountPkgSpecificDir(mntSource, mntTarget, package, "media", fail_fn);
if (!mountAllObbs) {
- mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+ MountPkgSpecificDir(mntSource, mntTarget, package, "obb", fail_fn);
}
}
+
if (mountAllObbs) {
StringAppendF(&mntSource, "/Android/obb");
StringAppendF(&mntTarget, "/Android/obb");
- bindMount(mntSource, mntTarget, error_msg);
+ BindMount(mntSource, mntTarget, fail_fn);
}
}
- return true;
}
// Create a private mount namespace and bind mount appropriate emulated
// storage for the given user.
-static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
- bool force_mount_namespace, std::string* error_msg, const std::string& package_name,
+static void MountEmulatedStorage(uid_t uid, jint mount_mode,
+ bool force_mount_namespace, const std::string& package_name,
const std::vector<std::string>& packages_for_uid,
- const std::vector<std::string>& visible_vol_ids) {
+ const std::vector<std::string>& visible_vol_ids, fail_fn_t fail_fn) {
// See storage config details at http://source.android.com/tech/storage/
String8 storageSource;
@@ -534,18 +687,17 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
storageSource = "/mnt/runtime/write";
} else if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) {
// Sane default of no storage visible
- return true;
+ return;
}
// Create a second private mount namespace for our process
if (unshare(CLONE_NEWNS) == -1) {
- *error_msg = CREATE_ERROR("Failed to unshare(): %s", strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("Failed to unshare(): %s", strerror(errno)));
}
// Handle force_mount_namespace with MOUNT_EXTERNAL_NONE.
if (mount_mode == MOUNT_EXTERNAL_NONE) {
- return true;
+ return;
}
if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, false))) {
@@ -553,66 +705,64 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
storageSource = (mount_mode == MOUNT_EXTERNAL_FULL)
? "/mnt/runtime/full" : "/mnt/runtime/write";
if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
- NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
- storageSource.string(),
- strerror(errno));
- return false;
+ NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
+ storageSource.string(),
+ strerror(errno)));
}
// Mount user-specific symlink helper into place
userid_t user_id = multiuser_get_user_id(uid);
const String8 userSource(String8::format("/mnt/user/%d", user_id));
if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
- return false;
+ fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s (%s)",
+ userSource.string(), strerror(errno)));
}
- if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
- NULL, MS_BIND, NULL)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
- userSource.string(),
- strerror(errno));
- return false;
+
+ if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self", nullptr, MS_BIND,
+ nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s",
+ userSource.string(),
+ strerror(errno)));
}
} else {
if (package_name.empty()) {
- return true;
+ return;
}
+
userid_t user_id = multiuser_get_user_id(uid);
- std::string pkgSandboxDir = StringPrintf("/mnt/user/%d/package/%s",
- user_id, package_name.c_str());
+ std::string pkgSandboxDir =
+ StringPrintf("/mnt/user/%d/package/%s", user_id, package_name.c_str());
struct stat sb;
bool sandboxAlreadyCreated = true;
if (TEMP_FAILURE_RETRY(lstat(pkgSandboxDir.c_str(), &sb)) == -1) {
if (errno == ENOENT) {
ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str());
sandboxAlreadyCreated = false;
- if (!createPkgSandbox(uid, package_name, error_msg)) {
- return false;
- }
+ CreatePkgSandbox(uid, package_name, fail_fn);
} else {
- ALOGE("Failed to lstat %s", pkgSandboxDir.c_str());
- return false;
+ fail_fn(CREATE_ERROR("Failed to lstat %s: %s",
+ pkgSandboxDir.c_str(), strerror(errno)));
}
}
+
if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
- nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
- pkgSandboxDir.c_str(), strerror(errno));
- return false;
+ nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
+ pkgSandboxDir.c_str(), strerror(errno)));
}
+
if (access("/storage/obb_mount", F_OK) == 0) {
if (mount_mode != MOUNT_EXTERNAL_INSTALLER) {
remove("/storage/obb_mount");
}
} else {
if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
- int fd = TEMP_FAILURE_RETRY(open("/storage/obb_mount",
- O_RDWR | O_CREAT, 0660));
+ int fd =
+ TEMP_FAILURE_RETRY(open("/storage/obb_mount", O_RDWR | O_CREAT, 0660));
if (fd == -1) {
- *error_msg = CREATE_ERROR("Couldn't create /storage/obb_mount: %s",
- strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("Couldn't create /storage/obb_mount: %s",
+ strerror(errno)));
}
close(fd);
}
@@ -621,38 +771,32 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
// pkg specific directories. Otherwise, leave as is and bind mounts will be taken
// care of by vold later.
if (sandboxAlreadyCreated) {
- if (!preparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
- mount_mode == MOUNT_EXTERNAL_INSTALLER, user_id, error_msg)) {
- return false;
- }
+ PreparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
+ mount_mode == MOUNT_EXTERNAL_INSTALLER, user_id, fail_fn);
}
}
} else {
- if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
- NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
- storageSource.string(),
- strerror(errno));
- return false;
+ if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", nullptr,
+ MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
+ storageSource.string(),
+ strerror(errno)));
}
// Mount user-specific symlink helper into place
userid_t user_id = multiuser_get_user_id(uid);
const String8 userSource(String8::format("/mnt/user/%d", user_id));
if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
- return false;
+ fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s",
+ userSource.string()));
}
+
if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
- NULL, MS_BIND, NULL)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
- userSource.string(),
- strerror(errno));
- return false;
+ nullptr, MS_BIND, nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s",
+ userSource.string(), strerror(errno)));
}
}
-
- return true;
}
static bool NeedsNoRandomizeWorkaround() {
@@ -680,55 +824,45 @@ static bool NeedsNoRandomizeWorkaround() {
// descriptor (if any) is closed via dup2(), replacing it with a valid
// (open) descriptor to /dev/null.
-static bool DetachDescriptors(JNIEnv* env, jintArray fdsToClose, std::string* error_msg) {
- if (!fdsToClose) {
- return true;
- }
- jsize count = env->GetArrayLength(fdsToClose);
- ScopedIntArrayRO ar(env, fdsToClose);
- if (ar.get() == NULL) {
- *error_msg = "Bad fd array";
- return false;
- }
- jsize i;
- int devnull;
- for (i = 0; i < count; i++) {
- devnull = open("/dev/null", O_RDWR);
- if (devnull < 0) {
- *error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno));
- return false;
+static void DetachDescriptors(JNIEnv* env,
+ const std::vector<int>& fds_to_close,
+ fail_fn_t fail_fn) {
+
+ if (fds_to_close.size() > 0) {
+ android::base::unique_fd devnull_fd(open("/dev/null", O_RDWR));
+ if (devnull_fd == -1) {
+ fail_fn(std::string("Failed to open /dev/null: ").append(strerror(errno)));
}
- ALOGV("Switching descriptor %d to /dev/null: %s", ar[i], strerror(errno));
- if (dup2(devnull, ar[i]) < 0) {
- *error_msg = StringPrintf("Failed dup2() on descriptor %d: %s", ar[i], strerror(errno));
- return false;
+
+ for (int fd : fds_to_close) {
+ ALOGV("Switching descriptor %d to /dev/null", fd);
+ if (dup2(devnull_fd, fd) == -1) {
+ fail_fn(StringPrintf("Failed dup2() on descriptor %d: %s", fd, strerror(errno)));
+ }
}
- close(devnull);
}
- return true;
}
-void SetThreadName(const char* thread_name) {
+void SetThreadName(const std::string& thread_name) {
bool hasAt = false;
bool hasDot = false;
- const char* s = thread_name;
- while (*s) {
- if (*s == '.') {
+
+ for (const char str_el : thread_name) {
+ if (str_el == '.') {
hasDot = true;
- } else if (*s == '@') {
+ } else if (str_el == '@') {
hasAt = true;
}
- s++;
}
- const int len = s - thread_name;
- if (len < 15 || hasAt || !hasDot) {
- s = thread_name;
- } else {
- s = thread_name + len - 15;
+
+ const char* name_start_ptr = thread_name.c_str();
+ if (thread_name.length() >= MAX_NAME_LENGTH && !hasAt && hasDot) {
+ name_start_ptr += thread_name.length() - MAX_NAME_LENGTH;
}
+
// pthread_setname_np fails rather than truncating long strings.
char buf[16]; // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
- strlcpy(buf, s, sizeof(buf)-1);
+ strlcpy(buf, name_start_ptr, sizeof(buf) - 1);
errno = pthread_setname_np(pthread_self(), buf);
if (errno != 0) {
ALOGW("Unable to set the name of current thread to '%s': %s", buf, strerror(errno));
@@ -737,28 +871,16 @@ void SetThreadName(const char* thread_name) {
android::base::SetDefaultTag(buf);
}
-// The list of open zygote file descriptors.
-static FileDescriptorTable* gOpenFdTable = NULL;
-
-static bool FillFileDescriptorVector(JNIEnv* env,
- jintArray managed_fds,
- std::vector<int>* fds,
- std::string* error_msg) {
- CHECK(fds != nullptr);
- if (managed_fds != nullptr) {
- ScopedIntArrayRO ar(env, managed_fds);
- if (ar.get() == nullptr) {
- *error_msg = "Bad fd array";
- return false;
- }
- fds->reserve(ar.size());
- for (size_t i = 0; i < ar.size(); ++i) {
- fds->push_back(ar[i]);
- }
- }
- return true;
-}
-
+/**
+ * A failure function used to report fatal errors to the managed runtime. This
+ * function is often curried with the process name information and then passed
+ * to called functions.
+ *
+ * @param env Managed runtime environment
+ * @param process_name A native representation of the process name
+ * @param managed_process_name A managed representation of the process name
+ * @param msg The error message to be reported
+ */
[[noreturn]]
static void ZygoteFailure(JNIEnv* env,
const char* process_name,
@@ -779,12 +901,25 @@ static void ZygoteFailure(JNIEnv* env,
__builtin_unreachable();
}
+/**
+ * A helper method for converting managed strings to native strings. A fatal
+ * error is generated if a problem is encountered in extracting a non-null
+ * string.
+ *
+ * @param env Managed runtime environment
+ * @param process_name A native representation of the process name
+ * @param managed_process_name A managed representation of the process name
+ * @param managed_string The managed string to extract
+ *
+ * @return An empty option if the managed string is null. A optional-wrapped
+ * string otherwise.
+ */
static std::optional<std::string> ExtractJString(JNIEnv* env,
const char* process_name,
jstring managed_process_name,
jstring managed_string) {
if (managed_string == nullptr) {
- return std::optional<std::string>();
+ return std::nullopt;
} else {
ScopedUtfChars scoped_string_chars(env, managed_string);
@@ -796,16 +931,125 @@ static std::optional<std::string> ExtractJString(JNIEnv* env,
}
}
-// Utility routine to fork a zygote.
+/**
+ * A helper method for converting managed string arrays to native vectors. A
+ * fatal error is generated if a problem is encountered in extracting a non-null array.
+ *
+ * @param env Managed runtime environment
+ * @param process_name A native representation of the process name
+ * @param managed_process_name A managed representation of the process name
+ * @param managed_array The managed integer array to extract
+ *
+ * @return An empty option if the managed array is null. A optional-wrapped
+ * vector otherwise.
+ */
+static std::optional<std::vector<int>> ExtractJIntArray(JNIEnv* env,
+ const char* process_name,
+ jstring managed_process_name,
+ jintArray managed_array) {
+ if (managed_array == nullptr) {
+ return std::nullopt;
+ } else {
+ ScopedIntArrayRO managed_array_handle(env, managed_array);
+
+ if (managed_array_handle.get() != nullptr) {
+ std::vector<int> native_array;
+ native_array.reserve(managed_array_handle.size());
+
+ for (size_t array_index = 0; array_index < managed_array_handle.size(); ++array_index) {
+ native_array.push_back(managed_array_handle[array_index]);
+ }
+
+ return std::move(native_array);
+
+ } else {
+ ZygoteFailure(env, process_name, managed_process_name, "Failed to extract JIntArray.");
+ }
+ }
+}
+
+/**
+ * A helper method for converting managed string arrays to native vectors. A
+ * fatal error is generated if a problem is encountered in extracting a non-null array.
+ *
+ * @param env Managed runtime environment
+ * @param process_name A native representation of the process name
+ * @param managed_process_name A managed representation of the process name
+ * @param managed_array The managed string array to extract
+ *
+ * @return An empty option if the managed array is null. A optional-wrapped
+ * vector otherwise.
+ */
+static std::optional<std::vector<std::string>> ExtractJStringArray(JNIEnv* env,
+ const char* process_name,
+ jstring managed_process_name,
+ jobjectArray managed_array) {
+ if (managed_array == nullptr) {
+ return std::nullopt;
+ } else {
+ jsize element_count = env->GetArrayLength(managed_array);
+ std::vector<std::string> native_string_vector;
+ native_string_vector.reserve(element_count);
+
+ for (jsize array_index = 0; array_index < element_count; ++array_index) {
+ jstring managed_string = (jstring) env->GetObjectArrayElement(managed_array, array_index);
+ auto native_string = ExtractJString(env, process_name, managed_process_name, managed_string);
+
+ if (LIKELY(native_string.has_value())) {
+ native_string_vector.emplace_back(std::move(native_string.value()));
+ } else {
+ ZygoteFailure(env, process_name, managed_process_name,
+ "Null string found in managed string array.");
+ }
+ }
+
+ return std::move(native_string_vector);
+ }
+}
+
+/**
+ * A utility function for blocking signals.
+ *
+ * @param signum Signal number to block
+ * @param fail_fn Fatal error reporting function
+ *
+ * @see ZygoteFailure
+ */
+static void BlockSignal(int signum, fail_fn_t fail_fn) {
+ sigset_t sigs;
+ sigemptyset(&sigs);
+ sigaddset(&sigs, signum);
+
+ if (sigprocmask(SIG_BLOCK, &sigs, nullptr) == -1) {
+ fail_fn(CREATE_ERROR("Failed to block signal %s: %s", strsignal(signum), strerror(errno)));
+ }
+}
+
+
+/**
+ * A utility function for unblocking signals.
+ *
+ * @param signum Signal number to unblock
+ * @param fail_fn Fatal error reporting function
+ *
+ * @see ZygoteFailure
+ */
+static void UnblockSignal(int signum, fail_fn_t fail_fn) {
+ sigset_t sigs;
+ sigemptyset(&sigs);
+ sigaddset(&sigs, signum);
+
+ if (sigprocmask(SIG_UNBLOCK, &sigs, nullptr) == -1) {
+ fail_fn(CREATE_ERROR("Failed to un-block signal %s: %s", strsignal(signum), strerror(errno)));
+ }
+}
+
+// Utility routine to fork a process from the zygote.
static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
- jintArray managed_fds_to_close, jintArray managed_fds_to_ignore) {
+ const std::vector<int>& fds_to_close,
+ const std::vector<int>& fds_to_ignore) {
SetSignalHandlers();
- // Block SIGCHLD prior to fork.
- sigset_t sigchld;
- sigemptyset(&sigchld);
- sigaddset(&sigchld, SIGCHLD);
-
// Curry a failure function.
auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote",
nullptr, _1);
@@ -815,9 +1059,7 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
// This would cause failures because the FDs are not whitelisted.
//
// Note that the zygote process is single threaded at this point.
- if (sigprocmask(SIG_BLOCK, &sigchld, nullptr) == -1) {
- fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
- }
+ BlockSignal(SIGCHLD, fail_fn);
// Close any logging related FDs before we start evaluating the list of
// file descriptors.
@@ -827,19 +1069,10 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
// If this is the first fork for this zygote, create the open FD table. If
// it isn't, we just need to check whether the list of open files has changed
// (and it shouldn't in the normal case).
- std::string error_msg;
- std::vector<int> fds_to_ignore;
- if (!FillFileDescriptorVector(env, managed_fds_to_ignore, &fds_to_ignore, &error_msg)) {
- fail_fn(error_msg);
- }
-
if (gOpenFdTable == nullptr) {
- gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, &error_msg);
- if (gOpenFdTable == nullptr) {
- fail_fn(error_msg);
- }
- } else if (!gOpenFdTable->Restat(fds_to_ignore, &error_msg)) {
- fail_fn(error_msg);
+ gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn);
+ } else {
+ gOpenFdTable->Restat(fds_to_ignore, fail_fn);
}
android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();
@@ -851,24 +1084,18 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
PreApplicationInit();
// Clean up any descriptors which must be closed immediately
- if (!DetachDescriptors(env, managed_fds_to_close, &error_msg)) {
- fail_fn(error_msg);
- }
+ DetachDescriptors(env, fds_to_close, fail_fn);
// Re-open all remaining open file descriptors so that they aren't shared
// with the zygote across a fork.
- if (!gOpenFdTable->ReopenOrDetach(&error_msg)) {
- fail_fn(error_msg);
- }
+ gOpenFdTable->ReopenOrDetach(fail_fn);
// Turn fdsan back on.
android_fdsan_set_error_level(fdsan_error_level);
}
// We blocked SIGCHLD prior to a fork, we unblock it here.
- if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
- fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
- }
+ UnblockSignal(SIGCHLD, fail_fn);
return pid;
}
@@ -883,10 +1110,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jstring managed_app_data_dir, jstring managed_package_name,
jobjectArray managed_pacakges_for_uid,
jobjectArray managed_visible_vol_ids) {
- auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote",
- managed_nice_name, _1);
- auto extract_fn = std::bind(ExtractJString, env, is_system_server ? "system_server" : "zygote",
- managed_nice_name, _1);
+ const char* process_name = is_system_server ? "system_server" : "zygote";
+ auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
+ auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
auto se_info = extract_fn(managed_se_info);
auto nice_name = extract_fn(managed_nice_name);
@@ -894,22 +1120,14 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
auto app_data_dir = extract_fn(managed_app_data_dir);
auto package_name = extract_fn(managed_package_name);
- std::string error_msg;
-
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
- if (!EnableKeepCapabilities(&error_msg)) {
- fail_fn(error_msg);
- }
+ EnableKeepCapabilities(fail_fn);
}
- if (!SetInheritable(permitted_capabilities, &error_msg)) {
- fail_fn(error_msg);
- }
+ SetInheritable(permitted_capabilities, fail_fn);
- if (!DropCapabilitiesBoundingSet(&error_msg)) {
- fail_fn(error_msg);
- }
+ DropCapabilitiesBoundingSet(fail_fn);
bool use_native_bridge = !is_system_server &&
instruction_set.has_value() &&
@@ -934,56 +1152,21 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
}
}
- std::vector<std::string> packages_for_uid;
- if (managed_pacakges_for_uid != nullptr) {
- jsize count = env->GetArrayLength(managed_pacakges_for_uid);
- for (jsize package_index = 0; package_index < count; ++package_index) {
- jstring managed_package_for_uid =
- (jstring) env->GetObjectArrayElement(managed_pacakges_for_uid, package_index);
+ std::vector<std::string> packages_for_uid =
+ ExtractJStringArray(env, process_name, managed_nice_name, managed_pacakges_for_uid).
+ value_or(std::vector<std::string>());
- auto package_for_uid = extract_fn(managed_package_for_uid);
- if (LIKELY(package_for_uid.has_value())) {
- packages_for_uid.emplace_back(std::move(package_for_uid.value()));
- } else {
- fail_fn("Null string found in managed packages_for_uid.");
- }
- }
- }
-
- std::vector<std::string> visible_vol_ids;
- if (managed_visible_vol_ids != nullptr) {
- jsize count = env->GetArrayLength(managed_visible_vol_ids);
- for (jsize vol_id_index = 0; vol_id_index < count; ++vol_id_index) {
- jstring managed_visible_vol_id =
- (jstring) env->GetObjectArrayElement(managed_visible_vol_ids, vol_id_index);
-
- auto visible_vol_id = extract_fn(managed_visible_vol_id);
- if (LIKELY(visible_vol_id.has_value())) {
- visible_vol_ids.emplace_back(std::move(visible_vol_id.value()));
- } else {
- fail_fn("Null string found in managed visible_vol_ids.");
- }
- }
- }
+ std::vector<std::string> visible_vol_ids =
+ ExtractJStringArray(env, process_name, managed_nice_name, managed_visible_vol_ids).
+ value_or(std::vector<std::string>());
- if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg,
- package_name.value(), packages_for_uid, visible_vol_ids)) {
- ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
- if (errno == ENOTCONN || errno == EROFS) {
- // When device is actively encrypting, we get ENOTCONN here
- // since FUSE was mounted before the framework restarted.
- // When encrypted device is booting, we get EROFS since
- // FUSE hasn't been created yet by init.
- // In either case, continue without external storage.
- } else {
- fail_fn(error_msg);
- }
- }
+ MountEmulatedStorage(uid, mount_external, use_native_bridge, package_name.value(),
+ packages_for_uid, visible_vol_ids, fail_fn);
// If this zygote isn't root, it won't be able to create a process group,
// since the directory is owned by root.
if (!is_system_server && getuid() == 0) {
- int rc = createProcessGroup(uid, getpid());
+ const int rc = createProcessGroup(uid, getpid());
if (rc == -EROFS) {
ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
} else if (rc != 0) {
@@ -991,13 +1174,8 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
}
}
- if (!SetGids(env, gids, &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (!SetRLimits(env, rlimits, &error_msg)) {
- fail_fn(error_msg);
- }
+ SetGids(env, gids, fail_fn);
+ SetRLimits(env, rlimits, fail_fn);
if (use_native_bridge) {
// Due to the logic behind use_native_bridge we know that both app_data_dir
@@ -1056,14 +1234,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
}
}
- if (!SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities,
- &error_msg)) {
- fail_fn(error_msg);
- }
+ SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities, fail_fn);
- if (!SetSchedulerPolicy(&error_msg)) {
- fail_fn(error_msg);
- }
+ SetSchedulerPolicy(fail_fn);
const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
@@ -1076,7 +1249,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
// Make it easier to debug audit logs by setting the main thread's name to the
// nice name rather than "app_process".
if (nice_name.has_value()) {
- SetThreadName(nice_name.value().c_str());
+ SetThreadName(nice_name.value());
} else if (is_system_server) {
SetThreadName("system_server");
}
@@ -1089,6 +1262,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
if (env->ExceptionCheck()) {
fail_fn("Error calling post fork system server hooks.");
}
+
// TODO(oth): Remove hardcoded label here (b/117874058).
static const char* kSystemServerLabel = "u:r:system_server:s0";
if (selinux_android_setcon(kSystemServerLabel) != 0) {
@@ -1192,6 +1366,74 @@ static jlong CalculateCapabilities(JNIEnv* env, jint uid, jint gid, jintArray gi
return capabilities & GetEffectiveCapabilityMask(env);
}
+
+/**
+ * Adds the given information about a newly created blastula to the Zygote's
+ * blastula table.
+ *
+ * @param blastula_pid Process ID of the newly created blastula
+ * @param read_pipe_fd File descriptor for the read end of the blastula
+ * reporting pipe. Used in the ZygoteServer poll loop to track blastula
+ * specialization.
+ */
+static void AddBlastulaTableEntry(pid_t blastula_pid, int read_pipe_fd) {
+ static int sBlastulaTableInsertIndex = 0;
+
+ int search_index = sBlastulaTableInsertIndex;
+
+ do {
+ if (gBlastulaTable[search_index].SetIfInvalid(blastula_pid, read_pipe_fd)) {
+ // Start our next search right after where we finished this one.
+ sBlastulaTableInsertIndex = (search_index + 1) % gBlastulaTable.size();
+
+ return;
+ }
+
+ search_index = (search_index + 1) % gBlastulaTable.size();
+ } while (search_index != sBlastulaTableInsertIndex);
+
+ // Much like money in the banana stand, there should always be an entry
+ // in the blastula table.
+ __builtin_unreachable();
+}
+
+/**
+ * Invalidates the entry in the BlastulaTable corresponding to the provided
+ * process ID if it is present. If an entry was removed the blastula pool
+ * count is decremented.
+ *
+ * @param blastula_pid Process ID of the blastula entry to invalidate
+ * @return True if an entry was invalidated; false otherwise
+ */
+static bool RemoveBlastulaTableEntry(pid_t blastula_pid) {
+ for (BlastulaTableEntry& entry : gBlastulaTable) {
+ if (entry.ClearForPID(blastula_pid)) {
+ --gBlastulaPoolCount;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @return A vector of the read pipe FDs for each of the active blastulas.
+ */
+std::vector<int> MakeBlastulaPipeReadFDVector() {
+ std::vector<int> fd_vec;
+ fd_vec.reserve(gBlastulaTable.size());
+
+ for (BlastulaTableEntry& entry : gBlastulaTable) {
+ auto entry_values = entry.GetValues();
+
+ if (entry_values.has_value()) {
+ fd_vec.push_back(entry_values.value().read_pipe_fd);
+ }
+ }
+
+ return fd_vec;
+}
+
} // anonymous namespace
namespace android {
@@ -1210,12 +1452,35 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
- jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote,
+ jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir, jstring package_name,
jobjectArray packages_for_uid, jobjectArray visible_vol_ids) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
+ if (UNLIKELY(managed_fds_to_close == nullptr)) {
+ ZygoteFailure(env, "zygote", nice_name, "Zygote received a null fds_to_close vector.");
+ }
+
+ std::vector<int> fds_to_close =
+ ExtractJIntArray(env, "zygote", nice_name, managed_fds_to_close).value();
+ std::vector<int> fds_to_ignore =
+ ExtractJIntArray(env, "zygote", nice_name, managed_fds_to_ignore)
+ .value_or(std::vector<int>());
+
+ std::vector<int> blastula_pipes = MakeBlastulaPipeReadFDVector();
+
+ fds_to_close.insert(fds_to_close.end(), blastula_pipes.begin(), blastula_pipes.end());
+ fds_to_ignore.insert(fds_to_ignore.end(), blastula_pipes.begin(), blastula_pipes.end());
+
+// fds_to_close.push_back(gBlastulaPoolSocketFD);
+
+ if (gBlastulaPoolEventFD != -1) {
+ fds_to_close.push_back(gBlastulaPoolEventFD);
+ fds_to_ignore.push_back(gBlastulaPoolEventFD);
+ }
+
pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore);
+
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
@@ -1230,9 +1495,19 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
jlong effective_capabilities) {
+ std::vector<int> fds_to_close(MakeBlastulaPipeReadFDVector()),
+ fds_to_ignore(fds_to_close);
+
+// fds_to_close.push_back(gBlastulaPoolSocketFD);
+
+ if (gBlastulaPoolEventFD != -1) {
+ fds_to_close.push_back(gBlastulaPoolEventFD);
+ fds_to_ignore.push_back(gBlastulaPoolEventFD);
+ }
+
pid_t pid = ForkCommon(env, true,
- /* managed_fds_to_close= */ nullptr,
- /* managed_fds_to_ignore= */ nullptr);
+ fds_to_close,
+ fds_to_ignore);
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
permitted_capabilities, effective_capabilities,
@@ -1266,6 +1541,52 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
return pid;
}
+/**
+ * A JNI function that forks a blastula from the Zygote while ensuring proper
+ * file descriptor hygiene.
+ *
+ * @param env Managed runtime environment
+ * @param read_pipe_fd The read FD for the blastula reporting pipe. Manually closed by blastlas
+ * in managed code.
+ * @param write_pipe_fd The write FD for the blastula reporting pipe. Manually closed by the
+ * zygote in managed code.
+ * @param managed_session_socket_fds A list of anonymous session sockets that must be ignored by
+ * the FD hygiene code and automatically "closed" in the new blastula.
+ * @return
+ */
+static jint com_android_internal_os_Zygote_nativeForkBlastula(JNIEnv* env, jclass,
+ jint read_pipe_fd, jint write_pipe_fd, jintArray managed_session_socket_fds) {
+ std::vector<int> fds_to_close(MakeBlastulaPipeReadFDVector()),
+ fds_to_ignore(fds_to_close);
+
+ std::vector<int> session_socket_fds =
+ ExtractJIntArray(env, "blastula", nullptr, managed_session_socket_fds)
+ .value_or(std::vector<int>());
+
+ // The Blastula Pool Event FD is created during the initialization of the
+ // blastula pool and should always be valid here.
+
+ fds_to_close.push_back(gZygoteSocketFD);
+ fds_to_close.push_back(gBlastulaPoolEventFD);
+ fds_to_close.insert(fds_to_close.end(), session_socket_fds.begin(), session_socket_fds.end());
+
+ fds_to_ignore.push_back(gZygoteSocketFD);
+ fds_to_ignore.push_back(gBlastulaPoolSocketFD);
+ fds_to_ignore.push_back(gBlastulaPoolEventFD);
+ fds_to_ignore.push_back(read_pipe_fd);
+ fds_to_ignore.push_back(write_pipe_fd);
+ fds_to_ignore.insert(fds_to_ignore.end(), session_socket_fds.begin(), session_socket_fds.end());
+
+ pid_t blastula_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore);
+
+ if (blastula_pid != 0) {
+ ++gBlastulaPoolCount;
+ AddBlastulaTableEntry(blastula_pid, read_pipe_fd);
+ }
+
+ return blastula_pid;
+}
+
static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
JNIEnv* env, jclass, jstring path) {
ScopedUtfChars path_native(env, path);
@@ -1321,14 +1642,125 @@ static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter(
return;
}
- // TODO(b/111434506) install the filter
-
- /*
bool installed = install_setuidgid_seccomp_filter(uidGidMin, uidGidMax);
if (!installed) {
RuntimeAbort(env, __LINE__, "Could not install setuid/setgid seccomp filter.");
}
- */
+}
+
+/**
+ * Called from a blastula to specialize the process for a specific application.
+ *
+ * @param env Managed runtime environment
+ * @param uid User ID of the new application
+ * @param gid Group ID of the new application
+ * @param gids Extra groups that the process belongs to
+ * @param runtime_flags Flags for changing the behavior of the managed runtime
+ * @param rlimits Resource limits
+ * @param mount_external The mode (read/write/normal) that external storage will be mounted with
+ * @param se_info SELinux policy information
+ * @param nice_name New name for this process
+ * @param is_child_zygote If the process is to become a WebViewZygote
+ * @param instruction_set The instruction set expected/requested by the new application
+ * @param app_data_dir Path to the application's data directory
+ */
+static void com_android_internal_os_Zygote_nativeSpecializeBlastula(
+ JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
+ jint runtime_flags, jobjectArray rlimits,
+ jint mount_external, jstring se_info, jstring nice_name,
+ jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir,
+ jstring package_name, jobjectArray packages_for_uid, jobjectArray visible_vol_ids) {
+ jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
+
+ SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
+ capabilities, capabilities,
+ mount_external, se_info, nice_name, false,
+ is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
+ package_name, packages_for_uid, visible_vol_ids);
+}
+
+/**
+ * A helper method for fetching socket file descriptors that were opened by init from the
+ * environment.
+ *
+ * @param env Managed runtime environment
+ * @param is_primary If this process is the primary or secondary Zygote; used to compute the name
+ * of the environment variable storing the file descriptors.
+ */
+static void com_android_internal_os_Zygote_nativeGetSocketFDs(JNIEnv* env, jclass,
+ jboolean is_primary) {
+ std::string android_socket_prefix(ANDROID_SOCKET_PREFIX);
+ std::string env_var_name = android_socket_prefix + (is_primary ? "zygote" : "zygote_secondary");
+ char* env_var_val = getenv(env_var_name.c_str());
+
+ if (env_var_val != nullptr) {
+ gZygoteSocketFD = atoi(env_var_val);
+ ALOGV("Zygote:zygoteSocketFD = %d", gZygoteSocketFD);
+ } else {
+ ALOGE("Unable to fetch Zygote socket file descriptor");
+ }
+
+ env_var_name = android_socket_prefix + (is_primary ? "blastula_pool" : "blastula_pool_secondary");
+ env_var_val = getenv(env_var_name.c_str());
+
+ if (env_var_val != nullptr) {
+ gBlastulaPoolSocketFD = atoi(env_var_val);
+ ALOGV("Zygote:blastulaPoolSocketFD = %d", gBlastulaPoolSocketFD);
+ } else {
+ ALOGE("Unable to fetch Blastula pool socket file descriptor");
+ }
+}
+
+/**
+ * @param env Managed runtime environment
+ * @return A managed array of raw file descriptors for the read ends of the blastula reporting
+ * pipes.
+ */
+static jintArray com_android_internal_os_Zygote_nativeGetBlastulaPipeFDs(JNIEnv* env, jclass) {
+ std::vector<int> blastula_fds = MakeBlastulaPipeReadFDVector();
+
+ jintArray managed_blastula_fds = env->NewIntArray(blastula_fds.size());
+ env->SetIntArrayRegion(managed_blastula_fds, 0, blastula_fds.size(), blastula_fds.data());
+
+ return managed_blastula_fds;
+}
+
+/**
+ * A JNI wrapper around RemoveBlastulaTableEntry.
+ *
+ * @param env Managed runtime environment
+ * @param blastula_pid Process ID of the blastula entry to invalidate
+ * @return True if an entry was invalidated; false otherwise.
+ */
+static jboolean com_android_internal_os_Zygote_nativeRemoveBlastulaTableEntry(JNIEnv* env, jclass,
+ jint blastula_pid) {
+ return RemoveBlastulaTableEntry(blastula_pid);
+}
+
+/**
+ * Creates the blastula pool event FD if it doesn't exist and returns it. This is used by the
+ * ZygoteServer poll loop to know when to re-fill the blastula pool.
+ *
+ * @param env Managed runtime environment
+ * @return A raw event file descriptor used to communicate (from the signal handler) when the
+ * Zygote receives a SIGCHLD for a blastula
+ */
+static jint com_android_internal_os_Zygote_nativeGetBlastulaPoolEventFD(JNIEnv* env, jclass) {
+ if (gBlastulaPoolEventFD == -1) {
+ if ((gBlastulaPoolEventFD = eventfd(0, 0)) == -1) {
+ ZygoteFailure(env, "zygote", nullptr, StringPrintf("Unable to create eventfd: %s", strerror(errno)));
+ }
+ }
+
+ return gBlastulaPoolEventFD;
+}
+
+/**
+ * @param env Managed runtime environment
+ * @return The number of blastulas currently in the blastula pool
+ */
+static jint com_android_internal_os_Zygote_nativeGetBlastulaPoolCount(JNIEnv* env, jclass) {
+ return gBlastulaPoolCount;
}
static const JNINativeMethod gMethods[] = {
@@ -1346,7 +1778,22 @@ static const JNINativeMethod gMethods[] = {
{ "nativePreApplicationInit", "()V",
(void *) com_android_internal_os_Zygote_nativePreApplicationInit },
{ "nativeInstallSeccompUidGidFilter", "(II)V",
- (void *) com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter }
+ (void *) com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter },
+ { "nativeForkBlastula", "(II[I)I",
+ (void *) com_android_internal_os_Zygote_nativeForkBlastula },
+ { "nativeSpecializeBlastula",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
+ (void *) com_android_internal_os_Zygote_nativeSpecializeBlastula },
+ { "nativeGetSocketFDs", "(Z)V",
+ (void *) com_android_internal_os_Zygote_nativeGetSocketFDs },
+ { "nativeGetBlastulaPipeFDs", "()[I",
+ (void *) com_android_internal_os_Zygote_nativeGetBlastulaPipeFDs },
+ { "nativeRemoveBlastulaTableEntry", "(I)Z",
+ (void *) com_android_internal_os_Zygote_nativeRemoveBlastulaTableEntry },
+ { "nativeGetBlastulaPoolEventFD", "()I",
+ (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolEventFD },
+ { "nativeGetBlastulaPoolCount", "()I",
+ (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolCount }
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index d60d1a637962..53dde80edd89 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -38,6 +38,8 @@ static const char* kPathWhitelist[] = {
"/dev/null",
"/dev/socket/zygote",
"/dev/socket/zygote_secondary",
+ "/dev/socket/blastula_pool",
+ "/dev/socket/blastula_pool_secondary",
"/dev/socket/webview_zygote",
"/dev/socket/heapprofd",
"/sys/kernel/debug/tracing/trace_marker",
@@ -134,15 +136,14 @@ FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
// open zygote file descriptor.
class FileDescriptorInfo {
public:
- // Create a FileDescriptorInfo for a given file descriptor. Returns
- // |NULL| if an error occurred.
- static FileDescriptorInfo* CreateFromFd(int fd, std::string* error_msg);
+ // Create a FileDescriptorInfo for a given file descriptor.
+ static FileDescriptorInfo* CreateFromFd(int fd, fail_fn_t fail_fn);
// Checks whether the file descriptor associated with this object
// refers to the same description.
- bool Restat() const;
+ bool RefersToSameFile() const;
- bool ReopenOrDetach(std::string* error_msg) const;
+ void ReopenOrDetach(fail_fn_t fail_fn) const;
const int fd;
const struct stat stat;
@@ -168,19 +169,18 @@ class FileDescriptorInfo {
// address).
static bool GetSocketName(const int fd, std::string* result);
- bool DetachSocket(std::string* error_msg) const;
+ void DetachSocket(fail_fn_t fail_fn) const;
DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
};
// static
-FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_msg) {
+FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, fail_fn_t fail_fn) {
struct stat f_stat;
// This should never happen; the zygote should always have the right set
// of permissions required to stat all its open files.
if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
- *error_msg = android::base::StringPrintf("Unable to stat %d", fd);
- return nullptr;
+ fail_fn(android::base::StringPrintf("Unable to stat %d", fd));
}
const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
@@ -188,15 +188,13 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
if (S_ISSOCK(f_stat.st_mode)) {
std::string socket_name;
if (!GetSocketName(fd, &socket_name)) {
- *error_msg = "Unable to get socket name";
- return nullptr;
+ fail_fn("Unable to get socket name");
}
if (!whitelist->IsAllowed(socket_name)) {
- *error_msg = android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
- socket_name.c_str(),
- fd);
- return nullptr;
+ fail_fn(android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
+ socket_name.c_str(),
+ fd));
}
return new FileDescriptorInfo(fd);
@@ -209,26 +207,35 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
// S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
// S_ISLINK : Not supported.
// S_ISBLK : Not supported.
- // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
- // with the child process across forks but those should have been closed
- // before we got to this point.
+ // S_ISFIFO : Not supported. Note that the Zygote and blastulas use pipes to
+ // communicate with the child processes across forks but those should have been
+ // added to the redirection exemption list.
if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
- *error_msg = android::base::StringPrintf("Unsupported st_mode %u", f_stat.st_mode);
- return nullptr;
+ std::string mode = "Unknown";
+
+ if (S_ISDIR(f_stat.st_mode)) {
+ mode = "DIR";
+ } else if (S_ISLNK(f_stat.st_mode)) {
+ mode = "LINK";
+ } else if (S_ISBLK(f_stat.st_mode)) {
+ mode = "BLOCK";
+ } else if (S_ISFIFO(f_stat.st_mode)) {
+ mode = "FIFO";
+ }
+
+ fail_fn(android::base::StringPrintf("Unsupported st_mode for FD %d: %s", fd, mode.c_str()));
}
std::string file_path;
const std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
if (!android::base::Readlink(fd_path, &file_path)) {
- *error_msg = android::base::StringPrintf("Could not read fd link %s: %s",
- fd_path.c_str(),
- strerror(errno));
- return nullptr;
+ fail_fn(android::base::StringPrintf("Could not read fd link %s: %s",
+ fd_path.c_str(),
+ strerror(errno)));
}
if (!whitelist->IsAllowed(file_path)) {
- *error_msg = std::string("Not whitelisted : ").append(file_path);
- return nullptr;
+ fail_fn(std::string("Not whitelisted : ").append(file_path));
}
// File descriptor flags : currently on FD_CLOEXEC. We can set these
@@ -236,11 +243,10 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
// there won't be any races.
const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
if (fd_flags == -1) {
- *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFD) (%s): %s",
- fd,
- file_path.c_str(),
- strerror(errno));
- return nullptr;
+ fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_GETFD) (%s): %s",
+ fd,
+ file_path.c_str(),
+ strerror(errno)));
}
// File status flags :
@@ -257,11 +263,10 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
// their presence and pass them in to open().
int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
if (fs_flags == -1) {
- *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFL) (%s): %s",
- fd,
- file_path.c_str(),
- strerror(errno));
- return nullptr;
+ fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_GETFL) (%s): %s",
+ fd,
+ file_path.c_str(),
+ strerror(errno)));
}
// File offset : Ignore the offset for non seekable files.
@@ -276,7 +281,7 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
}
-bool FileDescriptorInfo::Restat() const {
+bool FileDescriptorInfo::RefersToSameFile() const {
struct stat f_stat;
if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
PLOG(ERROR) << "Unable to restat fd " << fd;
@@ -286,9 +291,9 @@ bool FileDescriptorInfo::Restat() const {
return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
}
-bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const {
+void FileDescriptorInfo::ReopenOrDetach(fail_fn_t fail_fn) const {
if (is_sock) {
- return DetachSocket(error_msg);
+ return DetachSocket(fail_fn);
}
// NOTE: This might happen if the file was unlinked after being opened.
@@ -297,57 +302,50 @@ bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const {
const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
if (new_fd == -1) {
- *error_msg = android::base::StringPrintf("Failed open(%s, %i): %s",
- file_path.c_str(),
- open_flags,
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed open(%s, %i): %s",
+ file_path.c_str(),
+ open_flags,
+ strerror(errno)));
}
if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
close(new_fd);
- *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_SETFD, %d) (%s): %s",
- new_fd,
- fd_flags,
- file_path.c_str(),
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_SETFD, %d) (%s): %s",
+ new_fd,
+ fd_flags,
+ file_path.c_str(),
+ strerror(errno)));
}
if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
close(new_fd);
- *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_SETFL, %d) (%s): %s",
- new_fd,
- fs_flags,
- file_path.c_str(),
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_SETFL, %d) (%s): %s",
+ new_fd,
+ fs_flags,
+ file_path.c_str(),
+ strerror(errno)));
}
if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
close(new_fd);
- *error_msg = android::base::StringPrintf("Failed lseek64(%d, SEEK_SET) (%s): %s",
- new_fd,
- file_path.c_str(),
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed lseek64(%d, SEEK_SET) (%s): %s",
+ new_fd,
+ file_path.c_str(),
+ strerror(errno)));
}
- int dupFlags = (fd_flags & FD_CLOEXEC) ? O_CLOEXEC : 0;
- if (TEMP_FAILURE_RETRY(dup3(new_fd, fd, dupFlags)) == -1) {
+ int dup_flags = (fd_flags & FD_CLOEXEC) ? O_CLOEXEC : 0;
+ if (TEMP_FAILURE_RETRY(dup3(new_fd, fd, dup_flags)) == -1) {
close(new_fd);
- *error_msg = android::base::StringPrintf("Failed dup3(%d, %d, %d) (%s): %s",
- fd,
- new_fd,
- dupFlags,
- file_path.c_str(),
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed dup3(%d, %d, %d) (%s): %s",
+ fd,
+ new_fd,
+ dup_flags,
+ file_path.c_str(),
+ strerror(errno)));
}
close(new_fd);
-
- return true;
}
FileDescriptorInfo::FileDescriptorInfo(int fd) :
@@ -373,7 +371,6 @@ FileDescriptorInfo::FileDescriptorInfo(struct stat stat, const std::string& file
is_sock(false) {
}
-// static
bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
sockaddr_storage ss;
sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
@@ -417,86 +414,75 @@ bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
return true;
}
-bool FileDescriptorInfo::DetachSocket(std::string* error_msg) const {
+void FileDescriptorInfo::DetachSocket(fail_fn_t fail_fn) const {
const int dev_null_fd = open("/dev/null", O_RDWR);
if (dev_null_fd < 0) {
- *error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno));
- return false;
+ fail_fn(std::string("Failed to open /dev/null: ").append(strerror(errno)));
}
if (dup2(dev_null_fd, fd) == -1) {
- *error_msg = android::base::StringPrintf("Failed dup2 on socket descriptor %d: %s",
- fd,
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed dup2 on socket descriptor %d: %s",
+ fd,
+ strerror(errno)));
}
if (close(dev_null_fd) == -1) {
- *error_msg = android::base::StringPrintf("Failed close(%d): %s", dev_null_fd, strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed close(%d): %s", dev_null_fd, strerror(errno)));
}
-
- return true;
}
// static
FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore,
- std::string* error_msg) {
- DIR* d = opendir(kFdPath);
- if (d == nullptr) {
- *error_msg = std::string("Unable to open directory ").append(kFdPath);
- return nullptr;
+ fail_fn_t fail_fn) {
+ DIR* proc_fd_dir = opendir(kFdPath);
+ if (proc_fd_dir == nullptr) {
+ fail_fn(std::string("Unable to open directory ").append(kFdPath));
}
- int dir_fd = dirfd(d);
- dirent* e;
+
+ int dir_fd = dirfd(proc_fd_dir);
+ dirent* dir_entry;
std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
- while ((e = readdir(d)) != NULL) {
- const int fd = ParseFd(e, dir_fd);
+ while ((dir_entry = readdir(proc_fd_dir)) != nullptr) {
+ const int fd = ParseFd(dir_entry, dir_fd);
if (fd == -1) {
continue;
}
+
if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
LOG(INFO) << "Ignoring open file descriptor " << fd;
continue;
}
- FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg);
- if (info == NULL) {
- if (closedir(d) == -1) {
- PLOG(ERROR) << "Unable to close directory";
- }
- return NULL;
- }
- open_fd_map[fd] = info;
+ open_fd_map[fd] = FileDescriptorInfo::CreateFromFd(fd, fail_fn);
}
- if (closedir(d) == -1) {
- *error_msg = "Unable to close directory";
- return nullptr;
+ if (closedir(proc_fd_dir) == -1) {
+ fail_fn("Unable to close directory");
}
+
return new FileDescriptorTable(open_fd_map);
}
-bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg) {
+void FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, fail_fn_t fail_fn) {
std::set<int> open_fds;
// First get the list of open descriptors.
- DIR* d = opendir(kFdPath);
- if (d == NULL) {
- *error_msg = android::base::StringPrintf("Unable to open directory %s: %s",
- kFdPath,
- strerror(errno));
- return false;
+ DIR* proc_fd_dir = opendir(kFdPath);
+ if (proc_fd_dir == nullptr) {
+ fail_fn(android::base::StringPrintf("Unable to open directory %s: %s",
+ kFdPath,
+ strerror(errno)));
}
- int dir_fd = dirfd(d);
- dirent* e;
- while ((e = readdir(d)) != NULL) {
- const int fd = ParseFd(e, dir_fd);
+ int dir_fd = dirfd(proc_fd_dir);
+ dirent* dir_entry;
+ while ((dir_entry = readdir(proc_fd_dir)) != nullptr) {
+ const int fd = ParseFd(dir_entry, dir_fd);
if (fd == -1) {
continue;
}
+
if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
LOG(INFO) << "Ignoring open file descriptor " << fd;
continue;
@@ -505,27 +491,24 @@ bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, std::str
open_fds.insert(fd);
}
- if (closedir(d) == -1) {
- *error_msg = android::base::StringPrintf("Unable to close directory: %s", strerror(errno));
- return false;
+ if (closedir(proc_fd_dir) == -1) {
+ fail_fn(android::base::StringPrintf("Unable to close directory: %s", strerror(errno)));
}
- return RestatInternal(open_fds, error_msg);
+ RestatInternal(open_fds, fail_fn);
}
-// Reopens all file descriptors that are contained in the table. Returns true
-// if all descriptors were successfully re-opened or detached, and false if an
-// error occurred.
-bool FileDescriptorTable::ReopenOrDetach(std::string* error_msg) {
+// Reopens all file descriptors that are contained in the table.
+void FileDescriptorTable::ReopenOrDetach(fail_fn_t fail_fn) {
std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
const FileDescriptorInfo* info = it->second;
- if (info == NULL || !info->ReopenOrDetach(error_msg)) {
- return false;
+ if (info == nullptr) {
+ return;
+ } else {
+ info->ReopenOrDetach(fail_fn);
}
}
-
- return true;
}
FileDescriptorTable::FileDescriptorTable(
@@ -533,9 +516,7 @@ FileDescriptorTable::FileDescriptorTable(
: open_fd_map_(map) {
}
-bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* error_msg) {
- bool error = false;
-
+void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn) {
// Iterate through the list of file descriptors we've already recorded
// and check whether :
//
@@ -558,28 +539,18 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* e
} else {
// The entry from the file descriptor table is still open. Restat
// it and check whether it refers to the same file.
- const bool same_file = it->second->Restat();
- if (!same_file) {
+ if (!it->second->RefersToSameFile()) {
// The file descriptor refers to a different description. We must
// update our entry in the table.
delete it->second;
- it->second = FileDescriptorInfo::CreateFromFd(*element, error_msg);
- if (it->second == NULL) {
- // The descriptor no longer no longer refers to a whitelisted file.
- // We flag an error and remove it from the list of files we're
- // tracking.
- error = true;
- it = open_fd_map_.erase(it);
- } else {
- // Successfully restatted the file, move on to the next open FD.
- ++it;
- }
+ it->second = FileDescriptorInfo::CreateFromFd(*element, fail_fn);
} else {
// It's the same file. Nothing to do here. Move on to the next open
// FD.
- ++it;
}
+ ++it;
+
// Finally, remove the FD from the set of open_fds. We do this last because
// |element| will not remain valid after a call to erase.
open_fds.erase(element);
@@ -598,25 +569,15 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* e
std::set<int>::const_iterator it;
for (it = open_fds.begin(); it != open_fds.end(); ++it) {
const int fd = (*it);
- FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg);
- if (info == NULL) {
- // A newly opened file is not on the whitelist. Flag an error and
- // continue.
- error = true;
- } else {
- // Track the newly opened file.
- open_fd_map_[fd] = info;
- }
+ open_fd_map_[fd] = FileDescriptorInfo::CreateFromFd(fd, fail_fn);
}
}
-
- return !error;
}
// static
-int FileDescriptorTable::ParseFd(dirent* e, int dir_fd) {
+int FileDescriptorTable::ParseFd(dirent* dir_entry, int dir_fd) {
char* end;
- const int fd = strtol(e->d_name, &end, 10);
+ const int fd = strtol(dir_entry->d_name, &end, 10);
if ((*end) != '\0') {
return -1;
}
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index 09022a2e2408..2caf1575981a 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -30,6 +30,9 @@
class FileDescriptorInfo;
+// This type is duplicated in com_android_internal_os_Zygote.cpp
+typedef const std::function<void(std::string)>& fail_fn_t;
+
// Whitelist of open paths that the zygote is allowed to keep open.
//
// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
@@ -76,19 +79,19 @@ class FileDescriptorTable {
// /proc/self/fd for the list of open file descriptors and collects
// information about them. Returns NULL if an error occurs.
static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore,
- std::string* error_msg);
+ fail_fn_t fail_fn);
- bool Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg);
+ void Restat(const std::vector<int>& fds_to_ignore, fail_fn_t fail_fn);
// Reopens all file descriptors that are contained in the table. Returns true
// if all descriptors were successfully re-opened or detached, and false if an
// error occurred.
- bool ReopenOrDetach(std::string* error_msg);
+ void ReopenOrDetach(fail_fn_t fail_fn);
private:
explicit FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
- bool RestatInternal(std::set<int>& open_fds, std::string* error_msg);
+ void RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn);
static int ParseFd(dirent* e, int dir_fd);
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 7f3ea7a249ba..e68f9dbbc9b7 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -221,6 +221,8 @@ message ConstantsProto {
optional bool use_heartbeats = 23;
message TimeController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
// Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
// ready now.
optional bool skip_not_ready_jobs = 1;
@@ -228,6 +230,8 @@ message ConstantsProto {
optional TimeController time_controller = 25;
message QuotaController {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
// How much time each app will have to run jobs within their standby bucket window.
optional int64 allowed_time_per_period_ms = 1;
// How much time the package should have before transitioning from out-of-quota to in-quota.
@@ -251,6 +255,21 @@ message ConstantsProto {
optional int64 rare_window_size_ms = 6;
// The maximum amount of time an app can have its jobs running within a 24 hour window.
optional int64 max_execution_time_ms = 7;
+ // The maximum number of jobs an app can run within this particular standby bucket's
+ // window size.
+ optional int32 max_job_count_active = 8;
+ // The maximum number of jobs an app can run within this particular standby bucket's
+ // window size.
+ optional int32 max_job_count_working = 9;
+ // The maximum number of jobs an app can run within this particular standby bucket's
+ // window size.
+ optional int32 max_job_count_frequent = 10;
+ // The maximum number of jobs an app can run within this particular standby bucket's
+ // window size.
+ optional int32 max_job_count_rare = 11;
+ // The maximum number of jobs that should be allowed to run in the past
+ // {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
+ optional int32 max_job_count_per_allowed_time = 12;
}
optional QuotaController quota_controller = 24;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ea0c8e250fe2..96b8dc222e7e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1748,6 +1748,10 @@
<permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
android:protectionLevel="signature" />
+ <!-- @hide Allows the device to be reset, clearing all data and enables Test Harness Mode. -->
+ <permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE"
+ android:protectionLevel="signature" />
+
<!-- ================================== -->
<!-- Permissions for accessing accounts -->
<!-- ================================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5995640c7659..16d1d1a9b90a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -381,7 +381,7 @@
<!-- Configuration of Ethernet interfaces in the following format:
- <interface name|mac address>;[Network Capabilities];[IP config]
+ <interface name|mac address>;[Network Capabilities];[IP config];[Override Transport]
Where
[Network Capabilities] Optional. A comma seprated list of network capabilities.
Values must be from NetworkCapabilities#NET_CAPABILITIES_* constants.
@@ -389,11 +389,16 @@
use the following format to specify static IP configuration:
ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
domains=<comma-sep-domains>
+ [Override Transport] Optional. An override network transport type to allow
+ the propagation of an interface type on the other end of a local Ethernet
+ interface. Value must be from NetworkCapabilities#TRANSPORT_* constants. If
+ left out, this will default to TRANSPORT_ETHERNET.
-->
<string-array translatable="false" name="config_ethernet_interfaces">
<!--
<item>eth1;12,13,14,15;ip=192.168.0.10/24 gateway=192.168.0.1 dns=4.4.4.4,8.8.8.8</item>
<item>eth2;;ip=192.168.0.11/24</item>
+ <item>eth3;12,13,14,15;ip=192.168.0.12/24;1</item>
-->
</string-array>
@@ -705,6 +710,9 @@
Software implementation will be used if config_hardware_auto_brightness_available is not set -->
<bool name="config_automatic_brightness_available">false</bool>
+ <!-- Flag indicating whether we should enable the adaptive sleep.-->
+ <bool name="config_adaptive_sleep_available">false</bool>
+
<!-- Flag indicating whether we should enable smart battery. -->
<bool name="config_smart_battery_available">false</bool>
@@ -931,6 +939,10 @@
in hardware. -->
<bool name="config_setColorTransformAccelerated">false</bool>
+ <!-- Boolean indicating whether the HWC setColorTransform function can be performed efficiently
+ in hardware for individual layers. -->
+ <bool name="config_setColorTransformAcceleratedPerLayer">false</bool>
+
<!-- Control whether Night display is available. This should only be enabled on devices
that have a HWC implementation that can apply the matrix passed to setColorTransform
without impacting power, performance, and app compatibility (e.g. protected content). -->
@@ -1743,6 +1755,19 @@
config_enableGeofenceOverlay is false. -->
<string name="config_geofenceProviderPackageName" translatable="false">@null</string>
+ <!-- Whether to enable Hardware Activity-Recognition overlay which allows Hardware
+ Activity-Recognition to be replaced by an app at run-time. When disabled, only the
+ config_activityRecognitionHardwarePackageName package will be searched for
+ its implementation, otherwise packages whose signature matches the
+ signatures of config_locationProviderPackageNames will be searched, and
+ the service with the highest version number will be picked. Anyone who
+ wants to disable the overlay mechanism can set it to false.
+ -->
+ <bool name="config_enableActivityRecognitionHardwareOverlay" translatable="false">true</bool>
+ <!-- Package name providing Hardware Activity-Recognition API support. Used only when
+ config_enableActivityRecognitionHardwareOverlay is false. -->
+ <string name="config_activityRecognitionHardwarePackageName" translatable="false">@null</string>
+
<!-- Package name(s) containing location provider support.
These packages can contain services implementing location providers,
such as the Geocode Provider, Network Location Provider, and
@@ -3728,4 +3753,7 @@
<!-- Enable Zram writeback feature to allow unused pages in zram be written to flash. -->
<bool name="config_zramWriteback">false</bool>
+
+ <!-- Whether cbrs is supported on the device or not -->
+ <bool translatable="false" name="config_cbrs_supported">false</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0878562e00fb..f2b4b9ccfce8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3353,9 +3353,9 @@
<string name="wifi_available_action_all_networks">All networks</string>
<!-- Notification title for a connection to a app suggested wireless network.-->
- <string name="wifi_suggestion_title">Connected to Wi\u2011Fi network proposed by <xliff:g id="name" example="App123">%s</xliff:g></string>
+ <string name="wifi_suggestion_title">A Wi\u2011Fi network proposed by <xliff:g id="name" example="App123">%s</xliff:g> is available</string>
<!-- Notification content for a connection to a app suggested wireless network.-->
- <string name="wifi_suggestion_content">Do you want to let <xliff:g id="name" example="App123">%s</xliff:g> propose networks for you?</string>
+ <string name="wifi_suggestion_content">Do you want to connect to networks proposed by <xliff:g id="name" example="App123">%s</xliff:g>?</string>
<!-- Notification action for allowing app specified in the notification body.-->
<string name="wifi_suggestion_action_allow_app">Yes</string>
<!-- Notification action for disallowing app specified in the notification body.-->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 53c33a3855dd..751721860441 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1845,6 +1845,7 @@
<java-symbol type="array" name="config_defaultNotificationVibePattern" />
<java-symbol type="array" name="config_notificationFallbackVibePattern" />
<java-symbol type="bool" name="config_useAttentionLight" />
+ <java-symbol type="bool" name="config_adaptive_sleep_available" />
<java-symbol type="bool" name="config_animateScreenLights" />
<java-symbol type="bool" name="config_automatic_brightness_available" />
<java-symbol type="bool" name="config_smart_battery_available" />
@@ -1853,6 +1854,7 @@
<java-symbol type="bool" name="config_enableNightMode" />
<java-symbol type="bool" name="config_tintNotificationActionButtons" />
<java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
+ <java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
<java-symbol type="bool" name="config_enableHardwareFlpOverlay" />
<java-symbol type="bool" name="config_enableGeocoderOverlay" />
@@ -2021,6 +2023,7 @@
<java-symbol type="string" name="car_mode_disable_notification_title" />
<java-symbol type="string" name="chooser_wallpaper" />
<java-symbol type="string" name="config_datause_iface" />
+ <java-symbol type="string" name="config_activityRecognitionHardwarePackageName" />
<java-symbol type="string" name="config_fusedLocationProviderPackageName" />
<java-symbol type="string" name="config_hardwareFlpPackageName" />
<java-symbol type="string" name="config_geocoderProviderPackageName" />
@@ -3031,6 +3034,7 @@
<java-symbol type="drawable" name="ic_doc_generic" />
<java-symbol type="bool" name="config_setColorTransformAccelerated" />
+ <java-symbol type="bool" name="config_setColorTransformAcceleratedPerLayer" />
<java-symbol type="bool" name="config_displayWhiteBalanceAvailable" />
<java-symbol type="bool" name="config_nightDisplayAvailable" />
<java-symbol type="bool" name="config_allowDisablingAssistDisclosure" />
@@ -3540,4 +3544,7 @@
<java-symbol type="bool" name="config_silenceSensorAvailable" />
<java-symbol type="bool" name="config_zramWriteback" />
+
+ <!-- For CBRS -->
+ <java-symbol type="bool" name="config_cbrs_supported" />
</resources>
diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
index 8d42c74be7b0..5731daa0b2a9 100644
--- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
+++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java
@@ -20,12 +20,23 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
+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_NUMERIC_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static android.app.admin.PasswordMetrics.complexityLevelToMinQuality;
+import static android.app.admin.PasswordMetrics.getActualRequiredQuality;
+import static android.app.admin.PasswordMetrics.getMinimumMetrics;
+import static android.app.admin.PasswordMetrics.getTargetQualityMetrics;
+import static android.app.admin.PasswordMetrics.sanitizeComplexityLevel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
-import android.app.admin.PasswordMetrics.PasswordComplexityBucket;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
@@ -109,7 +120,7 @@ public class PasswordMetricsTest {
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
PasswordMetrics.computeForPassword("1").quality);
// contains a long sequence so isn't complex
- assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
+ assertEquals(PASSWORD_QUALITY_NUMERIC,
PasswordMetrics.computeForPassword("1234").quality);
assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
PasswordMetrics.computeForPassword("").quality);
@@ -145,7 +156,7 @@ public class PasswordMetricsTest {
new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 5));
assertNotEquals(new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 4),
- new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, 4));
+ new PasswordMetrics(PASSWORD_QUALITY_COMPLEX, 4));
metrics0 = PasswordMetrics.computeForPassword("1234abcd,./");
metrics1 = PasswordMetrics.computeForPassword("1234abcd,./");
@@ -176,9 +187,9 @@ public class PasswordMetricsTest {
@Test
public void testConstructQuality() {
PasswordMetrics expected = new PasswordMetrics();
- expected.quality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
+ expected.quality = PASSWORD_QUALITY_COMPLEX;
- PasswordMetrics actual = new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+ PasswordMetrics actual = new PasswordMetrics(PASSWORD_QUALITY_COMPLEX);
assertEquals(expected, actual);
}
@@ -256,42 +267,178 @@ public class PasswordMetricsTest {
}
@Test
- public void testComplexityLevelToBucket_none() {
- PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
- PASSWORD_COMPLEXITY_NONE).getMetrics();
+ public void testSanitizeComplexityLevel_none() {
+ assertEquals(PASSWORD_COMPLEXITY_NONE, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_NONE));
- for (PasswordMetrics metrics : bucket) {
- assertEquals(PASSWORD_COMPLEXITY_NONE, metrics.determineComplexity());
- }
}
@Test
- public void testComplexityLevelToBucket_low() {
- PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
- PASSWORD_COMPLEXITY_LOW).getMetrics();
+ public void testSanitizeComplexityLevel_low() {
+ assertEquals(PASSWORD_COMPLEXITY_LOW, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_LOW));
+ }
- for (PasswordMetrics metrics : bucket) {
- assertEquals(PASSWORD_COMPLEXITY_LOW, metrics.determineComplexity());
- }
+ @Test
+ public void testSanitizeComplexityLevel_medium() {
+ assertEquals(
+ PASSWORD_COMPLEXITY_MEDIUM, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_MEDIUM));
}
@Test
- public void testComplexityLevelToBucket_medium() {
- PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
- PASSWORD_COMPLEXITY_MEDIUM).getMetrics();
+ public void testSanitizeComplexityLevel_high() {
+ assertEquals(PASSWORD_COMPLEXITY_HIGH, sanitizeComplexityLevel(PASSWORD_COMPLEXITY_HIGH));
+ }
- for (PasswordMetrics metrics : bucket) {
- assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metrics.determineComplexity());
- }
+ @Test
+ public void testSanitizeComplexityLevel_invalid() {
+ assertEquals(PASSWORD_COMPLEXITY_NONE, sanitizeComplexityLevel(-1));
}
@Test
- public void testComplexityLevelToBucket_high() {
- PasswordMetrics[] bucket = PasswordComplexityBucket.complexityLevelToBucket(
- PASSWORD_COMPLEXITY_HIGH).getMetrics();
+ public void testComplexityLevelToMinQuality_none() {
+ assertEquals(PASSWORD_QUALITY_UNSPECIFIED,
+ complexityLevelToMinQuality(PASSWORD_COMPLEXITY_NONE));
+ }
- for (PasswordMetrics metrics : bucket) {
- assertEquals(PASSWORD_COMPLEXITY_HIGH, metrics.determineComplexity());
- }
+ @Test
+ public void testComplexityLevelToMinQuality_low() {
+ assertEquals(PASSWORD_QUALITY_SOMETHING,
+ complexityLevelToMinQuality(PASSWORD_COMPLEXITY_LOW));
+ }
+
+ @Test
+ public void testComplexityLevelToMinQuality_medium() {
+ assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX,
+ complexityLevelToMinQuality(PASSWORD_COMPLEXITY_MEDIUM));
+ }
+
+ @Test
+ public void testComplexityLevelToMinQuality_high() {
+ assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX,
+ complexityLevelToMinQuality(PASSWORD_COMPLEXITY_HIGH));
+ }
+
+ @Test
+ public void testComplexityLevelToMinQuality_invalid() {
+ assertEquals(PASSWORD_QUALITY_UNSPECIFIED, complexityLevelToMinQuality(-1));
+ }
+
+ @Test
+ public void testGetTargetQualityMetrics_noneComplexityReturnsDefaultMetrics() {
+ PasswordMetrics metrics =
+ getTargetQualityMetrics(PASSWORD_COMPLEXITY_NONE, PASSWORD_QUALITY_ALPHANUMERIC);
+
+ assertTrue(metrics.isDefault());
+ }
+
+ @Test
+ public void testGetTargetQualityMetrics_qualityNotAllowedReturnsMinQualityMetrics() {
+ PasswordMetrics metrics =
+ getTargetQualityMetrics(PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_QUALITY_NUMERIC);
+
+ assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, metrics.quality);
+ assertEquals(/* expected= */ 4, metrics.length);
+ }
+
+ @Test
+ public void testGetTargetQualityMetrics_highComplexityNumericComplex() {
+ PasswordMetrics metrics = getTargetQualityMetrics(
+ PASSWORD_COMPLEXITY_HIGH, PASSWORD_QUALITY_NUMERIC_COMPLEX);
+
+ assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, metrics.quality);
+ assertEquals(/* expected= */ 8, metrics.length);
+ }
+
+ @Test
+ public void testGetTargetQualityMetrics_mediumComplexityAlphabetic() {
+ PasswordMetrics metrics = getTargetQualityMetrics(
+ PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_QUALITY_ALPHABETIC);
+
+ assertEquals(PASSWORD_QUALITY_ALPHABETIC, metrics.quality);
+ assertEquals(/* expected= */ 4, metrics.length);
+ }
+
+ @Test
+ public void testGetTargetQualityMetrics_lowComplexityAlphanumeric() {
+ PasswordMetrics metrics = getTargetQualityMetrics(
+ PASSWORD_COMPLEXITY_MEDIUM, PASSWORD_QUALITY_ALPHANUMERIC);
+
+ assertEquals(PASSWORD_QUALITY_ALPHANUMERIC, metrics.quality);
+ assertEquals(/* expected= */ 4, metrics.length);
+ }
+
+ @Test
+ public void testGetActualRequiredQuality_nonComplex() {
+ int actual = getActualRequiredQuality(
+ PASSWORD_QUALITY_NUMERIC_COMPLEX,
+ /* requiresNumeric= */ false,
+ /* requiresLettersOrSymbols= */ false);
+
+ assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, actual);
+ }
+
+ @Test
+ public void testGetActualRequiredQuality_complexRequiresNone() {
+ int actual = getActualRequiredQuality(
+ PASSWORD_QUALITY_COMPLEX,
+ /* requiresNumeric= */ false,
+ /* requiresLettersOrSymbols= */ false);
+
+ assertEquals(PASSWORD_QUALITY_UNSPECIFIED, actual);
+ }
+
+ @Test
+ public void testGetActualRequiredQuality_complexRequiresNumeric() {
+ int actual = getActualRequiredQuality(
+ PASSWORD_QUALITY_COMPLEX,
+ /* requiresNumeric= */ true,
+ /* requiresLettersOrSymbols= */ false);
+
+ assertEquals(PASSWORD_QUALITY_NUMERIC, actual);
+ }
+
+ @Test
+ public void testGetActualRequiredQuality_complexRequiresLetters() {
+ int actual = getActualRequiredQuality(
+ PASSWORD_QUALITY_COMPLEX,
+ /* requiresNumeric= */ false,
+ /* requiresLettersOrSymbols= */ true);
+
+ assertEquals(PASSWORD_QUALITY_ALPHABETIC, actual);
+ }
+
+ @Test
+ public void testGetActualRequiredQuality_complexRequiresNumericAndLetters() {
+ int actual = getActualRequiredQuality(
+ PASSWORD_QUALITY_COMPLEX,
+ /* requiresNumeric= */ true,
+ /* requiresLettersOrSymbols= */ true);
+
+ assertEquals(PASSWORD_QUALITY_ALPHANUMERIC, actual);
+ }
+
+ @Test
+ public void testGetMinimumMetrics_userInputStricter() {
+ PasswordMetrics metrics = getMinimumMetrics(
+ PASSWORD_COMPLEXITY_HIGH,
+ PASSWORD_QUALITY_ALPHANUMERIC,
+ PASSWORD_QUALITY_NUMERIC,
+ /* requiresNumeric= */ false,
+ /* requiresLettersOrSymbols= */ false);
+
+ assertEquals(PASSWORD_QUALITY_ALPHANUMERIC, metrics.quality);
+ assertEquals(/* expected= */ 6, metrics.length);
+ }
+
+ @Test
+ public void testGetMinimumMetrics_actualRequiredQualityStricter() {
+ PasswordMetrics metrics = getMinimumMetrics(
+ PASSWORD_COMPLEXITY_HIGH,
+ PASSWORD_QUALITY_UNSPECIFIED,
+ PASSWORD_QUALITY_NUMERIC,
+ /* requiresNumeric= */ false,
+ /* requiresLettersOrSymbols= */ false);
+
+ assertEquals(PASSWORD_QUALITY_NUMERIC_COMPLEX, metrics.quality);
+ assertEquals(/* expected= */ 8, metrics.length);
}
}
diff --git a/core/tests/coretests/src/android/os/WorkSourceTest.java b/core/tests/coretests/src/android/os/WorkSourceTest.java
index 425ab8962307..e94d60c32bfe 100644
--- a/core/tests/coretests/src/android/os/WorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/WorkSourceTest.java
@@ -341,12 +341,37 @@ public class WorkSourceTest extends TestCase {
}
public void testGetAttributionId() {
- WorkSource ws1 = new WorkSource();
- WorkChain wc = ws1.createWorkChain();
- wc.addNode(100, "tag");
- assertEquals(100, wc.getAttributionUid());
- wc.addNode(200, "tag2");
- assertEquals(100, wc.getAttributionUid());
+ WorkSource ws = new WorkSource();
+ WorkChain wc1 = ws.createWorkChain();
+ wc1.addNode(100, "tag");
+ assertEquals(100, wc1.getAttributionUid());
+ assertEquals(100, ws.getAttributionUid());
+ wc1.addNode(200, "tag2");
+ assertEquals(100, wc1.getAttributionUid());
+ assertEquals(100, ws.getAttributionUid());
+ WorkChain wc2 = ws.createWorkChain();
+ wc2.addNode(300, "tag3");
+ assertEquals(300, wc2.getAttributionUid());
+ assertEquals(100, ws.getAttributionUid());
+ }
+
+ public void testGetAttributionIdWithoutWorkChain() {
+ WorkSource ws1 = new WorkSource(100);
+ ws1.add(200);
+ WorkSource ws2 = new WorkSource();
+ ws2.add(100);
+ ws2.add(200);
+ assertEquals(100, ws1.getAttributionUid());
+ assertEquals(100, ws2.getAttributionUid());
+ }
+
+ public void testGetAttributionWhenEmpty() {
+ WorkSource ws = new WorkSource();
+ assertEquals(-1, ws.getAttributionUid());
+ WorkChain wc = ws.createWorkChain();
+ assertEquals(-1, ws.getAttributionUid());
+ assertEquals(-1, wc.getAttributionUid());
+ assertNull(wc.getAttributionTag());
}
public void testGetAttributionTag() {
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 9857b7a1e035..2db2f5f2a4fd 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -60,7 +60,7 @@ public class InsetsStateTest {
mState.getSource(TYPE_IME).setVisible(true);
SparseIntArray typeSideMap = new SparseIntArray();
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT, typeSideMap);
+ DisplayCutout.NO_CUTOUT, null, null, typeSideMap);
assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR));
@@ -76,7 +76,7 @@ public class InsetsStateTest {
mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
mState.getSource(TYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT, null);
+ DisplayCutout.NO_CUTOUT, null, null, null);
assertEquals(100, insets.getStableInsetBottom());
assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.all()));
assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
@@ -92,7 +92,7 @@ public class InsetsStateTest {
mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT, null);
+ DisplayCutout.NO_CUTOUT, null, null, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.sideBars()));
@@ -106,7 +106,7 @@ public class InsetsStateTest {
mState.getSource(TYPE_IME).setVisible(true);
mState.removeSource(TYPE_IME);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT, null);
+ DisplayCutout.NO_CUTOUT, null, null, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
}
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 15a96a144dd5..d57fa8f9f612 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -18,7 +18,7 @@ package android.view;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.sideBars;
-
+import static android.view.WindowInsets.Type.topBar;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -54,6 +54,7 @@ public class WindowInsetsTest {
assertTrue(new WindowInsets((Rect) null).isConsumed());
}
+ // TODO: Move this to CTS once API made public
@Test
public void typeMap() {
Builder b = new WindowInsets.Builder();
@@ -62,4 +63,14 @@ public class WindowInsetsTest {
WindowInsets insets = b.build();
assertEquals(300, insets.getSystemWindowInsets().bottom);
}
+
+ // TODO: Move this to CTS once API made public
+ @Test
+ public void compatInsets() {
+ Builder b = new WindowInsets.Builder();
+ b.setSystemWindowInsets(Insets.of(0, 50, 30, 10));
+ WindowInsets insets = b.build();
+ assertEquals(Insets.of(0, 50, 0, 0), insets.getInsets(topBar()));
+ assertEquals(Insets.of(0, 0, 30, 10), insets.getInsets(sideBars()));
+ }
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 0dd768530603..683d16b1dea1 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -88,32 +88,32 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
public void setOnKeyEventResult(boolean handled, int sequence) {}
- public float getMagnificationScale() {
+ public float getMagnificationScale(int displayId) {
return 0.0f;
}
- public float getMagnificationCenterX() {
+ public float getMagnificationCenterX(int displayId) {
return 0.0f;
}
- public float getMagnificationCenterY() {
+ public float getMagnificationCenterY(int displayId) {
return 0.0f;
}
- public Region getMagnificationRegion() {
+ public Region getMagnificationRegion(int displayId) {
return null;
}
- public boolean resetMagnification(boolean animate) {
+ public boolean resetMagnification(int displayId, boolean animate) {
return false;
}
- public boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY,
- boolean animate) {
+ public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
+ float centerY, boolean animate) {
return false;
}
- public void setMagnificationCallbackEnabled(boolean enabled) {}
+ public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {}
public boolean setSoftKeyboardShowMode(int showMode) {
return false;
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index 0179eadae6a1..9cac7e794965 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -43,6 +43,10 @@ import android.view.Display;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import com.android.internal.util.ArrayUtils;
import org.junit.Before;
@@ -76,13 +80,13 @@ import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
public class BatteryStatsCpuTimesTest {
@Mock
- KernelUidCpuTimeReader mKernelUidCpuTimeReader;
+ KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader;
@Mock
- KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
+ KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader;
@Mock
- KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader;
+ KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader;
@Mock
- KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader;
+ KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader;
@Mock
BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
@Mock
@@ -98,10 +102,10 @@ public class BatteryStatsCpuTimesTest {
mClocks = new MockClocks();
mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks)
- .setKernelUidCpuTimeReader(mKernelUidCpuTimeReader)
- .setKernelUidCpuFreqTimeReader(mKernelUidCpuFreqTimeReader)
- .setKernelUidCpuActiveTimeReader(mKernelUidCpuActiveTimeReader)
- .setKernelUidCpuClusterTimeReader(mKernelUidCpuClusterTimeReader)
+ .setKernelCpuUidUserSysTimeReader(mCpuUidUserSysTimeReader)
+ .setKernelCpuUidFreqTimeReader(mCpuUidFreqTimeReader)
+ .setKernelCpuUidActiveTimeReader(mCpuUidActiveTimeReader)
+ .setKernelCpuUidClusterTimeReader(mCpuUidClusterTimeReader)
.setUserInfoProvider(mUserInfoProvider);
}
@@ -113,21 +117,21 @@ public class BatteryStatsCpuTimesTest {
final int numClusters = 3;
initKernelCpuSpeedReaders(numClusters);
final long[] freqs = {1, 12, 123, 12, 1234};
- when(mKernelUidCpuFreqTimeReader.readFreqs(mPowerProfile)).thenReturn(freqs);
+ when(mCpuUidFreqTimeReader.readFreqs(mPowerProfile)).thenReturn(freqs);
// RUN
mBatteryStatsImpl.updateCpuTimeLocked(false, false);
// VERIFY
assertArrayEquals("Unexpected cpu freqs", freqs, mBatteryStatsImpl.getCpuFreqs());
- verify(mKernelUidCpuTimeReader).readDelta(null);
- verify(mKernelUidCpuFreqTimeReader).readDelta(null);
+ verify(mCpuUidUserSysTimeReader).readDelta(null);
+ verify(mCpuUidFreqTimeReader).readDelta(null);
for (int i = 0; i < numClusters; ++i) {
verify(mKernelCpuSpeedReaders[i]).readDelta();
}
// Prepare for next test
- Mockito.reset(mUserInfoProvider, mKernelUidCpuFreqTimeReader, mKernelUidCpuTimeReader);
+ Mockito.reset(mUserInfoProvider, mCpuUidFreqTimeReader, mCpuUidUserSysTimeReader);
for (int i = 0; i < numClusters; ++i) {
Mockito.reset(mKernelCpuSpeedReaders[i]);
}
@@ -140,17 +144,18 @@ public class BatteryStatsCpuTimesTest {
// VERIFY
verify(mUserInfoProvider).refreshUserIds();
- verify(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+ verify(mCpuUidUserSysTimeReader).readDelta(
+ any(KernelCpuUidUserSysTimeReader.Callback.class));
// perClusterTimesAvailable is called twice, once in updateCpuTimeLocked() and the other
// in readKernelUidCpuFreqTimesLocked.
- verify(mKernelUidCpuFreqTimeReader, times(2)).perClusterTimesAvailable();
- verify(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
- verify(mKernelUidCpuActiveTimeReader).readDelta(
- any(KernelUidCpuActiveTimeReader.Callback.class));
- verify(mKernelUidCpuClusterTimeReader).readDelta(
- any(KernelUidCpuClusterTimeReader.Callback.class));
- verifyNoMoreInteractions(mKernelUidCpuFreqTimeReader);
+ verify(mCpuUidFreqTimeReader, times(2)).perClusterTimesAvailable();
+ verify(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
+ verify(mCpuUidActiveTimeReader).readDelta(
+ any(KernelCpuUidActiveTimeReader.Callback.class));
+ verify(mCpuUidClusterTimeReader).readDelta(
+ any(KernelCpuUidClusterTimeReader.Callback.class));
+ verifyNoMoreInteractions(mCpuUidFreqTimeReader);
for (int i = 0; i < numClusters; ++i) {
verify(mKernelCpuSpeedReaders[i]).readDelta();
}
@@ -253,13 +258,14 @@ public class BatteryStatsCpuTimesTest {
{12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
};
doAnswer(invocation -> {
- final KernelUidCpuTimeReader.Callback callback =
- (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
+ (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
+ callback.onUidCpuTime(testUids[i], uidTimesUs[i]);
}
return null;
- }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+ }).when(mCpuUidUserSysTimeReader).readDelta(
+ any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
final SparseLongArray updatedUids = new SparseLongArray();
@@ -287,13 +293,14 @@ public class BatteryStatsCpuTimesTest {
{9379, 3332409833484l}, {493247, 723234}, {3247819, 123348}
};
doAnswer(invocation -> {
- final KernelUidCpuTimeReader.Callback callback =
- (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
+ (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuTime(testUids[i], deltasUs[i][0], deltasUs[i][1]);
+ callback.onUidCpuTime(testUids[i], deltasUs[i]);
}
return null;
- }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+ }).when(mCpuUidUserSysTimeReader).readDelta(
+ any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null, true);
@@ -326,13 +333,14 @@ public class BatteryStatsCpuTimesTest {
{12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
};
doAnswer(invocation -> {
- final KernelUidCpuTimeReader.Callback callback =
- (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
+ (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
+ callback.onUidCpuTime(testUids[i], uidTimesUs[i]);
}
return null;
- }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+ }).when(mCpuUidUserSysTimeReader).readDelta(
+ any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null, true);
@@ -350,7 +358,7 @@ public class BatteryStatsCpuTimesTest {
assertEquals("Unexpected system cpu time for uid=" + testUids[i],
uidTimesUs[i][1], u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
}
- verify(mKernelUidCpuTimeReader).removeUid(isolatedUid);
+ verify(mCpuUidUserSysTimeReader).removeUid(isolatedUid);
// Add an isolated uid mapping and repeat the test.
@@ -361,13 +369,14 @@ public class BatteryStatsCpuTimesTest {
{9379, 3332409833484l}, {493247, 723234}, {3247819, 123348}
};
doAnswer(invocation -> {
- final KernelUidCpuTimeReader.Callback callback =
- (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
+ (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuTime(testUids[i], deltasUs[i][0], deltasUs[i][1]);
+ callback.onUidCpuTime(testUids[i], deltasUs[i]);
}
return null;
- }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+ }).when(mCpuUidUserSysTimeReader).readDelta(
+ any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null, true);
@@ -414,15 +423,16 @@ public class BatteryStatsCpuTimesTest {
{12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
};
doAnswer(invocation -> {
- final KernelUidCpuTimeReader.Callback callback =
- (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
+ (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
+ callback.onUidCpuTime(testUids[i], uidTimesUs[i]);
}
// And one for the invalid uid
- callback.onUidCpuTime(invalidUid, 3879, 239);
+ callback.onUidCpuTime(invalidUid, new long[]{3879, 239});
return null;
- }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+ }).when(mCpuUidUserSysTimeReader).readDelta(
+ any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null, true);
@@ -438,7 +448,7 @@ public class BatteryStatsCpuTimesTest {
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
- verify(mKernelUidCpuTimeReader).removeUid(invalidUid);
+ verify(mCpuUidUserSysTimeReader).removeUid(invalidUid);
}
@Test
@@ -462,13 +472,14 @@ public class BatteryStatsCpuTimesTest {
{12, 34}, {3394, 3123}, {7977, 80434}
};
doAnswer(invocation -> {
- final KernelUidCpuTimeReader.Callback callback =
- (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidUserSysTimeReader.Callback<long[]> callback =
+ (KernelCpuUidUserSysTimeReader.Callback<long[]>) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
+ callback.onUidCpuTime(testUids[i], uidTimesUs[i]);
}
return null;
- }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+ }).when(mCpuUidUserSysTimeReader).readDelta(
+ any(KernelCpuUidUserSysTimeReader.Callback.class));
// RUN
final SparseLongArray updatedUids = new SparseLongArray();
@@ -541,14 +552,14 @@ public class BatteryStatsCpuTimesTest {
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelUidCpuFreqTimeReader.Callback callback =
- (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback callback =
+ (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
+ }).when(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false);
@@ -574,14 +585,14 @@ public class BatteryStatsCpuTimesTest {
{43, 3345, 2143, 123, 4554}
};
doAnswer(invocation -> {
- final KernelUidCpuFreqTimeReader.Callback callback =
- (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback callback =
+ (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuFreqTime(testUids[i], deltasMs[i]);
+ callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
+ }).when(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, true);
@@ -624,15 +635,15 @@ public class BatteryStatsCpuTimesTest {
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelUidCpuFreqTimeReader.Callback callback =
- (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback callback =
+ (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
- when(mKernelUidCpuFreqTimeReader.perClusterTimesAvailable()).thenReturn(true);
+ }).when(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
+ when(mCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(true);
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false);
@@ -668,14 +679,14 @@ public class BatteryStatsCpuTimesTest {
{43, 3345, 2143, 123, 4554}
};
doAnswer(invocation -> {
- final KernelUidCpuFreqTimeReader.Callback callback =
- (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback callback =
+ (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuFreqTime(testUids[i], deltasMs[i]);
+ callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
+ }).when(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, true);
@@ -734,15 +745,15 @@ public class BatteryStatsCpuTimesTest {
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelUidCpuFreqTimeReader.Callback callback =
- (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback callback =
+ (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
- when(mKernelUidCpuFreqTimeReader.perClusterTimesAvailable()).thenReturn(true);
+ }).when(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
+ when(mCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(true);
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(partialTimers, true, false);
@@ -824,14 +835,14 @@ public class BatteryStatsCpuTimesTest {
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelUidCpuFreqTimeReader.Callback callback =
- (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback callback =
+ (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
+ }).when(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false);
@@ -857,14 +868,14 @@ public class BatteryStatsCpuTimesTest {
{43, 3345, 2143, 123, 4554, 9374983794839l, 979875}
};
doAnswer(invocation -> {
- final KernelUidCpuFreqTimeReader.Callback callback =
- (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback callback =
+ (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuFreqTime(testUids[i], deltasMs[i]);
+ callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
+ }).when(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, true);
@@ -901,14 +912,14 @@ public class BatteryStatsCpuTimesTest {
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelUidCpuFreqTimeReader.Callback callback =
- (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback callback =
+ (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
+ }).when(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false);
@@ -927,7 +938,7 @@ public class BatteryStatsCpuTimesTest {
assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
}
- verify(mKernelUidCpuFreqTimeReader).removeUid(isolatedUid);
+ verify(mCpuUidFreqTimeReader).removeUid(isolatedUid);
// Add an isolated uid mapping and repeat the test.
@@ -941,14 +952,14 @@ public class BatteryStatsCpuTimesTest {
{43, 3345, 2143, 123, 4554}
};
doAnswer(invocation -> {
- final KernelUidCpuFreqTimeReader.Callback callback =
- (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback callback =
+ (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuFreqTime(testUids[i], deltasMs[i]);
+ callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
+ }).when(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false);
@@ -996,16 +1007,16 @@ public class BatteryStatsCpuTimesTest {
{8, 25, 3, 0, 42}
};
doAnswer(invocation -> {
- final KernelUidCpuFreqTimeReader.Callback callback =
- (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidFreqTimeReader.Callback callback =
+ (KernelCpuUidFreqTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
// And one for the invalid uid
- callback.onUidCpuFreqTime(invalidUid, new long[]{12, 839, 32, 34, 21});
+ callback.onUidCpuTime(invalidUid, new long[]{12, 839, 32, 34, 21});
return null;
- }).when(mKernelUidCpuFreqTimeReader).readDelta(
- any(KernelUidCpuFreqTimeReader.Callback.class));
+ }).when(mCpuUidFreqTimeReader).readDelta(
+ any(KernelCpuUidFreqTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked(null, true, false);
@@ -1022,7 +1033,7 @@ public class BatteryStatsCpuTimesTest {
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
- verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid);
+ verify(mCpuUidFreqTimeReader).removeUid(invalidUid);
}
@Test
@@ -1039,14 +1050,14 @@ public class BatteryStatsCpuTimesTest {
});
final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
doAnswer(invocation -> {
- final KernelUidCpuActiveTimeReader.Callback callback =
- (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidActiveTimeReader.Callback callback =
+ (KernelCpuUidActiveTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuActiveTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mKernelUidCpuActiveTimeReader).readDelta(
- any(KernelUidCpuActiveTimeReader.Callback.class));
+ }).when(mCpuUidActiveTimeReader).readDelta(
+ any(KernelCpuUidActiveTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(true);
@@ -1065,14 +1076,14 @@ public class BatteryStatsCpuTimesTest {
updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[] deltasMs = {43000, 3345000, 2143000, 123000, 4554000};
doAnswer(invocation -> {
- final KernelUidCpuActiveTimeReader.Callback callback =
- (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidActiveTimeReader.Callback callback =
+ (KernelCpuUidActiveTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuActiveTime(testUids[i], deltasMs[i]);
+ callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mKernelUidCpuActiveTimeReader).readDelta(
- any(KernelUidCpuActiveTimeReader.Callback.class));
+ }).when(mCpuUidActiveTimeReader).readDelta(
+ any(KernelCpuUidActiveTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(true);
@@ -1103,16 +1114,16 @@ public class BatteryStatsCpuTimesTest {
});
final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
doAnswer(invocation -> {
- final KernelUidCpuActiveTimeReader.Callback callback =
- (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidActiveTimeReader.Callback callback =
+ (KernelCpuUidActiveTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuActiveTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
// And one for the invalid uid
- callback.onUidCpuActiveTime(invalidUid, 1200L);
+ callback.onUidCpuTime(invalidUid, 1200L);
return null;
- }).when(mKernelUidCpuActiveTimeReader).readDelta(
- any(KernelUidCpuActiveTimeReader.Callback.class));
+ }).when(mCpuUidActiveTimeReader).readDelta(
+ any(KernelCpuUidActiveTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(true);
@@ -1126,7 +1137,7 @@ public class BatteryStatsCpuTimesTest {
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
- verify(mKernelUidCpuActiveTimeReader).removeUid(invalidUid);
+ verify(mCpuUidActiveTimeReader).removeUid(invalidUid);
}
@Test
@@ -1147,14 +1158,14 @@ public class BatteryStatsCpuTimesTest {
{8000, 0}
};
doAnswer(invocation -> {
- final KernelUidCpuClusterTimeReader.Callback callback =
- (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidClusterTimeReader.Callback callback =
+ (KernelCpuUidClusterTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuPolicyTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
return null;
- }).when(mKernelUidCpuClusterTimeReader).readDelta(
- any(KernelUidCpuClusterTimeReader.Callback.class));
+ }).when(mCpuUidClusterTimeReader).readDelta(
+ any(KernelCpuUidClusterTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(true);
@@ -1177,14 +1188,14 @@ public class BatteryStatsCpuTimesTest {
{43000, 3345000}
};
doAnswer(invocation -> {
- final KernelUidCpuClusterTimeReader.Callback callback =
- (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidClusterTimeReader.Callback callback =
+ (KernelCpuUidClusterTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuPolicyTime(testUids[i], deltasMs[i]);
+ callback.onUidCpuTime(testUids[i], deltasMs[i]);
}
return null;
- }).when(mKernelUidCpuClusterTimeReader).readDelta(
- any(KernelUidCpuClusterTimeReader.Callback.class));
+ }).when(mCpuUidClusterTimeReader).readDelta(
+ any(KernelCpuUidClusterTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(true);
@@ -1193,7 +1204,8 @@ public class BatteryStatsCpuTimesTest {
for (int i = 0; i < testUids.length; ++i) {
final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
assertNotNull("No entry for uid=" + testUids[i], u);
- assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], sum(uidTimesMs[i], deltasMs[i]),
+ assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i],
+ sum(uidTimesMs[i], deltasMs[i]),
u.getCpuClusterTimes());
}
}
@@ -1219,16 +1231,16 @@ public class BatteryStatsCpuTimesTest {
{8000, 0}
};
doAnswer(invocation -> {
- final KernelUidCpuClusterTimeReader.Callback callback =
- (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+ final KernelCpuUidClusterTimeReader.Callback callback =
+ (KernelCpuUidClusterTimeReader.Callback) invocation.getArguments()[0];
for (int i = 0; i < testUids.length; ++i) {
- callback.onUidCpuPolicyTime(testUids[i], uidTimesMs[i]);
+ callback.onUidCpuTime(testUids[i], uidTimesMs[i]);
}
// And one for the invalid uid
- callback.onUidCpuPolicyTime(invalidUid, new long[] {400, 1000});
+ callback.onUidCpuTime(invalidUid, new long[]{400, 1000});
return null;
- }).when(mKernelUidCpuClusterTimeReader).readDelta(
- any(KernelUidCpuClusterTimeReader.Callback.class));
+ }).when(mCpuUidClusterTimeReader).readDelta(
+ any(KernelCpuUidClusterTimeReader.Callback.class));
// RUN
mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(true);
@@ -1242,7 +1254,7 @@ public class BatteryStatsCpuTimesTest {
}
assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
mBatteryStatsImpl.getUidStats().get(invalidUid));
- verify(mKernelUidCpuClusterTimeReader).removeUid(invalidUid);
+ verify(mCpuUidClusterTimeReader).removeUid(invalidUid);
}
@Test
@@ -1268,25 +1280,25 @@ public class BatteryStatsCpuTimesTest {
mClocks.realtime = mClocks.uptime = 400_000;
mBatteryStatsImpl.clearPendingRemovedUids();
assertEquals(1, mBatteryStatsImpl.getPendingRemovedUids().size());
- verify(mKernelUidCpuActiveTimeReader).removeUid(1);
- verify(mKernelUidCpuActiveTimeReader).removeUidsInRange(5, 10);
- verify(mKernelUidCpuClusterTimeReader).removeUid(1);
- verify(mKernelUidCpuClusterTimeReader).removeUidsInRange(5, 10);
- verify(mKernelUidCpuFreqTimeReader).removeUid(1);
- verify(mKernelUidCpuFreqTimeReader).removeUidsInRange(5, 10);
- verify(mKernelUidCpuTimeReader).removeUid(1);
- verify(mKernelUidCpuTimeReader).removeUidsInRange(5, 10);
+ verify(mCpuUidActiveTimeReader).removeUid(1);
+ verify(mCpuUidActiveTimeReader).removeUidsInRange(5, 10);
+ verify(mCpuUidClusterTimeReader).removeUid(1);
+ verify(mCpuUidClusterTimeReader).removeUidsInRange(5, 10);
+ verify(mCpuUidFreqTimeReader).removeUid(1);
+ verify(mCpuUidFreqTimeReader).removeUidsInRange(5, 10);
+ verify(mCpuUidUserSysTimeReader).removeUid(1);
+ verify(mCpuUidUserSysTimeReader).removeUidsInRange(5, 10);
mClocks.realtime = mClocks.uptime = 800_000;
mBatteryStatsImpl.clearPendingRemovedUids();
assertEquals(0, mBatteryStatsImpl.getPendingRemovedUids().size());
- verify(mKernelUidCpuActiveTimeReader).removeUid(100);
- verify(mKernelUidCpuClusterTimeReader).removeUid(100);
- verify(mKernelUidCpuFreqTimeReader).removeUid(100);
- verify(mKernelUidCpuTimeReader).removeUid(100);
+ verify(mCpuUidActiveTimeReader).removeUid(100);
+ verify(mCpuUidClusterTimeReader).removeUid(100);
+ verify(mCpuUidFreqTimeReader).removeUid(100);
+ verify(mCpuUidUserSysTimeReader).removeUid(100);
- verifyNoMoreInteractions(mKernelUidCpuActiveTimeReader, mKernelUidCpuClusterTimeReader,
- mKernelUidCpuFreqTimeReader, mKernelUidCpuTimeReader);
+ verifyNoMoreInteractions(mCpuUidActiveTimeReader, mCpuUidClusterTimeReader,
+ mCpuUidFreqTimeReader, mCpuUidUserSysTimeReader);
}
private void updateTimeBasesLocked(boolean unplugged, int screenState,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index dc9367557f9f..077182921118 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -39,6 +39,7 @@ import android.view.Display;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.util.ArrayUtils;
import org.junit.Before;
@@ -54,7 +55,7 @@ import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
public class BatteryStatsImplTest {
@Mock
- private KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
+ private KernelCpuUidFreqTimeReader mKernelUidCpuFreqTimeReader;
@Mock
private KernelSingleUidTimeReader mKernelSingleUidTimeReader;
@@ -67,7 +68,7 @@ public class BatteryStatsImplTest {
when(mKernelUidCpuFreqTimeReader.allUidTimesAvailable()).thenReturn(true);
when(mKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
mBatteryStatsImpl = new MockBatteryStatsImpl()
- .setKernelUidCpuFreqTimeReader(mKernelUidCpuFreqTimeReader)
+ .setKernelCpuUidFreqTimeReader(mKernelUidCpuFreqTimeReader)
.setKernelSingleUidTimeReader(mKernelSingleUidTimeReader);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index d69e1d131731..a6329298b0f9 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -38,7 +38,6 @@ import org.junit.runners.Suite;
BatteryStatsTimerTest.class,
BatteryStatsUidTest.class,
BatteryStatsUserLifecycleTests.class,
- KernelCpuProcReaderTest.class,
KernelCpuProcStringReaderTest.class,
KernelCpuUidActiveTimeReaderTest.class,
KernelCpuUidClusterTimeReaderTest.class,
@@ -46,9 +45,6 @@ import org.junit.runners.Suite;
KernelCpuUidUserSysTimeReaderTest.class,
KernelMemoryBandwidthStatsTest.class,
KernelSingleUidTimeReaderTest.class,
- KernelUidCpuFreqTimeReaderTest.class,
- KernelUidCpuActiveTimeReaderTest.class,
- KernelUidCpuClusterTimeReaderTest.class,
KernelWakelockReaderTest.class,
LongSamplingCounterTest.class,
LongSamplingCounterArrayTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
deleted file mode 100644
index a25a74890029..000000000000
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.os;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.os.FileUtils;
-import android.os.SystemClock;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.file.Files;
-import java.util.Arrays;
-import java.util.Random;
-
-/**
- * Test class for {@link KernelCpuProcReader}.
- *
- * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReaderTest
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class KernelCpuProcReaderTest {
-
- private File mRoot;
- private File mTestDir;
- private File mTestFile;
- private Random mRand = new Random();
-
- private KernelCpuProcReader mKernelCpuProcReader;
-
- private Context getContext() {
- return InstrumentationRegistry.getContext();
- }
-
- @Before
- public void setUp() {
- mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
- mRoot = getContext().getFilesDir();
- mTestFile = new File(mTestDir, "test.file");
- mKernelCpuProcReader = new KernelCpuProcReader(mTestFile.getAbsolutePath());
- }
-
- @After
- public void tearDown() throws Exception {
- FileUtils.deleteContents(mTestDir);
- FileUtils.deleteContents(mRoot);
- }
-
-
- /**
- * Tests that reading will return null if the file does not exist.
- */
- @Test
- public void testReadInvalidFile() throws Exception {
- assertEquals(null, mKernelCpuProcReader.readBytes());
- }
-
- /**
- * Tests that reading will always return null after 5 failures.
- */
- @Test
- public void testReadErrorsLimit() throws Exception {
- mKernelCpuProcReader.setThrottleInterval(0);
- for (int i = 0; i < 3; i++) {
- assertNull(mKernelCpuProcReader.readBytes());
- SystemClock.sleep(50);
- }
-
- final byte[] data = new byte[1024];
- mRand.nextBytes(data);
- try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
- os.write(data);
- }
- assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
-
- assertTrue(mTestFile.delete());
- for (int i = 0; i < 3; i++) {
- assertNull(mKernelCpuProcReader.readBytes());
- SystemClock.sleep(50);
- }
- try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
- os.write(data);
- }
- assertNull(mKernelCpuProcReader.readBytes());
- }
-
- /**
- * Tests reading functionality.
- */
- @Test
- public void testSimpleRead() throws Exception {
- final byte[] data = new byte[1024];
- mRand.nextBytes(data);
- try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
- os.write(data);
- }
- assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
- }
-
- /**
- * Tests multiple reading functionality.
- */
- @Test
- public void testMultipleRead() throws Exception {
- mKernelCpuProcReader.setThrottleInterval(0);
- for (int i = 0; i < 100; i++) {
- final byte[] data = new byte[mRand.nextInt(102400) + 4];
- mRand.nextBytes(data);
- try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
- os.write(data);
- }
- assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
- assertTrue(mTestFile.delete());
- }
- }
-
- /**
- * Tests reading with resizing.
- */
- @Test
- public void testReadWithResize() throws Exception {
- final byte[] data = new byte[128001];
- mRand.nextBytes(data);
- try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
- os.write(data);
- }
- assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
- }
-
- /**
- * Tests that reading a file over the limit (1MB) will return null.
- */
- @Test
- public void testReadOverLimit() throws Exception {
- final byte[] data = new byte[1228800];
- mRand.nextBytes(data);
- try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
- os.write(data);
- }
- assertNull(mKernelCpuProcReader.readBytes());
- }
-
- /**
- * Tests throttling. Deleting underlying file should not affect cache.
- */
- @Test
- public void testThrottle() throws Exception {
- mKernelCpuProcReader.setThrottleInterval(3000);
- final byte[] data = new byte[20001];
- mRand.nextBytes(data);
- try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
- os.write(data);
- }
- assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
- assertTrue(mTestFile.delete());
- for (int i = 0; i < 5; i++) {
- assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
- SystemClock.sleep(10);
- }
- SystemClock.sleep(5000);
- assertNull(mKernelCpuProcReader.readBytes());
- }
-
- private byte[] toArray(ByteBuffer buffer) {
- assertNotNull(buffer);
- byte[] arr = new byte[buffer.remaining()];
- buffer.get(arr);
- return arr;
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
index 7a316056429a..cbd2ba4eeabc 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -299,9 +299,10 @@ public class KernelCpuProcStringReaderTest {
assertTrue(mTestFile.delete());
try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
w.write(data1);
- modify.countDown();
} catch (Throwable e) {
errs.add(e);
+ } finally {
+ modify.countDown();
}
}, 600, TimeUnit.MILLISECONDS);
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
deleted file mode 100644
index 12f6c188f18f..000000000000
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
+++ /dev/null
@@ -1,260 +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.
- */
-
-package com.android.internal.os;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Random;
-
-/**
- * Test class for {@link KernelUidCpuActiveTimeReader}.
- *
- * To run it:
- * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuActiveTimeReaderTest
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class KernelUidCpuActiveTimeReaderTest {
- @Mock
- private KernelCpuProcReader mProcReader;
- @Mock
- private KernelUidCpuActiveTimeReader.Callback mCallback;
- private KernelUidCpuActiveTimeReader mReader;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mReader = new KernelUidCpuActiveTimeReader(mProcReader);
- mReader.setThrottleInterval(0);
- }
-
- @Test
- public void testReadDelta() {
- final int cores = 8;
- final int[] uids = {1, 22, 333, 4444, 5555};
-
- final long[][] times = increaseTime(new long[uids.length][cores]);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length; i++) {
- verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
- }
- verifyNoMoreInteractions(mCallback);
-
- // Verify that a second call will only return deltas.
- Mockito.reset(mCallback);
- final long[][] times1 = increaseTime(times);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length; i++) {
- verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times1[i], times[i])));
- }
- verifyNoMoreInteractions(mCallback);
-
- // Verify that there won't be a callback if the proc file values didn't change.
- Mockito.reset(mCallback);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1));
- mReader.readDelta(mCallback);
- verifyNoMoreInteractions(mCallback);
-
- // Verify that calling with a null callback doesn't result in any crashes
- Mockito.reset(mCallback);
- final long[][] times2 = increaseTime(times1);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times2));
- mReader.readDelta(null);
-
- // Verify that the readDelta call will only return deltas when
- // the previous call had null callback.
- Mockito.reset(mCallback);
- final long[][] times3 = increaseTime(times2);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times3));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length; ++i) {
- verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i])));
- }
- verifyNoMoreInteractions(mCallback);
- }
-
- @Test
- public void testReadAbsolute() {
- final int cores = 8;
- final int[] uids = {1, 22, 333, 4444, 5555};
-
- final long[][] times = increaseTime(new long[uids.length][cores]);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times));
- mReader.readAbsolute(mCallback);
- for (int i = 0; i < uids.length; i++) {
- verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
- }
- verifyNoMoreInteractions(mCallback);
-
- // Verify that a second call still returns absolute values
- Mockito.reset(mCallback);
- final long[][] times1 = increaseTime(times);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1));
- mReader.readAbsolute(mCallback);
- for (int i = 0; i < uids.length; i++) {
- verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times1[i]));
- }
- verifyNoMoreInteractions(mCallback);
- }
-
- @Test
- public void testReadDelta_malformedData() {
- final int cores = 8;
- final int[] uids = {1, 22, 333, 4444, 5555};
- final long[][] times = increaseTime(new long[uids.length][cores]);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length; i++) {
- verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
- }
- verifyNoMoreInteractions(mCallback);
-
- // Verify that there is no callback if subsequent call is in wrong format.
- Mockito.reset(mCallback);
- final long[][] times1 = increaseTime(times);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1).putInt(0, 5));
- mReader.readDelta(mCallback);
- verifyNoMoreInteractions(mCallback);
-
- // Verify that the internal state was not modified if the given core count does not match
- // the following # of entries.
- Mockito.reset(mCallback);
- final long[][] times2 = increaseTime(times);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times2));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length; i++) {
- verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times2[i], times[i])));
- }
- verifyNoMoreInteractions(mCallback);
-
- // Verify that there is no callback if any value in the proc file is -ve.
- Mockito.reset(mCallback);
- final long[][] times3 = increaseTime(times2);
- times3[uids.length - 1][cores - 1] *= -1;
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times3));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length - 1; ++i) {
- verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i])));
- }
- verifyNoMoreInteractions(mCallback);
-
- // Verify that the internal state was not modified when the proc file had -ve value.
- Mockito.reset(mCallback);
- for (int i = 0; i < cores; i++) {
- times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 2520;
- }
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times3));
- mReader.readDelta(mCallback);
- verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1],
- getTotal(subtract(times3[uids.length - 1], times2[uids.length - 1])));
- verifyNoMoreInteractions(mCallback);
-
- // Verify that there is no callback if the values in the proc file are decreased.
- Mockito.reset(mCallback);
- final long[][] times4 = increaseTime(times3);
- System.arraycopy(times3[uids.length - 1], 0, times4[uids.length - 1], 0, cores);
- times4[uids.length - 1][cores - 1] -= 100;
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times4));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length - 1; ++i) {
- verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times4[i], times3[i])));
- }
- verifyNoMoreInteractions(mCallback);
-
- // Verify that the internal state was not modified when the proc file had decreased values.
- Mockito.reset(mCallback);
- for (int i = 0; i < cores; i++) {
- times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 2520;
- }
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times4));
- mReader.readDelta(mCallback);
- verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1],
- getTotal(subtract(times4[uids.length - 1], times3[uids.length - 1])));
- verifyNoMoreInteractions(mCallback);
- }
-
- private long[] subtract(long[] a1, long[] a2) {
- long[] val = new long[a1.length];
- for (int i = 0; i < val.length; ++i) {
- val[i] = a1[i] - a2[i];
- }
- return val;
- }
-
- /**
- * Unit of original and return value is 10ms. What's special about 2520? 2520 is LCM of 1, 2, 3,
- * ..., 10. So that when wedivide shared cpu time by concurrent thread count, we always get a
- * nice integer, avoiding rounding errors.
- */
- private long[][] increaseTime(long[][] original) {
- long[][] newTime = new long[original.length][original[0].length];
- Random rand = new Random();
- for (int i = 0; i < original.length; i++) {
- for (int j = 0; j < original[0].length; j++) {
- newTime[i][j] = original[i][j] + rand.nextInt(1000) * 2520 + 2520;
- }
- }
- return newTime;
- }
-
- // Unit of times is 10ms
- private long getTotal(long[] times) {
- long sum = 0;
- for (int i = 0; i < times.length; i++) {
- sum += times[i] * 10 / (i + 1);
- }
- return sum;
- }
-
- /**
- * Format uids and times (in 10ms) into the following format:
- * [n, uid0, time0a, time0b, ..., time0n,
- * uid1, time1a, time1b, ..., time1n,
- * uid2, time2a, time2b, ..., time2n, etc.]
- * where n is the total number of cpus (num_possible_cpus)
- */
- private ByteBuffer getUidTimesBytes(int[] uids, long[][] times) {
- int size = (1 + uids.length * (times[0].length + 1)) * 4;
- ByteBuffer buf = ByteBuffer.allocate(size);
- buf.order(ByteOrder.nativeOrder());
- buf.putInt(times[0].length);
- for (int i = 0; i < uids.length; i++) {
- buf.putInt(uids[i]);
- for (int j = 0; j < times[i].length; j++) {
- buf.putInt((int) times[i][j]);
- }
- }
- buf.flip();
- return buf.order(ByteOrder.nativeOrder());
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
deleted file mode 100644
index 532f337fe2e1..000000000000
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
+++ /dev/null
@@ -1,326 +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.
- */
-
-package com.android.internal.os;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.when;
-
-import android.util.SparseArray;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-import java.util.Random;
-
-/**
- * Test class for {@link KernelUidCpuClusterTimeReader}.
- *
- * To run it:
- * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuClusterTimeReaderTest
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class KernelUidCpuClusterTimeReaderTest {
- @Mock
- private KernelCpuProcReader mProcReader;
- private KernelUidCpuClusterTimeReader mReader;
- private VerifiableCallback mCallback;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mReader = new KernelUidCpuClusterTimeReader(mProcReader);
- mCallback = new VerifiableCallback();
- mReader.setThrottleInterval(0);
- }
-
- @Test
- public void testReadDelta() throws Exception {
- VerifiableCallback cb = new VerifiableCallback();
- final int cores = 6;
- final int[] clusters = {2, 4};
- final int[] uids = {1, 22, 333, 4444, 5555};
-
- // Verify initial call
- final long[][] times = increaseTime(new long[uids.length][cores]);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times));
- mReader.readDelta(cb);
- for (int i = 0; i < uids.length; i++) {
- cb.verify(uids[i], getTotal(clusters, times[i]));
- }
- cb.verifyNoMoreInteractions();
-
- // Verify that a second call will only return deltas.
- cb.clear();
- Mockito.reset(mProcReader);
- final long[][] times1 = increaseTime(times);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1));
- mReader.readDelta(cb);
- for (int i = 0; i < uids.length; i++) {
- cb.verify(uids[i], getTotal(clusters, subtract(times1[i], times[i])));
- }
- cb.verifyNoMoreInteractions();
-
- // Verify that there won't be a callback if the proc file values didn't change.
- cb.clear();
- Mockito.reset(mProcReader);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1));
- mReader.readDelta(cb);
- cb.verifyNoMoreInteractions();
-
- // Verify that calling with a null callback doesn't result in any crashes
- Mockito.reset(mProcReader);
- final long[][] times2 = increaseTime(times1);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times2));
- mReader.readDelta(null);
-
- // Verify that the readDelta call will only return deltas when
- // the previous call had null callback.
- cb.clear();
- Mockito.reset(mProcReader);
- final long[][] times3 = increaseTime(times2);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times3));
- mReader.readDelta(cb);
- for (int i = 0; i < uids.length; i++) {
- cb.verify(uids[i], getTotal(clusters, subtract(times3[i], times2[i])));
- }
- cb.verifyNoMoreInteractions();
-
- }
-
- @Test
- public void testReadAbsolute() throws Exception {
- VerifiableCallback cb = new VerifiableCallback();
- final int cores = 6;
- final int[] clusters = {2, 4};
- final int[] uids = {1, 22, 333, 4444, 5555};
-
- // Verify return absolute value
- final long[][] times = increaseTime(new long[uids.length][cores]);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times));
- mReader.readAbsolute(cb);
- for (int i = 0; i < uids.length; i++) {
- cb.verify(uids[i], getTotal(clusters, times[i]));
- }
- cb.verifyNoMoreInteractions();
-
- // Verify that a second call should return the same absolute value
- cb.clear();
- Mockito.reset(mProcReader);
- final long[][] times1 = increaseTime(times);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1));
- mReader.readAbsolute(cb);
- for (int i = 0; i < uids.length; i++) {
- cb.verify(uids[i], getTotal(clusters, times1[i]));
- }
- cb.verifyNoMoreInteractions();
- }
-
- @Test
- public void testReadDelta_malformedData() throws Exception {
- final int cores = 6;
- final int[] clusters = {2, 4};
- final int[] uids = {1, 22, 333, 4444, 5555};
-
- // Verify initial call
- final long[][] times = increaseTime(new long[uids.length][cores]);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length; i++) {
- mCallback.verify(uids[i], getTotal(clusters, times[i]));
- }
- mCallback.verifyNoMoreInteractions();
-
- // Verify that there is no callback if a call has wrong format
- mCallback.clear();
- Mockito.reset(mProcReader);
- final long[][] temp = increaseTime(times);
- final long[][] times1 = new long[uids.length][];
- for (int i = 0; i < temp.length; i++) {
- times1[i] = Arrays.copyOfRange(temp[i], 0, 4);
- }
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1));
- mReader.readDelta(mCallback);
- mCallback.verifyNoMoreInteractions();
-
- // Verify that the internal state was not modified if the given core count does not match
- // the following # of entries.
- mCallback.clear();
- Mockito.reset(mProcReader);
- final long[][] times2 = increaseTime(times);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times2));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length; i++) {
- mCallback.verify(uids[i], getTotal(clusters, subtract(times2[i], times[i])));
- }
- mCallback.verifyNoMoreInteractions();
-
- // Verify that there is no callback if any value in the proc file is -ve.
- mCallback.clear();
- Mockito.reset(mProcReader);
- final long[][] times3 = increaseTime(times2);
- times3[uids.length - 1][cores - 1] *= -1;
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times3));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length - 1; i++) {
- mCallback.verify(uids[i], getTotal(clusters, subtract(times3[i], times2[i])));
- }
- mCallback.verifyNoMoreInteractions();
-
- // Verify that the internal state was not modified when the proc file had -ve value.
- mCallback.clear();
- Mockito.reset(mProcReader);
- for (int i = 0; i < cores; i++) {
- times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 2520;
- }
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times3));
- mReader.readDelta(mCallback);
- mCallback.verify(uids[uids.length - 1],
- getTotal(clusters, subtract(times3[uids.length - 1], times2[uids.length - 1])));
-
- // Verify that there is no callback if the values in the proc file are decreased.
- mCallback.clear();
- Mockito.reset(mProcReader);
- final long[][] times4 = increaseTime(times3);
- System.arraycopy(times3[uids.length - 1], 0, times4[uids.length - 1], 0, cores);
- times4[uids.length - 1][cores - 1] -= 100;
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times4));
- mReader.readDelta(mCallback);
- for (int i = 0; i < uids.length - 1; i++) {
- mCallback.verify(uids[i], getTotal(clusters, subtract(times4[i], times3[i])));
- }
- mCallback.verifyNoMoreInteractions();
-
- // Verify that the internal state was not modified when the proc file had decreased values.
- mCallback.clear();
- Mockito.reset(mProcReader);
- for (int i = 0; i < cores; i++) {
- times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 2520;
- }
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times4));
- mReader.readDelta(mCallback);
- mCallback.verify(uids[uids.length - 1],
- getTotal(clusters, subtract(times3[uids.length - 1], times2[uids.length - 1])));
- mCallback.verifyNoMoreInteractions();
- }
-
-
- private long[] subtract(long[] a1, long[] a2) {
- long[] val = new long[a1.length];
- for (int i = 0; i < val.length; ++i) {
- val[i] = a1[i] - a2[i];
- }
- return val;
- }
-
- /**
- * Unit is 10ms. What's special about 2520? 2520 is LCM of 1, 2, 3, ..., 10. So that when we
- * divide shared cpu time by concurrent thread count, we always get a nice integer, avoiding
- * rounding errors.
- */
- private long[][] increaseTime(long[][] original) {
- long[][] newTime = new long[original.length][original[0].length];
- Random rand = new Random();
- for (int i = 0; i < original.length; i++) {
- for (int j = 0; j < original[0].length; j++) {
- newTime[i][j] = original[i][j] + rand.nextInt(1000) * 2520 + 2520;
- }
- }
- return newTime;
- }
-
- // Format an array of cluster times according to the algorithm in KernelUidCpuClusterTimeReader
- private long[] getTotal(int[] cluster, long[] times) {
- int core = 0;
- long[] sumTimes = new long[cluster.length];
- for (int i = 0; i < cluster.length; i++) {
- double sum = 0;
- for (int j = 0; j < cluster[i]; j++) {
- sum += (double) times[core++] * 10 / (j + 1);
- }
- sumTimes[i] = (long) sum;
- }
- return sumTimes;
- }
-
- private class VerifiableCallback implements KernelUidCpuClusterTimeReader.Callback {
-
- SparseArray<long[]> mData = new SparseArray<>();
- int count = 0;
-
- public void verify(int uid, long[] cpuClusterTimeMs) {
- long[] array = mData.get(uid);
- assertNotNull(array);
- assertArrayEquals(cpuClusterTimeMs, array);
- count++;
- }
-
- public void clear() {
- mData.clear();
- count = 0;
- }
-
- @Override
- public void onUidCpuPolicyTime(int uid, long[] cpuClusterTimeMs) {
- long[] array = new long[cpuClusterTimeMs.length];
- System.arraycopy(cpuClusterTimeMs, 0, array, 0, array.length);
- mData.put(uid, array);
- }
-
- public void verifyNoMoreInteractions() {
- assertEquals(mData.size(), count);
- }
- }
-
- /**
- * Format uids and times (in 10ms) into the following format:
- * [n, x0, ..., xn, uid0, time0a, time0b, ..., time0n,
- * uid1, time1a, time1b, ..., time1n,
- * uid2, time2a, time2b, ..., time2n, etc.]
- * where n is the number of policies
- * xi is the number cpus on a particular policy
- */
- private ByteBuffer getUidTimesBytes(int[] uids, int[] clusters, long[][] times) {
- int size = (1 + clusters.length + uids.length * (times[0].length + 1)) * 4;
- ByteBuffer buf = ByteBuffer.allocate(size);
- buf.order(ByteOrder.nativeOrder());
- buf.putInt(clusters.length);
- for (int i = 0; i < clusters.length; i++) {
- buf.putInt(clusters[i]);
- }
- for (int i = 0; i < uids.length; i++) {
- buf.putInt(uids[i]);
- for (int j = 0; j < times[i].length; j++) {
- buf.putInt((int) (times[i][j]));
- }
- }
- buf.flip();
- return buf.order(ByteOrder.nativeOrder());
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
deleted file mode 100644
index 6d2980b8bed0..000000000000
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
+++ /dev/null
@@ -1,331 +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.
- */
-
-package com.android.internal.os;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import android.util.SparseArray;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.io.BufferedReader;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-
-/**
- * Test class for {@link KernelUidCpuFreqTimeReader}.
- *
- * To run the tests, use
- *
- * runtest -c com.android.internal.os.KernelUidCpuFreqTimeReaderTest frameworks-core
- *
- * or the following steps:
- *
- * Build: m FrameworksCoreTests
- * Install: adb install -r \
- * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
- * Run: adb shell am instrument -e class com.android.internal.os.KernelUidCpuFreqTimeReaderTest -w \
- * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
- *
- * or
- *
- * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuFreqTimeReaderTest
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class KernelUidCpuFreqTimeReaderTest {
- @Mock
- private BufferedReader mBufferedReader;
- @Mock
- private KernelUidCpuFreqTimeReader.Callback mCallback;
- @Mock
- private PowerProfile mPowerProfile;
- @Mock
- private KernelCpuProcReader mProcReader;
-
- private KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mKernelUidCpuFreqTimeReader = new KernelUidCpuFreqTimeReader(mProcReader);
- mKernelUidCpuFreqTimeReader.setThrottleInterval(0);
- }
-
- @Test
- public void testReadFreqs_perClusterTimesNotAvailable() throws Exception {
- final long[][] freqs = {
- {1, 12, 123, 1234},
- {1, 12, 123, 23, 123, 1234, 12345, 123456},
- {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345},
- {1, 12, 123, 23, 2345, 234567}
- };
- final int[] numClusters = {2, 2, 3, 1};
- final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
- for (int i = 0; i < freqs.length; ++i) {
- setCpuClusterFreqs(numClusters[i], numFreqs[i]);
- when(mBufferedReader.readLine()).thenReturn(getFreqsLine(freqs[i]));
- long[] actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(
- mBufferedReader, mPowerProfile);
- assertArrayEquals(freqs[i], actualFreqs);
- verifyZeroInteractions(mCallback);
- final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
- Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
- assertFalse(errMsg, mKernelUidCpuFreqTimeReader.perClusterTimesAvailable());
-
- // Verify that a second call won't read the proc file again
- Mockito.reset(mBufferedReader);
- actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mPowerProfile);
- assertArrayEquals(freqs[i], actualFreqs);
- assertFalse(errMsg, mKernelUidCpuFreqTimeReader.perClusterTimesAvailable());
-
- // Prepare for next iteration
- Mockito.reset(mBufferedReader, mPowerProfile);
- }
- }
-
- @Test
- public void testReadFreqs_perClusterTimesAvailable() throws Exception {
- final long[][] freqs = {
- {1, 12, 123, 1234},
- {1, 12, 123, 23, 123, 1234, 12345, 123456},
- {1, 12, 123, 23, 123, 1234, 12345, 123456, 12, 123, 12345, 1234567}
- };
- final int[] numClusters = {1, 2, 3};
- final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
- for (int i = 0; i < freqs.length; ++i) {
- setCpuClusterFreqs(numClusters[i], numFreqs[i]);
- when(mBufferedReader.readLine()).thenReturn(getFreqsLine(freqs[i]));
- long[] actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(
- mBufferedReader, mPowerProfile);
- assertArrayEquals(freqs[i], actualFreqs);
- verifyZeroInteractions(mCallback);
- final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
- Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
- assertTrue(errMsg, mKernelUidCpuFreqTimeReader.perClusterTimesAvailable());
-
- // Verify that a second call won't read the proc file again
- Mockito.reset(mBufferedReader);
- actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mPowerProfile);
- assertArrayEquals(freqs[i], actualFreqs);
- assertTrue(errMsg, mKernelUidCpuFreqTimeReader.perClusterTimesAvailable());
-
- // Prepare for next iteration
- Mockito.reset(mBufferedReader, mPowerProfile);
- }
- }
-
- @Test
- public void testReadDelta_Binary() throws Exception {
- VerifiableCallback cb = new VerifiableCallback();
- final long[] freqs = {110, 123, 145, 167, 289, 997};
- final int[] uids = {1, 22, 333, 444, 555};
- final long[][] times = new long[uids.length][freqs.length];
- for (int i = 0; i < uids.length; ++i) {
- for (int j = 0; j < freqs.length; ++j) {
- times[i][j] = uids[i] * freqs[j] * 10;
- }
- }
- when(mBufferedReader.readLine()).thenReturn(getFreqsLine(freqs));
- long[] actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mBufferedReader, mPowerProfile);
-
- assertArrayEquals(freqs, actualFreqs);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times));
- mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
- for (int i = 0; i < uids.length; ++i) {
- cb.verify(uids[i], times[i]);
- }
- cb.verifyNoMoreInteractions();
-
- // Verify that a second call will only return deltas.
- cb.clear();
- Mockito.reset(mProcReader);
- final long[][] newTimes1 = new long[uids.length][freqs.length];
- for (int i = 0; i < uids.length; ++i) {
- for (int j = 0; j < freqs.length; ++j) {
- newTimes1[i][j] = times[i][j] + (uids[i] + freqs[j]) * 50;
- }
- }
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes1));
- mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
- for (int i = 0; i < uids.length; ++i) {
- cb.verify(uids[i], subtract(newTimes1[i], times[i]));
- }
- cb.verifyNoMoreInteractions();
-
- // Verify that there won't be a callback if the proc file values didn't change.
- cb.clear();
- Mockito.reset(mProcReader);
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes1));
- mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
- cb.verifyNoMoreInteractions();
-
- // Verify that calling with a null callback doesn't result in any crashes
- cb.clear();
- Mockito.reset(mProcReader);
- final long[][] newTimes2 = new long[uids.length][freqs.length];
- for (int i = 0; i < uids.length; ++i) {
- for (int j = 0; j < freqs.length; ++j) {
- newTimes2[i][j] = newTimes1[i][j] + (uids[i] * freqs[j]) * 30;
- }
- }
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes2));
- mKernelUidCpuFreqTimeReader.readDeltaImpl(null);
- cb.verifyNoMoreInteractions();
-
- // Verify that the readDelta call will only return deltas when
- // the previous call had null callback.
- cb.clear();
- Mockito.reset(mProcReader);
- final long[][] newTimes3 = new long[uids.length][freqs.length];
- for (int i = 0; i < uids.length; ++i) {
- for (int j = 0; j < freqs.length; ++j) {
- newTimes3[i][j] = newTimes2[i][j] + (uids[i] + freqs[j]) * 40;
- }
- }
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes3));
- mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
- for (int i = 0; i < uids.length; ++i) {
- cb.verify(uids[i], subtract(newTimes3[i], newTimes2[i]));
- }
- cb.verifyNoMoreInteractions();
- }
-
- @Test
- public void testReadAbsolute() throws Exception {
- VerifiableCallback cb = new VerifiableCallback();
- final long[] freqs = {110, 123, 145, 167, 289, 997};
- final int[] uids = {1, 22, 333, 444, 555};
- final long[][] times = new long[uids.length][freqs.length];
- for (int i = 0; i < uids.length; ++i) {
- for (int j = 0; j < freqs.length; ++j) {
- times[i][j] = uids[i] * freqs[j] * 10;
- }
- }
- when(mBufferedReader.readLine()).thenReturn(getFreqsLine(freqs));
- long[] actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mBufferedReader, mPowerProfile);
-
- assertArrayEquals(freqs, actualFreqs);
- // Verify that the absolute values are returned
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times));
- mKernelUidCpuFreqTimeReader.readAbsolute(cb);
- for (int i = 0; i < uids.length; ++i) {
- cb.verify(uids[i], times[i]);
- }
- cb.verifyNoMoreInteractions();
-
- // Verify that a second call should still return absolute values
- cb.clear();
- Mockito.reset(mProcReader);
- final long[][] newTimes1 = new long[uids.length][freqs.length];
- for (int i = 0; i < uids.length; ++i) {
- for (int j = 0; j < freqs.length; ++j) {
- newTimes1[i][j] = times[i][j] + (uids[i] + freqs[j]) * 50;
- }
- }
- when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes1));
- mKernelUidCpuFreqTimeReader.readAbsolute(cb);
- for (int i = 0; i < uids.length; ++i) {
- cb.verify(uids[i], newTimes1[i]);
- }
- cb.verifyNoMoreInteractions();
- }
-
- private long[] subtract(long[] a1, long[] a2) {
- long[] val = new long[a1.length];
- for (int i = 0; i < val.length; ++i) {
- val[i] = a1[i] - a2[i];
- }
- return val;
- }
-
- private String getFreqsLine(long[] freqs) {
- final StringBuilder sb = new StringBuilder();
- sb.append("uid:");
- for (int i = 0; i < freqs.length; ++i) {
- sb.append(" " + freqs[i]);
- }
- return sb.toString();
- }
-
- private ByteBuffer getUidTimesBytes(int[] uids, long[][] times) {
- int size = (1 + uids.length + uids.length * times[0].length) * 4;
- ByteBuffer buf = ByteBuffer.allocate(size);
- buf.order(ByteOrder.nativeOrder());
- buf.putInt(times[0].length);
- for (int i = 0; i < uids.length; i++) {
- buf.putInt(uids[i]);
- for (int j = 0; j < times[i].length; j++) {
- buf.putInt((int) (times[i][j] / 10));
- }
- }
- buf.flip();
- return buf.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
- }
-
- private void setCpuClusterFreqs(int numClusters, int... clusterFreqs) {
- assertEquals(numClusters, clusterFreqs.length);
- when(mPowerProfile.getNumCpuClusters()).thenReturn(numClusters);
- for (int i = 0; i < numClusters; ++i) {
- when(mPowerProfile.getNumSpeedStepsInCpuCluster(i)).thenReturn(clusterFreqs[i]);
- }
- }
-
- private class VerifiableCallback implements KernelUidCpuFreqTimeReader.Callback {
-
- SparseArray<long[]> mData = new SparseArray<>();
- int count = 0;
-
- public void verify(int uid, long[] cpuFreqTimeMs) {
- long[] array = mData.get(uid);
- assertNotNull(array);
- assertArrayEquals(cpuFreqTimeMs, array);
- count++;
- }
-
- public void clear() {
- mData.clear();
- count = 0;
- }
-
- @Override
- public void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs) {
- long[] array = new long[cpuFreqTimeMs.length];
- System.arraycopy(cpuFreqTimeMs, 0, array, 0, array.length);
- mData.put(uid, array);
- }
-
- public void verifyNoMoreInteractions() {
- assertEquals(mData.size(), count);
- }
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index c18445e26173..bc0e0a496d80 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -21,6 +21,10 @@ import android.os.Looper;
import android.util.SparseIntArray;
import com.android.internal.location.gnssmetrics.GnssMetrics;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import java.util.ArrayList;
import java.util.Queue;
@@ -43,13 +47,14 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
setExternalStatsSyncLocked(new DummyExternalStatsSync());
- for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
- mGpsSignalQualityTimer[i] = new StopwatchTimer(clocks, null, -1000-i, null,
- mOnBatteryTimeBase);
+ for (int i = 0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i] = new StopwatchTimer(clocks, null, -1000 - i, null,
+ mOnBatteryTimeBase);
}
// A no-op handler.
- mHandler = new Handler(Looper.getMainLooper()) {};
+ mHandler = new Handler(Looper.getMainLooper()) {
+ };
}
MockBatteryStatsImpl() {
@@ -95,23 +100,26 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
return this;
}
- public MockBatteryStatsImpl setKernelUidCpuFreqTimeReader(KernelUidCpuFreqTimeReader reader) {
- mKernelUidCpuFreqTimeReader = reader;
+ public MockBatteryStatsImpl setKernelCpuUidFreqTimeReader(KernelCpuUidFreqTimeReader reader) {
+ mCpuUidFreqTimeReader = reader;
return this;
}
- public MockBatteryStatsImpl setKernelUidCpuActiveTimeReader(KernelUidCpuActiveTimeReader reader) {
- mKernelUidCpuActiveTimeReader = reader;
+ public MockBatteryStatsImpl setKernelCpuUidActiveTimeReader(
+ KernelCpuUidActiveTimeReader reader) {
+ mCpuUidActiveTimeReader = reader;
return this;
}
- public MockBatteryStatsImpl setKernelUidCpuClusterTimeReader(KernelUidCpuClusterTimeReader reader) {
- mKernelUidCpuClusterTimeReader = reader;
+ public MockBatteryStatsImpl setKernelCpuUidClusterTimeReader(
+ KernelCpuUidClusterTimeReader reader) {
+ mCpuUidClusterTimeReader = reader;
return this;
}
- public MockBatteryStatsImpl setKernelUidCpuTimeReader(KernelUidCpuTimeReader reader) {
- mKernelUidCpuTimeReader = reader;
+ public MockBatteryStatsImpl setKernelCpuUidUserSysTimeReader(
+ KernelCpuUidUserSysTimeReader reader) {
+ mCpuUidUserSysTimeReader = reader;
return this;
}
diff --git a/core/tests/coretests/src/com/android/server/wm/test/filters/CoreTestsFilter.java b/core/tests/coretests/src/com/android/server/wm/test/filters/CoreTestsFilter.java
new file mode 100644
index 000000000000..1a81c2c9fd41
--- /dev/null
+++ b/core/tests/coretests/src/com/android/server/wm/test/filters/CoreTestsFilter.java
@@ -0,0 +1,48 @@
+/*
+ * 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.wm.test.filters;
+
+import android.os.Bundle;
+
+import com.android.test.filters.SelectTest;
+
+/**
+ * JUnit test filter that select Window Manager Service related tests from FrameworksCoreTests.
+ *
+ * <p>Use this filter when running FrameworksCoreTests as
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.server.wm.test.filters.CoreTestsFilter \
+ * -e selectTest_verbose true \
+ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ */
+public final class CoreTestsFilter extends SelectTest {
+
+ private static final String[] SELECTED_CORE_TESTS = {
+ "android.app.servertransaction.", // all tests under the package.
+ "android.view.DisplayCutoutTest",
+ "android.view.InsetsControllerTest",
+ "android.view.InsetsSourceTest",
+ "android.view.InsetsSourceConsumerTest",
+ "android.view.InsetsStateTest",
+ };
+
+ public CoreTestsFilter(Bundle testArgs) {
+ super(addSelectTest(testArgs, SELECTED_CORE_TESTS));
+ }
+}
diff --git a/core/tests/hdmitests/Android.mk b/core/tests/hdmitests/Android.mk
index 2ca31a6a240a..f155feba0588 100644
--- a/core/tests/hdmitests/Android.mk
+++ b/core/tests/hdmitests/Android.mk
@@ -20,7 +20,7 @@ LOCAL_MODULE_TAGS := tests
# Include all test java files
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules frameworks-base-testutils
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules frameworks-base-testutils truth-prebuilt
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := HdmiCecTests
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiUtilsTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiUtilsTest.java
new file mode 100644
index 000000000000..fdc6b847d055
--- /dev/null
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiUtilsTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 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.hardware.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+/**
+ * Tests for {@link HdmiUtils}.
+ */
+@RunWith(JUnit4.class)
+@SmallTest
+public class HdmiUtilsTest {
+ @Test
+ public void testInvalidAddress() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0, -1))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_UNKNOWN);
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0xFFFF, 0xFFFF))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_UNKNOWN);
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0xFFFFF, 0))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_UNKNOWN);
+ }
+
+ @Test
+ public void testSameAddress() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x1000, 0x1000))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_SAME);
+ }
+
+ @Test
+ public void testDirectlyAbove() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x1000, 0x1200))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE);
+ }
+
+ @Test
+ public void testDirectlyAbove_rootDevice() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x0000, 0x2000))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE);
+ }
+
+ @Test
+ public void testDirectlyAbove_leafDevice() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x1240, 0x1245))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_DIRECTLY_ABOVE);
+ }
+
+ @Test
+ public void testAbove() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x1000, 0x1210))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_ABOVE);
+ }
+
+ @Test
+ public void testAbove_rootDevice() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x0000, 0x1200))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_ABOVE);
+ }
+
+ @Test
+ public void testDirectlyBelow() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x2250, 0x2200))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_DIRECTLY_BELOW);
+ }
+
+ @Test
+ public void testDirectlyBelow_rootDevice() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x5000, 0x0000))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_DIRECTLY_BELOW);
+ }
+
+ @Test
+ public void testDirectlyBelow_leafDevice() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x3249, 0x3240))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_DIRECTLY_BELOW);
+ }
+
+ @Test
+ public void testBelow() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x5143, 0x5100))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_BELOW);
+ }
+
+ @Test
+ public void testBelow_rootDevice() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x3420, 0x0000))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_BELOW);
+ }
+
+ @Test
+ public void testSibling() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x4000, 0x5000))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_SIBLING);
+ }
+
+ @Test
+ public void testSibling_leafDevice() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x798A, 0x798F))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_SIBLING);
+ }
+
+ @Test
+ public void testDifferentBranch() {
+ assertThat(HdmiUtils.getHdmiAddressRelativePosition(0x798A, 0x7970))
+ .isEqualTo(HdmiUtils.HDMI_RELATIVE_POSITION_DIFFERENT_BRANCH);
+ }
+
+ @Test
+ public void isValidPysicalAddress_true() {
+ assertThat(HdmiUtils.isValidPhysicalAddress(0)).isTrue();
+ assertThat(HdmiUtils.isValidPhysicalAddress(0xFFFE)).isTrue();
+ assertThat(HdmiUtils.isValidPhysicalAddress(0x1200)).isTrue();
+ }
+
+ @Test
+ public void isValidPysicalAddress_outOfRange() {
+ assertThat(HdmiUtils.isValidPhysicalAddress(-1)).isFalse();
+ assertThat(HdmiUtils.isValidPhysicalAddress(0xFFFF)).isFalse();
+ assertThat(HdmiUtils.isValidPhysicalAddress(0x10000)).isFalse();
+ }
+
+ @Test
+ public void isValidPysicalAddress_nonTrailingZeros() {
+ assertThat(HdmiUtils.isValidPhysicalAddress(0x0001)).isFalse();
+ assertThat(HdmiUtils.isValidPhysicalAddress(0x0213)).isFalse();
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
index 03cf3eb6a2b9..9913531cdf13 100644
--- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -67,6 +68,7 @@ public class LockPatternUtilsTest {
// TODO(b/63758238): stop spying the class under test
mLockPatternUtils = spy(new LockPatternUtils(context));
when(mLockPatternUtils.getLockSettings()).thenReturn(ils);
+ doReturn(true).when(mLockPatternUtils).hasSecureLockScreen();
final UserInfo userInfo = Mockito.mock(UserInfo.class);
when(userInfo.isDemo()).thenReturn(isDemoUser);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 597d14ac286e..904c3fb6d549 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -180,6 +180,7 @@ applications that come with the platform
<permission name="android.permission.WRITE_APN_SETTINGS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
+ <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
</privapp-permissions>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 30f0bfa68b4a..bfbdbc585e08 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,7 +21,6 @@ import android.annotation.ColorInt;
import android.annotation.ColorLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.Size;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.annotation.WorkerThread;
@@ -748,22 +747,10 @@ public final class Bitmap implements Parcelable {
throw new IllegalArgumentException("usage flags must contain USAGE_GPU_SAMPLED_IMAGE.");
}
int format = hardwareBuffer.getFormat();
- ColorSpace.Rgb rgb = null;
- if (colorSpace != null) {
- if (!(colorSpace instanceof ColorSpace.Rgb)) {
- throw new IllegalArgumentException("colorSpace must be an RGB color space");
- }
- rgb = (ColorSpace.Rgb) colorSpace;
- } else {
- rgb = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
- }
- ColorSpace.Rgb.TransferParameters parameters = rgb.getTransferParameters();
- if (parameters == null) {
- throw new IllegalArgumentException("colorSpace must use an ICC "
- + "parametric transfer function");
+ if (colorSpace == null) {
+ colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
}
- ColorSpace.Rgb d50 = (ColorSpace.Rgb) ColorSpace.adapt(rgb, ColorSpace.ILLUMINANT_D50);
- return nativeWrapHardwareBufferBitmap(hardwareBuffer, d50.getTransform(), parameters);
+ return nativeWrapHardwareBufferBitmap(hardwareBuffer, colorSpace.getNativeInstance());
}
/**
@@ -1103,26 +1090,18 @@ public final class Bitmap implements Parcelable {
throw new IllegalArgumentException("can't create bitmap without a color space");
}
- Bitmap bm;
- // nullptr color spaces have a particular meaning in native and are interpreted as sRGB
- // (we also avoid the unnecessary extra work of the else branch)
- if (config != Config.ARGB_8888 || colorSpace == ColorSpace.get(ColorSpace.Named.SRGB)) {
- bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, null, null);
- } else {
- if (!(colorSpace instanceof ColorSpace.Rgb)) {
- throw new IllegalArgumentException("colorSpace must be an RGB color space");
- }
- ColorSpace.Rgb rgb = (ColorSpace.Rgb) colorSpace;
- ColorSpace.Rgb.TransferParameters parameters = rgb.getTransferParameters();
- if (parameters == null) {
- throw new IllegalArgumentException("colorSpace must use an ICC "
- + "parametric transfer function");
+ if (config != Config.ARGB_8888) {
+ if (config == Config.RGBA_F16) {
+ // FIXME: This should be LINEAR_EXTENDED_SRGB, but that would fail a CTS test. See
+ // b/120960866. SRGB matches the old (incorrect) behavior.
+ //colorSpace = ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
+ colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+ } else {
+ colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
}
-
- ColorSpace.Rgb d50 = (ColorSpace.Rgb) ColorSpace.adapt(rgb, ColorSpace.ILLUMINANT_D50);
- bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true,
- d50.getTransform(), parameters);
}
+ Bitmap bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true,
+ colorSpace.getNativeInstance());
if (display != null) {
bm.mDensity = display.densityDpi;
@@ -1200,8 +1179,9 @@ public final class Bitmap implements Parcelable {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("width and height must be > 0");
}
+ ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB);
Bitmap bm = nativeCreate(colors, offset, stride, width, height,
- config.nativeInt, false, null, null);
+ config.nativeInt, false, sRGB.getNativeInstance());
if (display != null) {
bm.mDensity = display.densityDpi;
}
@@ -1798,11 +1778,7 @@ public final class Bitmap implements Parcelable {
}
ColorSpace cs = Color.colorSpace(c);
- float r = Color.red(c);
- float g = Color.green(c);
- float b = Color.blue(c);
- float a = Color.alpha(c);
- nativeErase(mNativePtr, cs, r, g, b, a);
+ nativeErase(mNativePtr, cs.getNativeInstance(), c);
}
/**
@@ -2133,8 +2109,7 @@ public final class Bitmap implements Parcelable {
private static native Bitmap nativeCreate(int[] colors, int offset,
int stride, int width, int height,
int nativeConfig, boolean mutable,
- @Nullable @Size(9) float[] xyzD50,
- @Nullable ColorSpace.Rgb.TransferParameters p);
+ long nativeColorSpace);
private static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig,
boolean isMutable);
private static native Bitmap nativeCopyAshmem(long nativeSrcBitmap);
@@ -2149,8 +2124,7 @@ public final class Bitmap implements Parcelable {
int quality, OutputStream stream,
byte[] tempStorage);
private static native void nativeErase(long nativeBitmap, int color);
- private static native void nativeErase(long nativeBitmap, ColorSpace cs,
- float r, float g, float b, float a);
+ private static native void nativeErase(long nativeBitmap, long colorSpacePtr, long color);
private static native int nativeRowBytes(long nativeBitmap);
private static native int nativeConfig(long nativeBitmap);
@@ -2194,8 +2168,7 @@ public final class Bitmap implements Parcelable {
private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap);
private static native Bitmap nativeCreateHardwareBitmap(GraphicBuffer buffer);
private static native Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
- @Size(9) float[] xyzD50,
- ColorSpace.Rgb.TransferParameters p);
+ long nativeColorSpace);
private static native GraphicBuffer nativeCreateGraphicBufferHandle(long nativeBitmap);
private static native boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params);
private static native boolean nativeIsSRGB(long nativePtr);
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 022fbdc9df74..7aff0414106a 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -460,6 +460,21 @@ public class BitmapFactory {
}
}
}
+
+ /**
+ * Helper for passing SkColorSpace pointer to native.
+ *
+ * @throws IllegalArgumentException if the ColorSpace is not Rgb or does
+ * not have TransferParameters.
+ */
+ static long nativeColorSpace(Options opts) {
+ if (opts == null || opts.inPreferredColorSpace == null) {
+ return 0;
+ }
+
+ return opts.inPreferredColorSpace.getNativeInstance();
+ }
+
}
/**
@@ -633,7 +648,8 @@ public class BitmapFactory {
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
try {
- bm = nativeDecodeByteArray(data, offset, length, opts);
+ bm = nativeDecodeByteArray(data, offset, length, opts,
+ Options.nativeColorSpace(opts));
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
@@ -728,7 +744,7 @@ public class BitmapFactory {
try {
if (is instanceof AssetManager.AssetInputStream) {
final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
- bm = nativeDecodeAsset(asset, outPadding, opts);
+ bm = nativeDecodeAsset(asset, outPadding, opts, Options.nativeColorSpace(opts));
} else {
bm = decodeStreamInternal(is, outPadding, opts);
}
@@ -755,7 +771,8 @@ public class BitmapFactory {
byte [] tempStorage = null;
if (opts != null) tempStorage = opts.inTempStorage;
if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
- return nativeDecodeStream(is, tempStorage, outPadding, opts);
+ return nativeDecodeStream(is, tempStorage, outPadding, opts,
+ Options.nativeColorSpace(opts));
}
/**
@@ -798,7 +815,8 @@ public class BitmapFactory {
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor");
try {
if (nativeIsSeekable(fd)) {
- bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
+ bm = nativeDecodeFileDescriptor(fd, outPadding, opts,
+ Options.nativeColorSpace(opts));
} else {
FileInputStream fis = new FileInputStream(fd);
try {
@@ -835,14 +853,15 @@ public class BitmapFactory {
@UnsupportedAppUsage
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
- Rect padding, Options opts);
+ Rect padding, Options opts, long colorSpaceHandle);
@UnsupportedAppUsage
private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
- Rect padding, Options opts);
+ Rect padding, Options opts, long colorSpaceHandle);
@UnsupportedAppUsage
- private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts);
+ private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts,
+ long colorSpaceHandle);
@UnsupportedAppUsage
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
- int length, Options opts);
+ int length, Options opts, long colorSpaceHandle);
private static native boolean nativeIsSeekable(FileDescriptor fd);
}
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 43282d39ed47..1410423eafac 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -195,7 +195,8 @@ public final class BitmapRegionDecoder {
|| rect.top >= getHeight())
throw new IllegalArgumentException("rectangle is outside the image");
return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
- rect.right - rect.left, rect.bottom - rect.top, options);
+ rect.right - rect.left, rect.bottom - rect.top, options,
+ BitmapFactory.Options.nativeColorSpace(options));
}
}
@@ -265,7 +266,7 @@ public final class BitmapRegionDecoder {
private static native Bitmap nativeDecodeRegion(long lbm,
int start_x, int start_y, int width, int height,
- BitmapFactory.Options options);
+ BitmapFactory.Options options, long colorSpaceHandle);
private static native int nativeGetWidth(long lbm);
private static native int nativeGetHeight(long lbm);
private static native void nativeClean(long lbm);
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 9fa70a5ab19c..4755d45cd434 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -25,6 +25,8 @@ import android.annotation.Size;
import android.annotation.SuppressAutoDoc;
import android.util.Pair;
+import libcore.util.NativeAllocationRegistry;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -199,6 +201,9 @@ public abstract class ColorSpace {
private static final float[] NTSC_1953_PRIMARIES = { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f };
private static final float[] ILLUMINANT_D50_XYZ = { 0.964212f, 1.0f, 0.825188f };
+ private static final Rgb.TransferParameters SRGB_TRANSFER_PARAMETERS =
+ new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4);
+
// See static initialization block next to #get(Named)
private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length];
@@ -1341,6 +1346,26 @@ public abstract class ColorSpace {
}
/**
+ * Helper method for creating native SkColorSpace.
+ *
+ * This essentially calls adapt on a ColorSpace that has not been fully
+ * created. It also does not fully create the adapted ColorSpace, but
+ * just returns the transform.
+ */
+ @NonNull @Size(9)
+ private static float[] adaptToIlluminantD50(
+ @NonNull @Size(2) float[] origWhitePoint,
+ @NonNull @Size(9) float[] origTransform) {
+ float[] desired = ILLUMINANT_D50;
+ if (compare(origWhitePoint, desired)) return origTransform;
+
+ float[] xyz = xyYToXyz(desired);
+ float[] adaptationTransform = chromaticAdaptation(Adaptation.BRADFORD.mTransform,
+ xyYToXyz(origWhitePoint), xyz);
+ return mul3x3(adaptationTransform, origTransform);
+ }
+
+ /**
* <p>Returns an instance of {@link ColorSpace} whose ID matches the
* specified ID.</p>
*
@@ -1431,7 +1456,7 @@ public abstract class ColorSpace {
"sRGB IEC61966-2.1",
SRGB_PRIMARIES,
ILLUMINANT_D65,
- new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
+ SRGB_TRANSFER_PARAMETERS,
Named.SRGB.ordinal()
);
sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb(
@@ -1446,9 +1471,11 @@ public abstract class ColorSpace {
"scRGB-nl IEC 61966-2-2:2003",
SRGB_PRIMARIES,
ILLUMINANT_D65,
+ null,
x -> absRcpResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
x -> absResponse(x, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
-0.799f, 2.399f,
+ null, // FIXME: Use SRGB_TRANSFER_PARAMETERS
Named.EXTENDED_SRGB.ordinal()
);
sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
@@ -1485,7 +1512,7 @@ public abstract class ColorSpace {
"Display P3",
new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
ILLUMINANT_D65,
- new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
+ SRGB_TRANSFER_PARAMETERS,
Named.DISPLAY_P3.ordinal()
);
sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
@@ -1967,6 +1994,15 @@ public abstract class ColorSpace {
}
/**
+ * Retrieve the native SkColorSpace object for passing to native.
+ *
+ * Only valid on ColorSpace.Rgb.
+ */
+ long getNativeInstance() {
+ throw new IllegalArgumentException("colorSpace must be an RGB color space");
+ }
+
+ /**
* {@usesMathJax}
*
* <p>An RGB color space is an additive color space using the
@@ -2269,7 +2305,22 @@ public abstract class ColorSpace {
private final boolean mIsWideGamut;
private final boolean mIsSrgb;
- @Nullable private TransferParameters mTransferParameters;
+ @Nullable private final TransferParameters mTransferParameters;
+ private final long mNativePtr;
+
+ @Override
+ long getNativeInstance() {
+ if (mNativePtr == 0) {
+ // If this object has TransferParameters, it must have a native object.
+ throw new IllegalArgumentException("ColorSpace must use an ICC "
+ + "parametric transfer function! used " + this);
+ }
+ return mNativePtr;
+ }
+
+ private static native long nativeGetNativeFinalizer();
+ private static native long nativeCreate(float a, float b, float c, float d,
+ float e, float f, float g, float[] xyz);
/**
* <p>Creates a new RGB color space using a 3x3 column-major transform matrix.
@@ -2298,8 +2349,8 @@ public abstract class ColorSpace {
@NonNull @Size(9) float[] toXYZ,
@NonNull DoubleUnaryOperator oetf,
@NonNull DoubleUnaryOperator eotf) {
- this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ),
- oetf, eotf, 0.0f, 1.0f, MIN_ID);
+ this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), null,
+ oetf, eotf, 0.0f, 1.0f, null, MIN_ID);
}
/**
@@ -2349,7 +2400,7 @@ public abstract class ColorSpace {
@NonNull DoubleUnaryOperator eotf,
float min,
float max) {
- this(name, primaries, whitePoint, oetf, eotf, min, max, MIN_ID);
+ this(name, primaries, whitePoint, null, oetf, eotf, min, max, null, MIN_ID);
}
/**
@@ -2459,7 +2510,7 @@ public abstract class ColorSpace {
@NonNull @Size(min = 2, max = 3) float[] whitePoint,
@NonNull TransferParameters function,
@IntRange(from = MIN_ID, to = MAX_ID) int id) {
- this(name, primaries, whitePoint,
+ this(name, primaries, whitePoint, null,
function.e == 0.0 && function.f == 0.0 ?
x -> rcpResponse(x, function.a, function.b,
function.c, function.d, function.g) :
@@ -2470,8 +2521,7 @@ public abstract class ColorSpace {
function.c, function.d, function.g) :
x -> response(x, function.a, function.b, function.c,
function.d, function.e, function.f, function.g),
- 0.0f, 1.0f, id);
- mTransferParameters = function;
+ 0.0f, 1.0f, function, id);
}
/**
@@ -2586,13 +2636,12 @@ public abstract class ColorSpace {
float min,
float max,
@IntRange(from = MIN_ID, to = MAX_ID) int id) {
- this(name, primaries, whitePoint,
+ this(name, primaries, whitePoint, null,
gamma == 1.0 ? DoubleUnaryOperator.identity() :
x -> Math.pow(x < 0.0 ? 0.0 : x, 1 / gamma),
gamma == 1.0 ? DoubleUnaryOperator.identity() :
x -> Math.pow(x < 0.0 ? 0.0 : x, gamma),
- min, max, id);
- mTransferParameters = new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma);
+ min, max, new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma), id);
}
/**
@@ -2615,10 +2664,13 @@ public abstract class ColorSpace {
* @param name Name of the color space, cannot be null, its length must be >= 1
* @param primaries RGB primaries as an array of 6 (xy) or 9 (XYZ) floats
* @param whitePoint Reference white as an array of 2 (xy) or 3 (XYZ) floats
+ * @param transform Computed transform matrix that converts from RGB to XYZ, or
+ * {@code null} to compute it from {@code primaries} and {@code whitePoint}.
* @param oetf Opto-electronic transfer function, cannot be null
* @param eotf Electro-optical transfer function, cannot be null
* @param min The minimum valid value in this color space's RGB range
* @param max The maximum valid value in this color space's RGB range
+ * @param transferParameters Parameters for the transfer functions
* @param id ID of this color space as an integer between {@link #MIN_ID} and {@link #MAX_ID}
*
* @throws IllegalArgumentException If any of the following conditions is met:
@@ -2637,10 +2689,12 @@ public abstract class ColorSpace {
@NonNull @Size(min = 1) String name,
@NonNull @Size(min = 6, max = 9) float[] primaries,
@NonNull @Size(min = 2, max = 3) float[] whitePoint,
+ @Nullable @Size(9) float[] transform,
@NonNull DoubleUnaryOperator oetf,
@NonNull DoubleUnaryOperator eotf,
float min,
float max,
+ @Nullable TransferParameters transferParameters,
@IntRange(from = MIN_ID, to = MAX_ID) int id) {
super(name, Model.RGB, id);
@@ -2668,7 +2722,15 @@ public abstract class ColorSpace {
mWhitePoint = xyWhitePoint(whitePoint);
mPrimaries = xyPrimaries(primaries);
- mTransform = computeXYZMatrix(mPrimaries, mWhitePoint);
+ if (transform == null) {
+ mTransform = computeXYZMatrix(mPrimaries, mWhitePoint);
+ } else {
+ if (transform.length != 9) {
+ throw new IllegalArgumentException("Transform must have 9 entries! Has "
+ + transform.length);
+ }
+ mTransform = transform;
+ }
mInverseTransform = inverse3x3(mTransform);
mOetf = oetf;
@@ -2681,10 +2743,39 @@ public abstract class ColorSpace {
mClampedOetf = oetf.andThen(clamp);
mClampedEotf = clamp.andThen(eotf);
+ mTransferParameters = transferParameters;
+
// A color space is wide-gamut if its area is >90% of NTSC 1953 and
// if it entirely contains the Color space definition in xyY
mIsWideGamut = isWideGamut(mPrimaries, min, max);
mIsSrgb = isSrgb(mPrimaries, mWhitePoint, oetf, eotf, min, max, id);
+
+ if (mTransferParameters != null) {
+ if (mWhitePoint == null || mTransform == null) {
+ throw new IllegalStateException(
+ "ColorSpace (" + this + ") cannot create native object! mWhitePoint: "
+ + mWhitePoint + " mTransform: " + mTransform);
+ }
+
+ // This mimics the old code that was in native.
+ float[] nativeTransform = adaptToIlluminantD50(mWhitePoint, mTransform);
+ mNativePtr = nativeCreate((float) mTransferParameters.a,
+ (float) mTransferParameters.b,
+ (float) mTransferParameters.c,
+ (float) mTransferParameters.d,
+ (float) mTransferParameters.e,
+ (float) mTransferParameters.f,
+ (float) mTransferParameters.g,
+ nativeTransform);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePtr);
+ } else {
+ mNativePtr = 0;
+ }
+ }
+
+ private static class NoImagePreloadHolder {
+ public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ ColorSpace.Rgb.class.getClassLoader(), nativeGetNativeFinalizer(), 0);
}
/**
@@ -2695,27 +2786,9 @@ public abstract class ColorSpace {
private Rgb(Rgb colorSpace,
@NonNull @Size(9) float[] transform,
@NonNull @Size(min = 2, max = 3) float[] whitePoint) {
- super(colorSpace.getName(), Model.RGB, -1);
-
- mWhitePoint = xyWhitePoint(whitePoint);
- mPrimaries = colorSpace.mPrimaries;
-
- mTransform = transform;
- mInverseTransform = inverse3x3(transform);
-
- mMin = colorSpace.mMin;
- mMax = colorSpace.mMax;
-
- mOetf = colorSpace.mOetf;
- mEotf = colorSpace.mEotf;
-
- mClampedOetf = colorSpace.mClampedOetf;
- mClampedEotf = colorSpace.mClampedEotf;
-
- mIsWideGamut = colorSpace.mIsWideGamut;
- mIsSrgb = colorSpace.mIsSrgb;
-
- mTransferParameters = colorSpace.mTransferParameters;
+ this(colorSpace.getName(), colorSpace.mPrimaries, whitePoint, transform,
+ colorSpace.mOetf, colorSpace.mEotf, colorSpace.mMin, colorSpace.mMax,
+ colorSpace.mTransferParameters, MIN_ID);
}
/**
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index e3b165ccaf34..466a5fc2a770 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -1628,17 +1628,6 @@ public final class ImageDecoder implements AutoCloseable {
if (mPostProcessor != null && mUnpremultipliedRequired) {
throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
}
-
- if (mDesiredColorSpace != null) {
- if (!(mDesiredColorSpace instanceof ColorSpace.Rgb)) {
- throw new IllegalArgumentException("The target color space must use the "
- + "RGB color model - provided: " + mDesiredColorSpace);
- }
- if (((ColorSpace.Rgb) mDesiredColorSpace).getTransferParameters() == null) {
- throw new IllegalArgumentException("The target color space must use an "
- + "ICC parametric transfer function - provided: " + mDesiredColorSpace);
- }
- }
}
private static void checkSubset(int width, int height, Rect r) {
@@ -1655,10 +1644,12 @@ public final class ImageDecoder implements AutoCloseable {
@NonNull
private Bitmap decodeBitmapInternal() throws IOException {
checkState();
+ long colorSpacePtr = mDesiredColorSpace == null ? 0 :
+ mDesiredColorSpace.getNativeInstance();
return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
mDesiredWidth, mDesiredHeight, mCropRect,
mMutable, mAllocator, mUnpremultipliedRequired,
- mConserveMemory, mDecodeAsAlphaMask, mDesiredColorSpace);
+ mConserveMemory, mDecodeAsAlphaMask, colorSpacePtr);
}
private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -1946,7 +1937,7 @@ public final class ImageDecoder implements AutoCloseable {
@Nullable Rect cropRect, boolean mutable,
int allocator, boolean unpremulRequired,
boolean conserveMemory, boolean decodeAsAlphaMask,
- @Nullable ColorSpace desiredColorSpace)
+ long desiredColorSpace)
throws IOException;
private static native Size nGetSampledSize(long nativePtr,
int sampleSize);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 54e1abcaf1b7..7eee6f4bf37d 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1017,7 +1017,7 @@ public class Paint {
float b = Color.blue(color);
float a = Color.alpha(color);
- nSetColor(mNativePaint, cs, r, g, b, a);
+ nSetColor(mNativePaint, cs.getNativeInstance(), r, g, b, a);
mColor = color;
}
@@ -1455,7 +1455,7 @@ public class Paint {
float g = Color.green(shadowColor);
float b = Color.blue(shadowColor);
float a = Color.alpha(shadowColor);
- nSetShadowLayer(mNativePaint, radius, dx, dy, cs, r, g, b, a);
+ nSetShadowLayer(mNativePaint, radius, dx, dy, cs.getNativeInstance(), r, g, b, a);
mShadowLayerRadius = radius;
mShadowLayerDx = dx;
@@ -3003,11 +3003,6 @@ public class Paint {
int contextStart, int contextEnd, boolean isRtl, int offset);
private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, float advance);
- private static native void nSetColor(long paintPtr, ColorSpace cs,
- float r, float g, float b, float a);
- private static native void nSetShadowLayer(long paintPtr,
- float radius, float dx, float dy, ColorSpace cs,
- float r, float g, float b, float a);
// ---------------- @FastNative ------------------------
@@ -3063,7 +3058,8 @@ public class Paint {
int mMinikinLocaleListId);
@CriticalNative
private static native void nSetShadowLayer(long paintPtr,
- float radius, float dx, float dy, @ColorInt int color);
+ float radius, float dx, float dy, long colorSpaceHandle,
+ float r, float g, float b, float a);
@CriticalNative
private static native boolean nHasShadowLayer(long paintPtr);
@CriticalNative
@@ -3111,6 +3107,9 @@ public class Paint {
@CriticalNative
private static native void nSetFilterBitmap(long paintPtr, boolean filter);
@CriticalNative
+ private static native void nSetColor(long paintPtr, long colorSpaceHandle,
+ float r, float g, float b, float a);
+ @CriticalNative
private static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
@CriticalNative
private static native boolean nIsElegantTextHeight(long paintPtr);
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 154bd563fb0f..3d0afb098697 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -16,6 +16,8 @@
package android.location;
+import android.Manifest;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -161,6 +163,7 @@ public final class LocationRequest implements Parcelable {
private WorkSource mWorkSource = null;
@UnsupportedAppUsage
private boolean mHideFromAppOps = false; // True if this request shouldn't be counted by AppOps
+ private boolean mLocationSettingsIgnored = false;
@UnsupportedAppUsage
private String mProvider = LocationManager.FUSED_PROVIDER;
@@ -261,6 +264,7 @@ public final class LocationRequest implements Parcelable {
mWorkSource = src.mWorkSource;
mHideFromAppOps = src.mHideFromAppOps;
mLowPowerMode = src.mLowPowerMode;
+ mLocationSettingsIgnored = src.mLocationSettingsIgnored;
}
/**
@@ -375,6 +379,32 @@ public final class LocationRequest implements Parcelable {
}
/**
+ * Requests that user location settings be ignored in order to satisfy this request. This API
+ * is only for use in extremely rare scenarios where it is appropriate to ignore user location
+ * settings, such as a user initiated emergency (dialing 911 for instance).
+ *
+ * @param locationSettingsIgnored Whether to ignore location settings
+ * @return the same object, so that setters can be chained
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @SystemApi
+ public LocationRequest setLocationSettingsIgnored(boolean locationSettingsIgnored) {
+ mLocationSettingsIgnored = locationSettingsIgnored;
+ return this;
+ }
+
+ /**
+ * Returns true if location settings will be ignored in order to satisfy this request.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isLocationSettingsIgnored() {
+ return mLocationSettingsIgnored;
+ }
+
+ /**
* Explicitly set the fastest interval for location updates, in
* milliseconds.
*
diff --git a/location/lib/java/com/android/location/provider/ActivityChangedEvent.java b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java
new file mode 100644
index 000000000000..843dd670315a
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java
@@ -0,0 +1,57 @@
+/*
+ * 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
+ */
+
+package com.android.location.provider;
+
+import android.annotation.NonNull;
+
+import java.security.InvalidParameterException;
+import java.util.List;
+
+/**
+ * A class representing an event for Activity changes.
+ * @hide
+ */
+public class ActivityChangedEvent {
+ private final List<ActivityRecognitionEvent> mActivityRecognitionEvents;
+
+ public ActivityChangedEvent(List<ActivityRecognitionEvent> activityRecognitionEvents) {
+ if (activityRecognitionEvents == null) {
+ throw new InvalidParameterException(
+ "Parameter 'activityRecognitionEvents' must not be null.");
+ }
+
+ mActivityRecognitionEvents = activityRecognitionEvents;
+ }
+
+ @NonNull
+ public Iterable<ActivityRecognitionEvent> getActivityRecognitionEvents() {
+ return mActivityRecognitionEvents;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("[ ActivityChangedEvent:");
+
+ for (ActivityRecognitionEvent event : mActivityRecognitionEvents) {
+ builder.append("\n ");
+ builder.append(event.toString());
+ }
+ builder.append("\n]");
+
+ return builder.toString();
+ }
+}
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java
new file mode 100644
index 000000000000..e54dea40d690
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+package com.android.location.provider;
+
+/**
+ * A class that represents an Activity Recognition Event.
+ * @hide
+ */
+public class ActivityRecognitionEvent {
+ private final String mActivity;
+ private final int mEventType;
+ private final long mTimestampNs;
+
+ public ActivityRecognitionEvent(String activity, int eventType, long timestampNs) {
+ mActivity = activity;
+ mEventType = eventType;
+ mTimestampNs = timestampNs;
+ }
+
+ public String getActivity() {
+ return mActivity;
+ }
+
+ public int getEventType() {
+ return mEventType;
+ }
+
+ public long getTimestampNs() {
+ return mTimestampNs;
+ }
+
+ @Override
+ public String toString() {
+ String eventString;
+ switch (mEventType) {
+ case ActivityRecognitionProvider.EVENT_TYPE_ENTER:
+ eventString = "Enter";
+ break;
+ case ActivityRecognitionProvider.EVENT_TYPE_EXIT:
+ eventString = "Exit";
+ break;
+ case ActivityRecognitionProvider.EVENT_TYPE_FLUSH_COMPLETE:
+ eventString = "FlushComplete";
+ break;
+ default:
+ eventString = "<Invalid>";
+ break;
+ }
+
+ return String.format(
+ "Activity='%s', EventType=%s(%s), TimestampNs=%s",
+ mActivity,
+ eventString,
+ mEventType,
+ mTimestampNs);
+ }
+}
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
new file mode 100644
index 000000000000..0eff7d3f2014
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
@@ -0,0 +1,133 @@
+/*
+ * 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
+ */
+
+package com.android.location.provider;
+
+import com.android.internal.util.Preconditions;
+
+import android.hardware.location.IActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareSink;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * A class that exposes {@link IActivityRecognitionHardware} functionality to unbundled services.
+ * @hide
+ */
+public final class ActivityRecognitionProvider {
+ private final IActivityRecognitionHardware mService;
+ private final HashSet<Sink> mSinkSet = new HashSet<>();
+
+ // the following constants must remain in sync with activity_recognition.h
+
+ public static final String ACTIVITY_IN_VEHICLE = "android.activity_recognition.in_vehicle";
+ public static final String ACTIVITY_ON_BICYCLE = "android.activity_recognition.on_bicycle";
+ public static final String ACTIVITY_WALKING = "android.activity_recognition.walking";
+ public static final String ACTIVITY_RUNNING = "android.activity_recognition.running";
+ public static final String ACTIVITY_STILL = "android.activity_recognition.still";
+ public static final String ACTIVITY_TILTING = "android.activity_recognition.tilting";
+
+ // NOTE: when adding an additional EVENT_TYPE_, EVENT_TYPE_COUNT needs to be updated in
+ // android.hardware.location.ActivityRecognitionHardware
+ public static final int EVENT_TYPE_FLUSH_COMPLETE = 0;
+ public static final int EVENT_TYPE_ENTER = 1;
+ public static final int EVENT_TYPE_EXIT = 2;
+
+ // end constants activity_recognition.h
+
+ /**
+ * Used to receive Activity-Recognition events.
+ */
+ public interface Sink {
+ void onActivityChanged(ActivityChangedEvent event);
+ }
+
+ public ActivityRecognitionProvider(IActivityRecognitionHardware service)
+ throws RemoteException {
+ Preconditions.checkNotNull(service);
+ mService = service;
+ mService.registerSink(new SinkTransport());
+ }
+
+ public String[] getSupportedActivities() throws RemoteException {
+ return mService.getSupportedActivities();
+ }
+
+ public boolean isActivitySupported(String activity) throws RemoteException {
+ return mService.isActivitySupported(activity);
+ }
+
+ public void registerSink(Sink sink) {
+ Preconditions.checkNotNull(sink);
+ synchronized (mSinkSet) {
+ mSinkSet.add(sink);
+ }
+ }
+
+ // TODO: if this functionality is exposed to 3rd party developers, handle unregistration (here
+ // and in the service) of all sinks while failing to disable all events
+ public void unregisterSink(Sink sink) {
+ Preconditions.checkNotNull(sink);
+ synchronized (mSinkSet) {
+ mSinkSet.remove(sink);
+ }
+ }
+
+ public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs)
+ throws RemoteException {
+ return mService.enableActivityEvent(activity, eventType, reportLatencyNs);
+ }
+
+ public boolean disableActivityEvent(String activity, int eventType) throws RemoteException {
+ return mService.disableActivityEvent(activity, eventType);
+ }
+
+ public boolean flush() throws RemoteException {
+ return mService.flush();
+ }
+
+ private final class SinkTransport extends IActivityRecognitionHardwareSink.Stub {
+ @Override
+ public void onActivityChanged(android.hardware.location.ActivityChangedEvent event) {
+ Collection<Sink> sinks;
+ synchronized (mSinkSet) {
+ if (mSinkSet.isEmpty()) {
+ return;
+ }
+ sinks = new ArrayList<>(mSinkSet);
+ }
+
+ // translate the event from platform internal and GmsCore types
+ ArrayList<ActivityRecognitionEvent> gmsEvents = new ArrayList<>();
+ for (android.hardware.location.ActivityRecognitionEvent reportingEvent
+ : event.getActivityRecognitionEvents()) {
+ ActivityRecognitionEvent gmsEvent = new ActivityRecognitionEvent(
+ reportingEvent.getActivity(),
+ reportingEvent.getEventType(),
+ reportingEvent.getTimestampNs());
+ gmsEvents.add(gmsEvent);
+ }
+ ActivityChangedEvent gmsEvent = new ActivityChangedEvent(gmsEvents);
+
+ for (Sink sink : sinks) {
+ sink.onActivityChanged(gmsEvent);
+ }
+ }
+ }
+}
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
new file mode 100644
index 000000000000..326d901b9daa
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 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.location.provider;
+
+import android.annotation.NonNull;
+import android.hardware.location.IActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A client class for interaction with an Activity-Recognition provider.
+ * @hide
+ */
+public abstract class ActivityRecognitionProviderClient {
+ private static final String TAG = "ArProviderClient";
+
+ protected ActivityRecognitionProviderClient() {}
+
+ private IActivityRecognitionHardwareClient.Stub mClient =
+ new IActivityRecognitionHardwareClient.Stub() {
+ @Override
+ public void onAvailabilityChanged(
+ boolean isSupported,
+ IActivityRecognitionHardware instance) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ Log.d(TAG, "Ignoring calls from non-system server. Uid: " + callingUid);
+ return;
+ }
+ ActivityRecognitionProvider provider;
+ try {
+ provider = isSupported ? new ActivityRecognitionProvider(instance) : null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error creating Hardware Activity-Recognition Provider.", e);
+ return;
+ }
+ onProviderChanged(isSupported, provider);
+ }
+ };
+
+ /**
+ * Gets the binder needed to interact with proxy provider in the platform.
+ */
+ @NonNull
+ public IBinder getBinder() {
+ return mClient;
+ }
+
+ /**
+ * Called when a change in the availability of {@link ActivityRecognitionProvider} is detected.
+ *
+ * @param isSupported whether the platform supports the provider natively
+ * @param instance the available provider's instance
+ */
+ public abstract void onProviderChanged(
+ boolean isSupported,
+ ActivityRecognitionProvider instance);
+}
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
new file mode 100644
index 000000000000..42f77b42766f
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
@@ -0,0 +1,90 @@
+/*
+ * 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
+ */
+
+package com.android.location.provider;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.location.IActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareWatcher;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A watcher class for Activity-Recognition instances.
+ *
+ * @deprecated use {@link ActivityRecognitionProviderClient} instead.
+ * @hide
+ */
+@Deprecated
+public class ActivityRecognitionProviderWatcher {
+ private static final String TAG = "ActivityRecognitionProviderWatcher";
+
+ private static ActivityRecognitionProviderWatcher sWatcher;
+ private static final Object sWatcherLock = new Object();
+
+ private ActivityRecognitionProvider mActivityRecognitionProvider;
+
+ private ActivityRecognitionProviderWatcher() {}
+
+ public static ActivityRecognitionProviderWatcher getInstance() {
+ synchronized (sWatcherLock) {
+ if (sWatcher == null) {
+ sWatcher = new ActivityRecognitionProviderWatcher();
+ }
+ return sWatcher;
+ }
+ }
+
+ private IActivityRecognitionHardwareWatcher.Stub mWatcherStub =
+ new IActivityRecognitionHardwareWatcher.Stub() {
+ @Override
+ public void onInstanceChanged(IActivityRecognitionHardware instance) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ Log.d(TAG, "Ignoring calls from non-system server. Uid: " + callingUid);
+ return;
+ }
+
+ try {
+ mActivityRecognitionProvider = new ActivityRecognitionProvider(instance);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error creating Hardware Activity-Recognition", e);
+ }
+ }
+ };
+
+ /**
+ * Gets the binder needed to interact with proxy provider in the platform.
+ */
+ @NonNull
+ public IBinder getBinder() {
+ return mWatcherStub;
+ }
+
+ /**
+ * Gets an object that supports the functionality of {@link ActivityRecognitionProvider}.
+ *
+ * @return Non-null value if the functionality is supported by the platform, false otherwise.
+ */
+ @Nullable
+ public ActivityRecognitionProvider getActivityRecognitionProvider() {
+ return mActivityRecognitionProvider;
+ }
+}
diff --git a/lowpan/tests/Android.mk b/lowpan/tests/Android.mk
index 67727a7b1baa..832ed2f53f7b 100644
--- a/lowpan/tests/Android.mk
+++ b/lowpan/tests/Android.mk
@@ -45,7 +45,7 @@ LOCAL_JACK_COVERAGE_INCLUDE_FILTER := $(jacoco_include)
LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
guava \
mockito-target-minus-junit4 \
frameworks-base-testutils \
diff --git a/lowpan/tests/AndroidManifest.xml b/lowpan/tests/AndroidManifest.xml
index a21621423e41..4225613b4cc8 100644
--- a/lowpan/tests/AndroidManifest.xml
+++ b/lowpan/tests/AndroidManifest.xml
@@ -30,7 +30,7 @@
</activity>
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.net.lowpan.test"
android:label="Frameworks LoWPAN API Tests">
</instrumentation>
diff --git a/lowpan/tests/AndroidTest.xml b/lowpan/tests/AndroidTest.xml
index 72ad050efaeb..978cc02d2f57 100644
--- a/lowpan/tests/AndroidTest.xml
+++ b/lowpan/tests/AndroidTest.xml
@@ -22,6 +22,6 @@
<option name="test-tag" value="FrameworksLowpanApiTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.lowpan.test" />
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
</test>
</configuration>
diff --git a/lowpan/tests/README.md b/lowpan/tests/README.md
index d0eed95739a3..cb5772ee48ab 100644
--- a/lowpan/tests/README.md
+++ b/lowpan/tests/README.md
@@ -37,7 +37,7 @@ runtests.sh -e class android.net.lowpan.LowpanManagerTest
If you manually build and push the test APK to the device you can run tests using
```
-adb shell am instrument -w 'android.net.wifi.test/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -w 'android.net.wifi.test/androidx.test.runner.AndroidJUnitRunner'
```
## Adding Tests
diff --git a/lowpan/tests/runtests.sh b/lowpan/tests/runtests.sh
index 040f4f0492e9..8267a7975c42 100755
--- a/lowpan/tests/runtests.sh
+++ b/lowpan/tests/runtests.sh
@@ -21,4 +21,4 @@ adb wait-for-device
adb install -r -g "$OUT/data/app/FrameworksLowpanApiTests/FrameworksLowpanApiTests.apk"
-adb shell am instrument -w "$@" 'android.net.lowpan.test/android.support.test.runner.AndroidJUnitRunner'
+adb shell am instrument -w "$@" 'android.net.lowpan.test/androidx.test.runner.AndroidJUnitRunner'
diff --git a/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java b/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java
index a495d3d7a784..86f9d0e3ca69 100644
--- a/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java
+++ b/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java
@@ -23,15 +23,18 @@ import android.content.pm.ApplicationInfo;
import android.os.Handler;
import android.os.IBinder;
import android.os.test.TestLooper;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
-import java.util.Map;
+
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Map;
+
/** Unit tests for android.net.lowpan.LowpanInterface. */
@RunWith(AndroidJUnit4.class)
@SmallTest
diff --git a/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java b/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java
index 3dd7504d8332..998e8a549540 100644
--- a/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java
+++ b/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java
@@ -26,8 +26,10 @@ import android.content.pm.ApplicationInfo;
import android.os.Handler;
import android.os.IBinder;
import android.os.test.TestLooper;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 52771e4199d8..1d763ced3ca0 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -356,11 +356,11 @@ public final class AudioRecordingConfiguration implements Parcelable {
dest.writeInt(mDeviceSource);
dest.writeInt(mClientEffects.length);
for (int i = 0; i < mClientEffects.length; i++) {
- mClientEffects[i].writeToParcel(dest, 0);
+ mClientEffects[i].writeToParcel(dest);
}
dest.writeInt(mDeviceEffects.length);
for (int i = 0; i < mDeviceEffects.length; i++) {
- mDeviceEffects[i].writeToParcel(dest, 0);
+ mDeviceEffects[i].writeToParcel(dest);
}
}
@@ -375,13 +375,13 @@ public final class AudioRecordingConfiguration implements Parcelable {
mClientPortId = in.readInt();
mClientSilenced = in.readBoolean();
mDeviceSource = in.readInt();
- mClientEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
+ mClientEffects = new AudioEffect.Descriptor[in.readInt()];
for (int i = 0; i < mClientEffects.length; i++) {
- mClientEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+ mClientEffects[i] = new AudioEffect.Descriptor(in);
}
- mDeviceEffects = AudioEffect.Descriptor.CREATOR.newArray(in.readInt());
- for (int i = 0; i < mClientEffects.length; i++) {
- mDeviceEffects[i] = AudioEffect.Descriptor.CREATOR.createFromParcel(in);
+ mDeviceEffects = new AudioEffect.Descriptor[in.readInt()];
+ for (int i = 0; i < mDeviceEffects.length; i++) {
+ mDeviceEffects[i] = new AudioEffect.Descriptor(in);
}
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 2848b89a45ca..af016d5d4be9 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1100,7 +1100,8 @@ public class AudioSystem
(1 << STREAM_RING) |
(1 << STREAM_NOTIFICATION) |
(1 << STREAM_SYSTEM) |
- (1 << STREAM_VOICE_CALL);
+ (1 << STREAM_VOICE_CALL) |
+ (1 << STREAM_BLUETOOTH_SCO);
/**
* Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes.
diff --git a/media/java/android/media/IRemoteVolumeController.aidl b/media/java/android/media/IRemoteVolumeController.aidl
index a591c1119aeb..74c05c407571 100644
--- a/media/java/android/media/IRemoteVolumeController.aidl
+++ b/media/java/android/media/IRemoteVolumeController.aidl
@@ -9,6 +9,7 @@
*
* 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.
@@ -16,7 +17,7 @@
package android.media;
-import android.media.session.ISessionController;
+import android.media.session.MediaSession;
/**
* AIDL for the MediaSessionService to report interesting events on remote playback
@@ -25,8 +26,8 @@ import android.media.session.ISessionController;
* @hide
*/
oneway interface IRemoteVolumeController {
- void remoteVolumeChanged(in ISessionController session, int flags);
+ void remoteVolumeChanged(in MediaSession.Token sessionToken, int flags);
// sets the default session to use with the slider, replaces remoteSliderVisibility
// on IVolumeController
- void updateRemoteController(in ISessionController session);
+ void updateRemoteController(in MediaSession.Token sessionToken);
}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index dd971959dd25..887b4475a4d1 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -45,7 +45,7 @@ import java.util.concurrent.Executor;
/**
* Allows an app to interact with an active {@link MediaSession2} or a
- * MediaSession2Service which would provide {@link MediaSession2}. Media buttons and other
+ * {@link MediaSession2Service} which would provide {@link MediaSession2}. Media buttons and other
* commands can be sent to the session.
* <p>
* This API is not generally intended for third party application developers.
@@ -53,7 +53,6 @@ import java.util.concurrent.Executor;
* <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
* for consistent behavior across all devices.
*/
-// TODO: use @link for MediaSession2Service
public class MediaController2 implements AutoCloseable {
static final String TAG = "MediaController2";
static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -71,6 +70,8 @@ public class MediaController2 implements AutoCloseable {
private final Object mLock = new Object();
//@GuardedBy("mLock")
+ private boolean mClosed;
+ //@GuardedBy("mLock")
private int mNextSeqNumber;
//@GuardedBy("mLock")
private Session2Link mSessionBinder;
@@ -141,7 +142,14 @@ public class MediaController2 implements AutoCloseable {
@Override
public void close() {
synchronized (mLock) {
+ if (mClosed) {
+ // Already closed. Ignore rest of clean up code.
+ // Note: unbindService() throws IllegalArgumentException when it's called twice.
+ return;
+ }
+ mClosed = true;
if (mServiceConnection != null) {
+ // Note: This should be called even when the bindService() has returned false.
mContext.unbindService(mServiceConnection);
}
if (mSessionBinder != null) {
@@ -167,7 +175,7 @@ public class MediaController2 implements AutoCloseable {
* If it is not connected yet, it returns {@code null}.
* <p>
* This may differ with the {@link Session2Token} from the constructor. For example, if the
- * controller is created with the token for MediaSession2Service, this would return
+ * controller is created with the token for {@link MediaSession2Service}, this would return
* token for the {@link MediaSession2} in the service.
*
* @return Session2Token of the connected session, or {@code null} if not connected
@@ -316,7 +324,7 @@ public class MediaController2 implements AutoCloseable {
MediaController2.this, command, args);
if (resultReceiver != null) {
if (result == null) {
- throw new RuntimeException("onSessionCommand shouldn't return null");
+ resultReceiver.send(Session2Command.RESULT_INFO_SKIPPED, null);
} else {
resultReceiver.send(result.getResultCode(), result.getResultData());
}
@@ -433,8 +441,8 @@ public class MediaController2 implements AutoCloseable {
* @param controller the controller for this event
* @param command the session command
* @param args optional arguments
- * @return the result for the session command. A runtime exception will be thrown if null
- * is returned.
+ * @return the result for the session command. If {@code null}, RESULT_INFO_SKIPPED
+ * will be sent to the session.
*/
@Nullable
public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller,
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 3adac7295fff..fdd07fdd52e3 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -90,6 +90,8 @@ public class MediaSession2 implements AutoCloseable {
private boolean mClosed;
//@GuardedBy("mLock")
private boolean mPlaybackActive;
+ //@GuardedBy("mLock")
+ private ForegroundServiceEventCallback mForegroundServiceEventCallback;
MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity,
@NonNull Executor callbackExecutor, @NonNull SessionCallback callback) {
@@ -119,6 +121,7 @@ public class MediaSession2 implements AutoCloseable {
public void close() {
try {
List<ControllerInfo> controllerInfos;
+ ForegroundServiceEventCallback callback;
synchronized (mLock) {
if (mClosed) {
return;
@@ -126,11 +129,15 @@ public class MediaSession2 implements AutoCloseable {
mClosed = true;
controllerInfos = getConnectedControllers();
mConnectedControllers.clear();
- mCallback.onSessionClosed(this);
+ callback = mForegroundServiceEventCallback;
+ mForegroundServiceEventCallback = null;
}
synchronized (MediaSession2.class) {
SESSION_ID_LIST.remove(mSessionId);
}
+ if (callback != null) {
+ callback.onSessionClosed(this);
+ }
for (ControllerInfo info : controllerInfos) {
info.notifyDisconnected();
}
@@ -224,11 +231,16 @@ public class MediaSession2 implements AutoCloseable {
* @param playbackActive {@code true} if the playback active, {@code false} otherwise.
**/
public void setPlaybackActive(boolean playbackActive) {
+ final ForegroundServiceEventCallback serviceCallback;
synchronized (mLock) {
if (mPlaybackActive == playbackActive) {
return;
}
mPlaybackActive = playbackActive;
+ serviceCallback = mForegroundServiceEventCallback;
+ }
+ if (serviceCallback != null) {
+ serviceCallback.onPlaybackActiveChanged(this, playbackActive);
}
List<ControllerInfo> controllerInfos = getConnectedControllers();
for (ControllerInfo controller : controllerInfos) {
@@ -257,6 +269,18 @@ public class MediaSession2 implements AutoCloseable {
return mCallback;
}
+ void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) {
+ synchronized (mLock) {
+ if (mForegroundServiceEventCallback == callback) {
+ return;
+ }
+ if (mForegroundServiceEventCallback != null && callback != null) {
+ throw new IllegalStateException("A session cannot be added to multiple services");
+ }
+ mForegroundServiceEventCallback = callback;
+ }
+ }
+
// Called by Session2Link.onConnect and MediaSession2Service.MediaSession2ServiceStub.connect
void onConnect(final Controller2Link controller, int callingPid, int callingUid, int seq,
Bundle connectionRequest) {
@@ -373,7 +397,7 @@ public class MediaSession2 implements AutoCloseable {
MediaSession2.this, controllerInfo, command, args);
if (resultReceiver != null) {
if (result == null) {
- throw new RuntimeException("onSessionCommand shouldn't return null");
+ resultReceiver.send(Session2Command.RESULT_INFO_SKIPPED, null);
} else {
resultReceiver.send(result.getResultCode(), result.getResultData());
}
@@ -695,8 +719,6 @@ public class MediaSession2 implements AutoCloseable {
* This API is not generally intended for third party application developers.
*/
public abstract static class SessionCallback {
- ForegroundServiceEventCallback mForegroundServiceEventCallback;
-
/**
* Called when a controller is created for this session. Return allowed commands for
* controller. By default it returns {@code null}.
@@ -731,8 +753,8 @@ public class MediaSession2 implements AutoCloseable {
* @param controller controller information
* @param command the session command
* @param args optional arguments
- * @return the result for the session command. A runtime exception will be thrown if null
- * is returned.
+ * @return the result for the session command. If {@code null}, RESULT_INFO_SKIPPED
+ * will be sent to the session.
*/
@Nullable
public Session2Command.Result onSessionCommand(@NonNull MediaSession2 session,
@@ -753,19 +775,10 @@ public class MediaSession2 implements AutoCloseable {
public void onCommandResult(@NonNull MediaSession2 session,
@NonNull ControllerInfo controller, @NonNull Object token,
@NonNull Session2Command command, @NonNull Session2Command.Result result) {}
+ }
- final void onSessionClosed(MediaSession2 session) {
- if (mForegroundServiceEventCallback != null) {
- mForegroundServiceEventCallback.onSessionClosed(session);
- }
- }
-
- void setForegroundServiceEventCallback(ForegroundServiceEventCallback callback) {
- mForegroundServiceEventCallback = callback;
- }
-
- abstract static class ForegroundServiceEventCallback {
- public void onSessionClosed(MediaSession2 session) {}
- }
+ abstract static class ForegroundServiceEventCallback {
+ public void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {}
+ public void onSessionClosed(MediaSession2 session) {}
}
}
diff --git a/media/java/android/media/MediaSession2Service.java b/media/java/android/media/MediaSession2Service.java
index 8fb00fe487e8..5bb746a7f9e3 100644
--- a/media/java/android/media/MediaSession2Service.java
+++ b/media/java/android/media/MediaSession2Service.java
@@ -19,7 +19,10 @@ package android.media;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
@@ -28,8 +31,6 @@ import android.os.IBinder;
import android.util.ArrayMap;
import android.util.Log;
-import com.android.internal.annotations.GuardedBy;
-
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -42,11 +43,7 @@ import java.util.Map;
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
* <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
* for consistent behavior across all devices.
- * @hide
*/
-// TODO: Unhide
-// TODO: Add onUpdateNotification(), and calls it to get Notification for startForegroundService()
-// when a session's player state becomes playing.
public abstract class MediaSession2Service extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
@@ -56,10 +53,29 @@ public abstract class MediaSession2Service extends Service {
private static final String TAG = "MediaSession2Service";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final MediaSession2.ForegroundServiceEventCallback mForegroundServiceEventCallback =
+ new MediaSession2.ForegroundServiceEventCallback() {
+ @Override
+ public void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {
+ MediaSession2Service.this.onPlaybackActiveChanged(session, playbackActive);
+ }
+
+ @Override
+ public void onSessionClosed(MediaSession2 session) {
+ removeSession(session);
+ }
+ };
+
private final Object mLock = new Object();
- @GuardedBy("mLock")
+ //@GuardedBy("mLock")
+ private NotificationManager mNotificationManager;
+ //@GuardedBy("mLock")
+ private Intent mStartSelfIntent;
+ //@GuardedBy("mLock")
private Map<String, MediaSession2> mSessions = new ArrayMap<>();
-
+ //@GuardedBy("mLock")
+ private Map<MediaSession2, MediaNotification> mNotifications = new ArrayMap<>();
+ //@GuardedBy("mLock")
private MediaSession2ServiceStub mStub;
/**
@@ -72,7 +88,12 @@ public abstract class MediaSession2Service extends Service {
@Override
public void onCreate() {
super.onCreate();
- mStub = new MediaSession2ServiceStub(this);
+ synchronized (mLock) {
+ mStub = new MediaSession2ServiceStub(this);
+ mStartSelfIntent = new Intent(this, this.getClass());
+ mNotificationManager =
+ (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ }
}
@CallSuper
@@ -80,18 +101,13 @@ public abstract class MediaSession2Service extends Service {
@Nullable
public IBinder onBind(@NonNull Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mStub;
+ synchronized (mLock) {
+ return mStub;
+ }
}
return null;
}
- @CallSuper
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- // TODO: Dispatch media key events to the primary session.
- return START_STICKY;
- }
-
/**
* Called by the system to notify that it is no longer used and is being removed. Do not call
* this method directly.
@@ -104,10 +120,12 @@ public abstract class MediaSession2Service extends Service {
public void onDestroy() {
super.onDestroy();
synchronized (mLock) {
- for (MediaSession2 session : mSessions.values()) {
- session.getCallback().setForegroundServiceEventCallback(null);
+ List<MediaSession2> sessions = getSessions();
+ for (MediaSession2 session : sessions) {
+ removeSession(session);
}
mSessions.clear();
+ mNotifications.clear();
}
mStub.close();
}
@@ -144,6 +162,24 @@ public abstract class MediaSession2Service extends Service {
public abstract MediaSession2 onGetPrimarySession();
/**
+ * Called when notification UI needs update. Override this method to show or cancel your own
+ * notification UI.
+ * <p>
+ * This would be called on {@link MediaSession2}'s callback executor when playback state is
+ * changed.
+ * <p>
+ * With the notification returned here, the service becomes foreground service when the playback
+ * is started. Apps must request the permission
+ * {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use this API. It becomes
+ * background service after the playback is stopped.
+ *
+ * @param session a session that needs notification update.
+ * @return a {@link MediaNotification}. Can be {@code null}.
+ */
+ @Nullable
+ public abstract MediaNotification onUpdateNotification(@NonNull MediaSession2 session);
+
+ /**
* Adds a session to this service.
* <p>
* Added session will be removed automatically when it's closed, or removed when
@@ -161,21 +197,15 @@ public abstract class MediaSession2Service extends Service {
}
synchronized (mLock) {
MediaSession2 previousSession = mSessions.get(session.getSessionId());
- if (previousSession != session) {
- if (previousSession != null) {
+ if (previousSession != null) {
+ if (previousSession != session) {
Log.w(TAG, "Session ID should be unique, ID=" + session.getSessionId()
+ ", previous=" + previousSession + ", session=" + session);
}
return;
}
mSessions.put(session.getSessionId(), session);
- session.getCallback().setForegroundServiceEventCallback(
- new MediaSession2.SessionCallback.ForegroundServiceEventCallback() {
- @Override
- public void onSessionClosed(MediaSession2 session) {
- removeSession(session);
- }
- });
+ session.setForegroundServiceEventCallback(mForegroundServiceEventCallback);
}
}
@@ -189,8 +219,21 @@ public abstract class MediaSession2Service extends Service {
if (session == null) {
throw new IllegalArgumentException("session shouldn't be null");
}
+ MediaNotification notification;
synchronized (mLock) {
+ if (mSessions.get(session.getSessionId()) != session) {
+ // Session isn't added or removed already.
+ return;
+ }
mSessions.remove(session.getSessionId());
+ notification = mNotifications.remove(session);
+ }
+ session.setForegroundServiceEventCallback(null);
+ if (notification != null) {
+ mNotificationManager.cancel(notification.getNotificationId());
+ }
+ if (getSessions().isEmpty()) {
+ stopForeground(false);
}
}
@@ -207,6 +250,78 @@ public abstract class MediaSession2Service extends Service {
return list;
}
+ /**
+ * Called by registered {@link MediaSession2.ForegroundServiceEventCallback}
+ *
+ * @param session session with change
+ * @param playbackActive {@code true} if playback is active.
+ */
+ void onPlaybackActiveChanged(MediaSession2 session, boolean playbackActive) {
+ MediaNotification mediaNotification = onUpdateNotification(session);
+ if (mediaNotification == null) {
+ // The service implementation doesn't want to use the automatic start/stopForeground
+ // feature.
+ return;
+ }
+ synchronized (mLock) {
+ mNotifications.put(session, mediaNotification);
+ }
+ int id = mediaNotification.getNotificationId();
+ Notification notification = mediaNotification.getNotification();
+ if (!playbackActive) {
+ mNotificationManager.notify(id, notification);
+ return;
+ }
+ // playbackActive == true
+ startForegroundService(mStartSelfIntent);
+ startForeground(id, notification);
+ }
+
+ /**
+ * Returned by {@link #onUpdateNotification(MediaSession2)} for making session service
+ * foreground service to keep playback running in the background. It's highly recommended to
+ * show media style notification here.
+ */
+ public static class MediaNotification {
+ private final int mNotificationId;
+ private final Notification mNotification;
+
+ /**
+ * Default constructor
+ *
+ * @param notificationId notification id to be used for
+ * {@link NotificationManager#notify(int, Notification)}.
+ * @param notification a notification to make session service run in the foreground. Media
+ * style notification is recommended here.
+ */
+ public MediaNotification(int notificationId, @NonNull Notification notification) {
+ if (notification == null) {
+ throw new IllegalArgumentException("notification shouldn't be null");
+ }
+ mNotificationId = notificationId;
+ mNotification = notification;
+ }
+
+ /**
+ * Gets the id of the notification.
+ *
+ * @return the notification id
+ */
+ public int getNotificationId() {
+ return mNotificationId;
+ }
+
+ /**
+ * Gets the notification.
+ *
+ * @return the notification
+ */
+ @NonNull
+ public Notification getNotification() {
+ return mNotification;
+ }
+ }
+
private static final class MediaSession2ServiceStub extends IMediaSession2Service.Stub
implements AutoCloseable {
final WeakReference<MediaSession2Service> mService;
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 3b51c82e06c9..325420b06122 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -21,18 +21,14 @@ import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.media.session.MediaSession;
import android.media.session.MediaSessionLegacyHelper;
import android.media.session.PlaybackState;
-import android.media.session.MediaSession;
import android.os.Bundle;
-import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
-import java.lang.IllegalArgumentException;
-
/**
* RemoteControlClient enables exposing information meant to be consumed by remote controls
* capable of displaying metadata, artwork and media transport control buttons.
@@ -682,7 +678,7 @@ import java.lang.IllegalArgumentException;
// USE_SESSIONS
if (mSession != null) {
- int pbState = PlaybackState.getStateFromRccState(state);
+ int pbState = getStateFromRccState(state);
long position = hasPosition ? mPlaybackPositionMs
: PlaybackState.PLAYBACK_POSITION_UNKNOWN;
@@ -718,8 +714,7 @@ import java.lang.IllegalArgumentException;
// USE_SESSIONS
if (mSession != null) {
PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState);
- bob.setActions(
- PlaybackState.getActionsFromRccControlFlags(transportControlFlags));
+ bob.setActions(getActionsFromRccControlFlags(transportControlFlags));
mSessionPlaybackState = bob.build();
mSession.setPlaybackState(mSessionPlaybackState);
}
@@ -1001,16 +996,19 @@ import java.lang.IllegalArgumentException;
* Period for playback position drift checks, 15s when playing at 1x or slower.
*/
private final static long POSITION_REFRESH_PERIOD_PLAYING_MS = 15000;
+
/**
* Minimum period for playback position drift checks, never more often when every 2s, when
* fast forwarding or rewinding.
*/
private final static long POSITION_REFRESH_PERIOD_MIN_MS = 2000;
+
/**
* The value above which the difference between client-reported playback position and
* estimated position is considered a drift.
*/
private final static long POSITION_DRIFT_MAX_MS = 500;
+
/**
* Compute the period at which the estimated playback position should be compared against the
* actual playback position. Is a funciton of playback speed.
@@ -1025,4 +1023,151 @@ import java.lang.IllegalArgumentException;
POSITION_REFRESH_PERIOD_MIN_MS);
}
}
+
+ /**
+ * Get the {@link PlaybackState} state for the given
+ * {@link RemoteControlClient} state.
+ *
+ * @param rccState The state used by {@link RemoteControlClient}.
+ * @return The equivalent state used by {@link PlaybackState}.
+ */
+ private static int getStateFromRccState(int rccState) {
+ switch (rccState) {
+ case PLAYSTATE_BUFFERING:
+ return PlaybackState.STATE_BUFFERING;
+ case PLAYSTATE_ERROR:
+ return PlaybackState.STATE_ERROR;
+ case PLAYSTATE_FAST_FORWARDING:
+ return PlaybackState.STATE_FAST_FORWARDING;
+ case PLAYSTATE_NONE:
+ return PlaybackState.STATE_NONE;
+ case PLAYSTATE_PAUSED:
+ return PlaybackState.STATE_PAUSED;
+ case PLAYSTATE_PLAYING:
+ return PlaybackState.STATE_PLAYING;
+ case PLAYSTATE_REWINDING:
+ return PlaybackState.STATE_REWINDING;
+ case PLAYSTATE_SKIPPING_BACKWARDS:
+ return PlaybackState.STATE_SKIPPING_TO_PREVIOUS;
+ case PLAYSTATE_SKIPPING_FORWARDS:
+ return PlaybackState.STATE_SKIPPING_TO_NEXT;
+ case PLAYSTATE_STOPPED:
+ return PlaybackState.STATE_STOPPED;
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * Get the {@link RemoteControlClient} state for the given
+ * {@link PlaybackState} state.
+ *
+ * @param state The state used by {@link PlaybackState}.
+ * @return The equivalent state used by {@link RemoteControlClient}.
+ */
+ static int getRccStateFromState(int state) {
+ switch (state) {
+ case PlaybackState.STATE_BUFFERING:
+ return PLAYSTATE_BUFFERING;
+ case PlaybackState.STATE_ERROR:
+ return PLAYSTATE_ERROR;
+ case PlaybackState.STATE_FAST_FORWARDING:
+ return PLAYSTATE_FAST_FORWARDING;
+ case PlaybackState.STATE_NONE:
+ return PLAYSTATE_NONE;
+ case PlaybackState.STATE_PAUSED:
+ return PLAYSTATE_PAUSED;
+ case PlaybackState.STATE_PLAYING:
+ return PLAYSTATE_PLAYING;
+ case PlaybackState.STATE_REWINDING:
+ return PLAYSTATE_REWINDING;
+ case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
+ return PLAYSTATE_SKIPPING_BACKWARDS;
+ case PlaybackState.STATE_SKIPPING_TO_NEXT:
+ return PLAYSTATE_SKIPPING_FORWARDS;
+ case PlaybackState.STATE_STOPPED:
+ return PLAYSTATE_STOPPED;
+ default:
+ return -1;
+ }
+ }
+
+ private static long getActionsFromRccControlFlags(int rccFlags) {
+ long actions = 0;
+ long flag = 1;
+ while (flag <= rccFlags) {
+ if ((flag & rccFlags) != 0) {
+ actions |= getActionForRccFlag((int) flag);
+ }
+ flag = flag << 1;
+ }
+ return actions;
+ }
+
+ static int getRccControlFlagsFromActions(long actions) {
+ int rccFlags = 0;
+ long action = 1;
+ while (action <= actions && action < Integer.MAX_VALUE) {
+ if ((action & actions) != 0) {
+ rccFlags |= getRccFlagForAction(action);
+ }
+ action = action << 1;
+ }
+ return rccFlags;
+ }
+
+ private static long getActionForRccFlag(int flag) {
+ switch (flag) {
+ case FLAG_KEY_MEDIA_PREVIOUS:
+ return PlaybackState.ACTION_SKIP_TO_PREVIOUS;
+ case FLAG_KEY_MEDIA_REWIND:
+ return PlaybackState.ACTION_REWIND;
+ case FLAG_KEY_MEDIA_PLAY:
+ return PlaybackState.ACTION_PLAY;
+ case FLAG_KEY_MEDIA_PLAY_PAUSE:
+ return PlaybackState.ACTION_PLAY_PAUSE;
+ case FLAG_KEY_MEDIA_PAUSE:
+ return PlaybackState.ACTION_PAUSE;
+ case FLAG_KEY_MEDIA_STOP:
+ return PlaybackState.ACTION_STOP;
+ case FLAG_KEY_MEDIA_FAST_FORWARD:
+ return PlaybackState.ACTION_FAST_FORWARD;
+ case FLAG_KEY_MEDIA_NEXT:
+ return PlaybackState.ACTION_SKIP_TO_NEXT;
+ case FLAG_KEY_MEDIA_POSITION_UPDATE:
+ return PlaybackState.ACTION_SEEK_TO;
+ case FLAG_KEY_MEDIA_RATING:
+ return PlaybackState.ACTION_SET_RATING;
+ }
+ return 0;
+ }
+
+ private static int getRccFlagForAction(long action) {
+ // We only care about the lower set of actions that can map to rcc
+ // flags.
+ int testAction = action < Integer.MAX_VALUE ? (int) action : 0;
+ switch (testAction) {
+ case (int) PlaybackState.ACTION_SKIP_TO_PREVIOUS:
+ return FLAG_KEY_MEDIA_PREVIOUS;
+ case (int) PlaybackState.ACTION_REWIND:
+ return FLAG_KEY_MEDIA_REWIND;
+ case (int) PlaybackState.ACTION_PLAY:
+ return FLAG_KEY_MEDIA_PLAY;
+ case (int) PlaybackState.ACTION_PLAY_PAUSE:
+ return FLAG_KEY_MEDIA_PLAY_PAUSE;
+ case (int) PlaybackState.ACTION_PAUSE:
+ return FLAG_KEY_MEDIA_PAUSE;
+ case (int) PlaybackState.ACTION_STOP:
+ return FLAG_KEY_MEDIA_STOP;
+ case (int) PlaybackState.ACTION_FAST_FORWARD:
+ return FLAG_KEY_MEDIA_FAST_FORWARD;
+ case (int) PlaybackState.ACTION_SKIP_TO_NEXT:
+ return FLAG_KEY_MEDIA_NEXT;
+ case (int) PlaybackState.ACTION_SEEK_TO:
+ return FLAG_KEY_MEDIA_POSITION_UPDATE;
+ case (int) PlaybackState.ACTION_SET_RATING:
+ return FLAG_KEY_MEDIA_RATING;
+ }
+ return 0;
+ }
}
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 5e9eed737256..f70963a982e4 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -632,8 +632,8 @@ import java.util.List;
l = this.mOnClientUpdateListener;
}
if (l != null) {
- int playstate = state == null ? RemoteControlClient.PLAYSTATE_NONE : PlaybackState
- .getRccStateFromState(state.getState());
+ int playstate = state == null ? RemoteControlClient.PLAYSTATE_NONE
+ : RemoteControlClient.getRccStateFromState(state.getState());
if (state == null || state.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
l.onClientPlaybackStateUpdate(playstate);
} else {
@@ -642,7 +642,7 @@ import java.util.List;
}
if (state != null) {
l.onClientTransportControlUpdate(
- PlaybackState.getRccControlFlagsFromActions(state.getActions()));
+ RemoteControlClient.getRccControlFlagsFromActions(state.getActions()));
}
}
}
diff --git a/media/java/android/media/Session2Token.java b/media/java/android/media/Session2Token.java
index d8f74c518bf4..238cc2b8ee7d 100644
--- a/media/java/android/media/Session2Token.java
+++ b/media/java/android/media/Session2Token.java
@@ -35,7 +35,7 @@ import java.util.List;
import java.util.Objects;
/**
- * Represents an ongoing {@link MediaSession2} or a MediaSession2Service.
+ * Represents an ongoing {@link MediaSession2} or a {@link MediaSession2Service}.
* If it's representing a session service, it may not be ongoing.
* <p>
* This API is not generally intended for third party application developers.
@@ -48,14 +48,6 @@ import java.util.Objects;
* <p>
* It can be also obtained by {@link android.media.session.MediaSessionManager}.
*/
-// New version of MediaSession2.Token for following reasons
-// - Stop implementing Parcelable for updatable support
-// - Represent session and library service (formerly browser service) in one class.
-// Previously MediaSession2.Token was for session and ComponentName was for service.
-// This helps controller apps to keep target of dispatching media key events in uniform way.
-// For details about the reason, see following. (Android O+)
-// android.media.session.MediaSessionManager.Callback#onAddressedPlayerChanged
-// TODO: use @link for MediaSession2Service
public final class Session2Token implements Parcelable {
private static final String TAG = "Session2Token";
@@ -85,12 +77,13 @@ public final class Session2Token implements Parcelable {
public static final int TYPE_SESSION = 0;
/**
- * Type for MediaSession2Service.
+ * Type for {@link MediaSession2Service}.
*/
public static final int TYPE_SESSION_SERVICE = 1;
private final int mUid;
- private final @TokenType int mType;
+ @TokenType
+ private final int mType;
private final String mPackageName;
private final String mServiceName;
private final Session2Link mSessionLink;
@@ -206,14 +199,6 @@ public final class Session2Token implements Parcelable {
}
/**
- * @hide
- * @return component name of the session. Can be {@code null} for {@link #TYPE_SESSION}.
- */
- public ComponentName getComponentName() {
- return mComponentName;
- }
-
- /**
* @return type of the token
* @see #TYPE_SESSION
* @see #TYPE_SESSION_SERVICE
@@ -222,10 +207,7 @@ public final class Session2Token implements Parcelable {
return mType;
}
- /**
- * @hide
- */
- public Session2Link getSessionLink() {
+ Session2Link getSessionLink() {
return mSessionLink;
}
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 52e9ae191f0c..5b4bbce91784 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -26,7 +26,6 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
-import android.os.Parcelable;
import android.util.Log;
import java.lang.ref.WeakReference;
@@ -229,7 +228,7 @@ public class AudioEffect {
* The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects
* enumeration.
*/
- public static final class Descriptor implements Parcelable {
+ public static class Descriptor {
public Descriptor() {
}
@@ -294,7 +293,9 @@ public class AudioEffect {
this.implementor = implementor;
}
- private Descriptor(Parcel in) {
+ /** @hide */
+ @TestApi
+ public Descriptor(Parcel in) {
type = UUID.fromString(in.readString());
uuid = UUID.fromString(in.readString());
connectMode = in.readString();
@@ -302,33 +303,14 @@ public class AudioEffect {
implementor = in.readString();
}
- public static final Parcelable.Creator<Descriptor> CREATOR =
- new Parcelable.Creator<Descriptor>() {
- /**
- * Rebuilds a Descriptor previously stored with writeToParcel().
- * @param p Parcel object to read the Descriptor from
- * @return a new Descriptor created from the data in the parcel
- */
- public Descriptor createFromParcel(Parcel p) {
- return new Descriptor(p);
- }
- public Descriptor[] newArray(int size) {
- return new Descriptor[size];
- }
- };
-
@Override
public int hashCode() {
return Objects.hash(type, uuid, connectMode, name, implementor);
}
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
+ /** @hide */
+ @TestApi
+ public void writeToParcel(Parcel dest) {
dest.writeString(type.toString());
dest.writeString(uuid.toString());
dest.writeString(connectMode);
diff --git a/media/java/android/media/session/ControllerCallbackLink.java b/media/java/android/media/session/ControllerCallbackLink.java
index 95e19d20e57d..2d59e442abf0 100644
--- a/media/java/android/media/session/ControllerCallbackLink.java
+++ b/media/java/android/media/session/ControllerCallbackLink.java
@@ -24,6 +24,7 @@ import android.annotation.SystemApi;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.MediaMetadata;
+import android.media.MediaParceledListSlice;
import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession.QueueItem;
import android.os.Binder;
@@ -127,7 +128,8 @@ public final class ControllerCallbackLink implements Parcelable {
@RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
public void notifyQueueChanged(@Nullable List<QueueItem> queue) {
try {
- mIControllerCallback.notifyQueueChanged(queue);
+ mIControllerCallback.notifyQueueChanged(queue == null ? null :
+ new MediaParceledListSlice(queue));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -254,7 +256,7 @@ public final class ControllerCallbackLink implements Parcelable {
@Override
public void notifyPlaybackStateChanged(PlaybackState state) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPlaybackStateChanged(state);
@@ -265,7 +267,7 @@ public final class ControllerCallbackLink implements Parcelable {
@Override
public void notifyMetadataChanged(MediaMetadata metadata) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onMetadataChanged(metadata);
@@ -275,11 +277,11 @@ public final class ControllerCallbackLink implements Parcelable {
}
@Override
- public void notifyQueueChanged(List<QueueItem> queue) {
- ensureMediasControlPermission();
+ public void notifyQueueChanged(MediaParceledListSlice queue) {
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
- mCallbackStub.onQueueChanged(queue);
+ mCallbackStub.onQueueChanged(queue == null ? null : queue.getList());
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -287,7 +289,7 @@ public final class ControllerCallbackLink implements Parcelable {
@Override
public void notifyQueueTitleChanged(CharSequence title) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onQueueTitleChanged(title);
@@ -303,7 +305,7 @@ public final class ControllerCallbackLink implements Parcelable {
@Override
public void notifyVolumeInfoChanged(PlaybackInfo info) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onVolumeInfoChanged(info);
@@ -312,7 +314,7 @@ public final class ControllerCallbackLink implements Parcelable {
}
}
- private void ensureMediasControlPermission() {
+ private void ensureMediaControlPermission() {
// Allow API calls from the System UI
if (mContext.checkCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
== PackageManager.PERMISSION_GRANTED) {
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index 5c02e7c2f6e3..56ae852d6f50 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -16,8 +16,8 @@
package android.media.session;
import android.media.MediaMetadata;
+import android.media.MediaParceledListSlice;
import android.media.session.MediaController;
-import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Bundle;
@@ -31,7 +31,7 @@ oneway interface ISessionControllerCallback {
// These callbacks are for the TransportController
void notifyPlaybackStateChanged(in PlaybackState state);
void notifyMetadataChanged(in MediaMetadata metadata);
- void notifyQueueChanged(in List<MediaSession.QueueItem> queue);
+ void notifyQueueChanged(in MediaParceledListSlice queue);
void notifyQueueTitleChanged(CharSequence title);
void notifyExtrasChanged(in Bundle extras);
void notifyVolumeInfoChanged(in MediaController.PlaybackInfo info);
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 9d537c8c2fcb..057c9cb028c1 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -117,7 +117,7 @@ public final class MediaController {
* @param token The token for the session.
*/
public MediaController(@NonNull Context context, @NonNull MediaSession.Token token) {
- this(context, token.getBinder());
+ this(context, token.getControllerLink());
}
/**
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index f02d9ba1b7df..4896d0803e42 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -19,6 +19,7 @@ package android.media.session;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.app.Activity;
import android.app.PendingIntent;
@@ -33,23 +34,15 @@ import android.media.session.MediaSessionManager.RemoteUserInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ResultReceiver;
import android.service.media.MediaBrowserService;
import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-import android.view.KeyEvent;
-import android.view.ViewConfiguration;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
import java.util.List;
-import java.util.Objects;
/**
* Allows interaction with media controllers, volume keys, media buttons, and
@@ -74,7 +67,7 @@ import java.util.Objects;
* MediaSession objects are thread safe.
*/
public final class MediaSession {
- private static final String TAG = "MediaSession";
+ static final String TAG = "MediaSession";
/**
* Set this flag on the session to indicate that it can handle media button
@@ -121,21 +114,11 @@ public final class MediaSession {
FLAG_EXCLUSIVE_GLOBAL_PRIORITY })
public @interface SessionFlags { }
- private final Object mLock = new Object();
- private final int mMaxBitmapSize;
-
- private final MediaSession.Token mSessionToken;
- private final MediaController mController;
- private final SessionLink mSessionLink;
- private final SessionCallbackLink mCbStub;
+ private final MediaSessionEngine mImpl;
// Do not change the name of mCallback. Support lib accesses this by using reflection.
@UnsupportedAppUsage
- private CallbackMessageHandler mCallback;
- private VolumeProvider mVolumeProvider;
- private PlaybackState mPlaybackState;
-
- private boolean mActive = false;
+ private Object mCallback;
/**
* Creates a new session. The session will automatically be registered with
@@ -153,15 +136,15 @@ public final class MediaSession {
if (TextUtils.isEmpty(tag)) {
throw new IllegalArgumentException("tag cannot be null or empty");
}
- mMaxBitmapSize = context.getResources().getDimensionPixelSize(
- android.R.dimen.config_mediaMetadataBitmapMaxSize);
- mCbStub = new SessionCallbackLink(context, new CallbackStub(this));
MediaSessionManager manager = (MediaSessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
try {
- mSessionLink = manager.createSession(mCbStub, tag);
- mSessionToken = new Token(mSessionLink.getController());
- mController = new MediaController(context, mSessionToken);
+ MediaSessionEngine.CallbackStub cbStub = new MediaSessionEngine.CallbackStub();
+ SessionCallbackLink cbLink = new SessionCallbackLink(context, cbStub);
+ SessionLink sessionLink = manager.createSession(cbLink, tag);
+ mImpl = new MediaSessionEngine(context, sessionLink, cbLink, cbStub,
+ context.getResources().getDimensionPixelSize(
+ android.R.dimen.config_mediaMetadataBitmapMaxSize));
} catch (RuntimeException e) {
throw new RuntimeException("Remote error creating session.", e);
}
@@ -177,7 +160,8 @@ public final class MediaSession {
* @param callback The callback object
*/
public void setCallback(@Nullable Callback callback) {
- setCallback(callback, null);
+ mCallback = callback == null ? null : new Object();
+ mImpl.setCallback(callback);
}
/**
@@ -190,24 +174,8 @@ public final class MediaSession {
* @param handler The handler that events should be posted on.
*/
public void setCallback(@Nullable Callback callback, @Nullable Handler handler) {
- synchronized (mLock) {
- if (mCallback != null) {
- // We're updating the callback, clear the session from the old one.
- mCallback.mCallback.mSession = null;
- mCallback.removeCallbacksAndMessages(null);
- }
- if (callback == null) {
- mCallback = null;
- return;
- }
- if (handler == null) {
- handler = new Handler();
- }
- callback.mSession = this;
- CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
- callback);
- mCallback = msgHandler;
- }
+ mCallback = callback == null ? null : new Object();
+ mImpl.setCallback(callback, handler);
}
/**
@@ -218,11 +186,7 @@ public final class MediaSession {
* @param pi The intent to launch to show UI for this Session.
*/
public void setSessionActivity(@Nullable PendingIntent pi) {
- try {
- mSessionLink.setLaunchPendingIntent(pi);
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Failure in setLaunchPendingIntent.", e);
- }
+ mImpl.setSessionActivity(pi);
}
/**
@@ -234,11 +198,7 @@ public final class MediaSession {
* @param mbr The {@link PendingIntent} to send the media button event to.
*/
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
- try {
- mSessionLink.setMediaButtonReceiver(mbr);
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
- }
+ mImpl.setMediaButtonReceiver(mbr);
}
/**
@@ -247,11 +207,7 @@ public final class MediaSession {
* @param flags The flags to set for this session.
*/
public void setFlags(@SessionFlags int flags) {
- try {
- mSessionLink.setFlags(flags);
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Failure in setFlags.", e);
- }
+ mImpl.setFlags(flags);
}
/**
@@ -266,14 +222,7 @@ public final class MediaSession {
* @param attributes The {@link AudioAttributes} for this session's audio.
*/
public void setPlaybackToLocal(AudioAttributes attributes) {
- if (attributes == null) {
- throw new IllegalArgumentException("Attributes cannot be null for local playback.");
- }
- try {
- mSessionLink.setPlaybackToLocal(attributes);
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
- }
+ mImpl.setPlaybackToLocal(attributes);
}
/**
@@ -288,26 +237,7 @@ public final class MediaSession {
* not be null.
*/
public void setPlaybackToRemote(@NonNull VolumeProvider volumeProvider) {
- if (volumeProvider == null) {
- throw new IllegalArgumentException("volumeProvider may not be null!");
- }
- synchronized (mLock) {
- mVolumeProvider = volumeProvider;
- }
- volumeProvider.setCallback(new VolumeProvider.Callback() {
- @Override
- public void onVolumeChanged(VolumeProvider volumeProvider) {
- notifyRemoteVolumeChanged(volumeProvider);
- }
- });
-
- try {
- mSessionLink.setPlaybackToRemote(volumeProvider.getVolumeControl(),
- volumeProvider.getMaxVolume());
- mSessionLink.setCurrentVolume(volumeProvider.getCurrentVolume());
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
- }
+ mImpl.setPlaybackToRemote(volumeProvider);
}
/**
@@ -319,15 +249,7 @@ public final class MediaSession {
* @param active Whether this session is active or not.
*/
public void setActive(boolean active) {
- if (mActive == active) {
- return;
- }
- try {
- mSessionLink.setActive(active);
- mActive = active;
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Failure in setActive.", e);
- }
+ mImpl.setActive(active);
}
/**
@@ -336,7 +258,7 @@ public final class MediaSession {
* @return True if the session is active, false otherwise.
*/
public boolean isActive() {
- return mActive;
+ return mImpl.isActive();
}
/**
@@ -348,14 +270,7 @@ public final class MediaSession {
* @param extras Any extras included with the event
*/
public void sendSessionEvent(@NonNull String event, @Nullable Bundle extras) {
- if (TextUtils.isEmpty(event)) {
- throw new IllegalArgumentException("event cannot be null or empty");
- }
- try {
- mSessionLink.sendEvent(event, extras);
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Error sending event", e);
- }
+ mImpl.sendSessionEvent(event, extras);
}
/**
@@ -364,11 +279,7 @@ public final class MediaSession {
* but it must be released if your activity or service is being destroyed.
*/
public void release() {
- try {
- mSessionLink.destroySession();
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Error releasing session: ", e);
- }
+ mImpl.close();
}
/**
@@ -380,7 +291,7 @@ public final class MediaSession {
* session
*/
public @NonNull Token getSessionToken() {
- return mSessionToken;
+ return mImpl.getSessionToken();
}
/**
@@ -390,7 +301,7 @@ public final class MediaSession {
* @return A controller for this session.
*/
public @NonNull MediaController getController() {
- return mController;
+ return mImpl.getController();
}
/**
@@ -399,12 +310,7 @@ public final class MediaSession {
* @param state The current state of playback
*/
public void setPlaybackState(@Nullable PlaybackState state) {
- mPlaybackState = state;
- try {
- mSessionLink.setPlaybackState(state);
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Dead object in setPlaybackState.", e);
- }
+ mImpl.setPlaybackState(state);
}
/**
@@ -416,24 +322,7 @@ public final class MediaSession {
* @see android.media.MediaMetadata.Builder#putBitmap
*/
public void setMetadata(@Nullable MediaMetadata metadata) {
- long duration = -1;
- int fields = 0;
- MediaDescription description = null;
- if (metadata != null) {
- metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build();
- if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
- duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
- }
- fields = metadata.size();
- description = metadata.getDescription();
- }
- String metadataDescription = "size=" + fields + ", description=" + description;
-
- try {
- mSessionLink.setMetadata(metadata, duration, metadataDescription);
- } catch (RuntimeException e) {
- Log.wtf(TAG, "Dead object in setPlaybackState.", e);
- }
+ mImpl.setMetadata(metadata);
}
/**
@@ -448,11 +337,7 @@ public final class MediaSession {
* @param queue A list of items in the play queue.
*/
public void setQueue(@Nullable List<QueueItem> queue) {
- try {
- mSessionLink.setQueue(queue);
- } catch (RuntimeException e) {
- Log.wtf("Dead object in setQueue.", e);
- }
+ mImpl.setQueue(queue);
}
/**
@@ -463,11 +348,7 @@ public final class MediaSession {
* @param title The title of the play queue.
*/
public void setQueueTitle(@Nullable CharSequence title) {
- try {
- mSessionLink.setQueueTitle(title);
- } catch (RuntimeException e) {
- Log.wtf("Dead object in setQueueTitle.", e);
- }
+ mImpl.setQueueTitle(title);
}
/**
@@ -484,11 +365,7 @@ public final class MediaSession {
* </ul>
*/
public void setRatingType(@Rating.Style int type) {
- try {
- mSessionLink.setRatingType(type);
- } catch (RuntimeException e) {
- Log.e(TAG, "Error in setRatingType.", e);
- }
+ mImpl.setRatingType(type);
}
/**
@@ -499,11 +376,7 @@ public final class MediaSession {
* @param extras The extras associated with the {@link MediaSession}.
*/
public void setExtras(@Nullable Bundle extras) {
- try {
- mSessionLink.setExtras(extras);
- } catch (RuntimeException e) {
- Log.wtf("Dead object in setExtras.", e);
- }
+ mImpl.setExtras(extras);
}
/**
@@ -515,11 +388,7 @@ public final class MediaSession {
* @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
*/
public final @NonNull RemoteUserInfo getCurrentControllerInfo() {
- if (mCallback == null || mCallback.mCurrentControllerInfo == null) {
- throw new IllegalStateException(
- "This should be called inside of MediaSession.Callback methods");
- }
- return mCallback.mCurrentControllerInfo;
+ return mImpl.getCurrentControllerInfo();
}
/**
@@ -529,17 +398,7 @@ public final class MediaSession {
* @hide
*/
public void notifyRemoteVolumeChanged(VolumeProvider provider) {
- synchronized (mLock) {
- if (provider == null || provider != mVolumeProvider) {
- Log.w(TAG, "Received update from stale volume provider");
- return;
- }
- }
- try {
- mSessionLink.setCurrentVolume(provider.getCurrentVolume());
- } catch (RuntimeException e) {
- Log.e(TAG, "Error in notifyVolumeChanged", e);
- }
+ mImpl.notifyRemoteVolumeChanged(provider);
}
/**
@@ -551,119 +410,7 @@ public final class MediaSession {
*/
@UnsupportedAppUsage
public String getCallingPackage() {
- if (mCallback != null && mCallback.mCurrentControllerInfo != null) {
- return mCallback.mCurrentControllerInfo.getPackageName();
- }
- return null;
- }
-
- private void dispatchPrepare(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_PREPARE, null, null);
- }
-
- private void dispatchPrepareFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras);
- }
-
- private void dispatchPrepareFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_SEARCH, query, extras);
- }
-
- private void dispatchPrepareFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_URI, uri, extras);
- }
-
- private void dispatchPlay(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_PLAY, null, null);
- }
-
- private void dispatchPlayFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras);
- }
-
- private void dispatchPlayFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PLAY_SEARCH, query, extras);
- }
-
- private void dispatchPlayFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
- postToCallback(caller, CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
- }
-
- private void dispatchSkipToItem(RemoteUserInfo caller, long id) {
- postToCallback(caller, CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, null);
- }
-
- private void dispatchPause(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_PAUSE, null, null);
- }
-
- private void dispatchStop(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_STOP, null, null);
- }
-
- private void dispatchNext(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_NEXT, null, null);
- }
-
- private void dispatchPrevious(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_PREVIOUS, null, null);
- }
-
- private void dispatchFastForward(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_FAST_FORWARD, null, null);
- }
-
- private void dispatchRewind(RemoteUserInfo caller) {
- postToCallback(caller, CallbackMessageHandler.MSG_REWIND, null, null);
- }
-
- private void dispatchSeekTo(RemoteUserInfo caller, long pos) {
- postToCallback(caller, CallbackMessageHandler.MSG_SEEK_TO, pos, null);
- }
-
- private void dispatchRate(RemoteUserInfo caller, Rating rating) {
- postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null);
- }
-
- private void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) {
- postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
- }
-
- private void dispatchMediaButton(RemoteUserInfo caller, Intent mediaButtonIntent) {
- postToCallback(caller, CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, null);
- }
-
- private void dispatchMediaButtonDelayed(RemoteUserInfo info, Intent mediaButtonIntent,
- long delay) {
- postToCallbackDelayed(info, CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT,
- mediaButtonIntent, null, delay);
- }
-
- private void dispatchAdjustVolume(RemoteUserInfo caller, int direction) {
- postToCallback(caller, CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, null);
- }
-
- private void dispatchSetVolumeTo(RemoteUserInfo caller, int volume) {
- postToCallback(caller, CallbackMessageHandler.MSG_SET_VOLUME, volume, null);
- }
-
- private void dispatchCommand(RemoteUserInfo caller, String command, Bundle args,
- ResultReceiver resultCb) {
- Command cmd = new Command(command, args, resultCb);
- postToCallback(caller, CallbackMessageHandler.MSG_COMMAND, cmd, null);
- }
-
- private void postToCallback(RemoteUserInfo caller, int what, Object obj, Bundle data) {
- postToCallbackDelayed(caller, what, obj, data, 0);
- }
-
- private void postToCallbackDelayed(RemoteUserInfo caller, int what, Object obj, Bundle data,
- long delay) {
- synchronized (mLock) {
- if (mCallback != null) {
- mCallback.post(caller, what, obj, data, delay);
- }
- }
+ return mImpl.getCallingPackage();
}
/**
@@ -672,17 +419,7 @@ public final class MediaSession {
* @hide
*/
public static boolean isActiveState(int state) {
- switch (state) {
- case PlaybackState.STATE_FAST_FORWARDING:
- case PlaybackState.STATE_REWINDING:
- case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
- case PlaybackState.STATE_SKIPPING_TO_NEXT:
- case PlaybackState.STATE_BUFFERING:
- case PlaybackState.STATE_CONNECTING:
- case PlaybackState.STATE_PLAYING:
- return true;
- }
- return false;
+ return MediaSessionEngine.isActiveState(state);
}
/**
@@ -692,13 +429,13 @@ public final class MediaSession {
*/
public static final class Token implements Parcelable {
- private ControllerLink mBinder;
+ private ControllerLink mControllerLink;
/**
* @hide
*/
- public Token(ControllerLink binder) {
- mBinder = binder;
+ public Token(ControllerLink controllerLink) {
+ mControllerLink = controllerLink;
}
@Override
@@ -708,14 +445,15 @@ public final class MediaSession {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelable(mBinder, flags);
+ dest.writeParcelable(mControllerLink, flags);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
- result = prime * result + ((mBinder == null) ? 0 : mBinder.getBinder().hashCode());
+ result = prime * result + ((mControllerLink == null)
+ ? 0 : mControllerLink.getBinder().hashCode());
return result;
}
@@ -728,17 +466,23 @@ public final class MediaSession {
if (getClass() != obj.getClass())
return false;
Token other = (Token) obj;
- if (mBinder == null) {
- if (other.mBinder != null)
+ if (mControllerLink == null) {
+ if (other.mControllerLink != null) {
return false;
- } else if (!mBinder.getBinder().equals(other.mBinder.getBinder())) {
+ }
+ } else if (!mControllerLink.getBinder().equals(other.mControllerLink.getBinder())) {
return false;
}
return true;
}
- ControllerLink getBinder() {
- return mBinder;
+ /**
+ * Gets the controller link in this token.
+ * @hide
+ */
+ @SystemApi
+ ControllerLink getControllerLink() {
+ return mControllerLink;
}
public static final Parcelable.Creator<Token> CREATOR =
@@ -762,9 +506,7 @@ public final class MediaSession {
*/
public abstract static class Callback {
- private MediaSession mSession;
- private CallbackMessageHandler mHandler;
- private boolean mMediaPlayPauseKeyPending;
+ MediaSessionEngine.MediaButtonEventDelegate mMediaButtonEventDelegate;
public Callback() {
}
@@ -796,110 +538,12 @@ public final class MediaSession {
* @return True if the event was handled, false otherwise.
*/
public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) {
- if (mSession != null && mHandler != null
- && Intent.ACTION_MEDIA_BUTTON.equals(mediaButtonIntent.getAction())) {
- KeyEvent ke = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
- if (ke != null && ke.getAction() == KeyEvent.ACTION_DOWN) {
- PlaybackState state = mSession.mPlaybackState;
- long validActions = state == null ? 0 : state.getActions();
- switch (ke.getKeyCode()) {
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- case KeyEvent.KEYCODE_HEADSETHOOK:
- if (ke.getRepeatCount() > 0) {
- // Consider long-press as a single tap.
- handleMediaPlayPauseKeySingleTapIfPending();
- } else if (mMediaPlayPauseKeyPending) {
- // Consider double tap as the next.
- mHandler.removeMessages(CallbackMessageHandler
- .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
- mMediaPlayPauseKeyPending = false;
- if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
- onSkipToNext();
- }
- } else {
- mMediaPlayPauseKeyPending = true;
- mSession.dispatchMediaButtonDelayed(
- mSession.getCurrentControllerInfo(),
- mediaButtonIntent, ViewConfiguration.getDoubleTapTimeout());
- }
- return true;
- default:
- // If another key is pressed within double tap timeout, consider the
- // pending play/pause as a single tap to handle media keys in order.
- handleMediaPlayPauseKeySingleTapIfPending();
- break;
- }
-
- switch (ke.getKeyCode()) {
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- if ((validActions & PlaybackState.ACTION_PLAY) != 0) {
- onPlay();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- if ((validActions & PlaybackState.ACTION_PAUSE) != 0) {
- onPause();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
- onSkipToNext();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- if ((validActions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
- onSkipToPrevious();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_STOP:
- if ((validActions & PlaybackState.ACTION_STOP) != 0) {
- onStop();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
- if ((validActions & PlaybackState.ACTION_FAST_FORWARD) != 0) {
- onFastForward();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_REWIND:
- if ((validActions & PlaybackState.ACTION_REWIND) != 0) {
- onRewind();
- return true;
- }
- break;
- }
- }
+ if (mMediaButtonEventDelegate != null) {
+ return mMediaButtonEventDelegate.onMediaButtonIntent(mediaButtonIntent);
}
return false;
}
- private void handleMediaPlayPauseKeySingleTapIfPending() {
- if (!mMediaPlayPauseKeyPending) {
- return;
- }
- mMediaPlayPauseKeyPending = false;
- mHandler.removeMessages(CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
- PlaybackState state = mSession.mPlaybackState;
- long validActions = state == null ? 0 : state.getActions();
- boolean isPlaying = state != null
- && state.getState() == PlaybackState.STATE_PLAYING;
- boolean canPlay = (validActions & (PlaybackState.ACTION_PLAY_PAUSE
- | PlaybackState.ACTION_PLAY)) != 0;
- boolean canPause = (validActions & (PlaybackState.ACTION_PLAY_PAUSE
- | PlaybackState.ACTION_PAUSE)) != 0;
- if (isPlaying && canPause) {
- onPause();
- } else if (!isPlaying && canPlay) {
- onPlay();
- }
- }
-
/**
* Override to handle requests to prepare playback. During the preparation, a session should
* not hold audio focus in order to allow other sessions play seamlessly. The state of
@@ -1042,251 +686,14 @@ public final class MediaSession {
*/
public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
}
- }
-
- /**
- * @hide
- */
- public static final class CallbackStub extends SessionCallbackLink.CallbackStub {
- private WeakReference<MediaSession> mMediaSession;
-
- public CallbackStub(MediaSession session) {
- mMediaSession = new WeakReference<>(session);
- }
-
- private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- return new RemoteUserInfo(packageName, pid, uid,
- caller != null ? caller.getBinder() : null);
- }
-
- @Override
- public void onCommand(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchCommand(createRemoteUserInfo(packageName, pid, uid, caller),
- command, args, cb);
- }
- }
-
- @Override
- public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent,
- int sequenceNumber, ResultReceiver cb) {
- MediaSession session = mMediaSession.get();
- try {
- if (session != null) {
- session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, null),
- mediaButtonIntent);
- }
- } finally {
- if (cb != null) {
- cb.send(sequenceNumber, null);
- }
- }
- }
-
- @Override
- public void onMediaButtonFromController(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Intent mediaButtonIntent) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid, caller),
- mediaButtonIntent);
- }
- }
-
- @Override
- public void onPrepare(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onPrepareFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId,
- Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPrepareFromMediaId(
- createRemoteUserInfo(packageName, pid, uid, caller), mediaId, extras);
- }
- }
-
- @Override
- public void onPrepareFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query,
- Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPrepareFromSearch(
- createRemoteUserInfo(packageName, pid, uid, caller), query, extras);
- }
- }
-
- @Override
- public void onPrepareFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPrepareFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
- uri, extras);
- }
- }
-
- @Override
- public void onPlay(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPlay(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onPlayFromMediaId(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String mediaId,
- Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPlayFromMediaId(createRemoteUserInfo(packageName, pid, uid, caller),
- mediaId, extras);
- }
- }
-
- @Override
- public void onPlayFromSearch(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String query,
- Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPlayFromSearch(createRemoteUserInfo(packageName, pid, uid, caller),
- query, extras);
- }
- }
-
- @Override
- public void onPlayFromUri(String packageName, int pid, int uid,
- ControllerCallbackLink caller, Uri uri, Bundle extras) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPlayFromUri(createRemoteUserInfo(packageName, pid, uid, caller),
- uri, extras);
- }
- }
-
- @Override
- public void onSkipToTrack(String packageName, int pid, int uid,
- ControllerCallbackLink caller, long id) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchSkipToItem(createRemoteUserInfo(packageName, pid, uid, caller), id);
- }
- }
-
- @Override
- public void onPause(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPause(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
- @Override
- public void onStop(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchStop(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onNext(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchNext(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onPrevious(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onFastForward(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchFastForward(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onRewind(String packageName, int pid, int uid,
- ControllerCallbackLink caller) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchRewind(createRemoteUserInfo(packageName, pid, uid, caller));
- }
- }
-
- @Override
- public void onSeekTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, long pos) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchSeekTo(createRemoteUserInfo(packageName, pid, uid, caller), pos);
- }
- }
-
- @Override
- public void onRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
- Rating rating) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchRate(createRemoteUserInfo(packageName, pid, uid, caller), rating);
- }
- }
-
- @Override
- public void onCustomAction(String packageName, int pid, int uid,
- ControllerCallbackLink caller, String action, Bundle args) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchCustomAction(createRemoteUserInfo(packageName, pid, uid, caller),
- action, args);
- }
- }
-
- @Override
- public void onAdjustVolume(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int direction) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchAdjustVolume(createRemoteUserInfo(packageName, pid, uid, caller),
- direction);
- }
- }
-
- @Override
- public void onSetVolumeTo(String packageName, int pid, int uid,
- ControllerCallbackLink caller, int value) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchSetVolumeTo(createRemoteUserInfo(packageName, pid, uid, caller),
- value);
- }
+ /**
+ * @hide
+ */
+ @SystemApi
+ public void onSetMediaButtonEventDelegate(
+ @NonNull MediaSessionEngine.MediaButtonEventDelegate delegate) {
+ mMediaButtonEventDelegate = delegate;
}
}
@@ -1300,7 +707,7 @@ public final class MediaSession {
*/
public static final int UNKNOWN_ID = -1;
- private final MediaDescription mDescription;
+ private final MediaSessionEngine.QueueItem mImpl;
@UnsupportedAppUsage
private final long mId;
@@ -1312,39 +719,32 @@ public final class MediaSession {
* play queue and cannot be {@link #UNKNOWN_ID}.
*/
public QueueItem(MediaDescription description, long id) {
- if (description == null) {
- throw new IllegalArgumentException("Description cannot be null.");
- }
- if (id == UNKNOWN_ID) {
- throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID");
- }
- mDescription = description;
+ mImpl = new MediaSessionEngine.QueueItem(description, id);
mId = id;
}
private QueueItem(Parcel in) {
- mDescription = MediaDescription.CREATOR.createFromParcel(in);
- mId = in.readLong();
+ mImpl = new MediaSessionEngine.QueueItem(in);
+ mId = mImpl.getQueueId();
}
/**
* Get the description for this item.
*/
public MediaDescription getDescription() {
- return mDescription;
+ return mImpl.getDescription();
}
/**
* Get the queue id for this item.
*/
public long getQueueId() {
- return mId;
+ return mImpl.getQueueId();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
- mDescription.writeToParcel(dest, flags);
- dest.writeLong(mId);
+ mImpl.writeToParcel(dest, flags);
}
@Override
@@ -1368,9 +768,7 @@ public final class MediaSession {
@Override
public String toString() {
- return "MediaSession.QueueItem {" +
- "Description=" + mDescription +
- ", Id=" + mId + " }";
+ return mImpl.toString();
}
@Override
@@ -1383,167 +781,7 @@ public final class MediaSession {
return false;
}
- final QueueItem item = (QueueItem) o;
- if (mId != item.mId) {
- return false;
- }
-
- if (!Objects.equals(mDescription, item.mDescription)) {
- return false;
- }
-
- return true;
- }
- }
-
- private static final class Command {
- public final String command;
- public final Bundle extras;
- public final ResultReceiver stub;
-
- public Command(String command, Bundle extras, ResultReceiver stub) {
- this.command = command;
- this.extras = extras;
- this.stub = stub;
- }
- }
-
- private class CallbackMessageHandler extends Handler {
- private static final int MSG_COMMAND = 1;
- private static final int MSG_MEDIA_BUTTON = 2;
- private static final int MSG_PREPARE = 3;
- private static final int MSG_PREPARE_MEDIA_ID = 4;
- private static final int MSG_PREPARE_SEARCH = 5;
- private static final int MSG_PREPARE_URI = 6;
- private static final int MSG_PLAY = 7;
- private static final int MSG_PLAY_MEDIA_ID = 8;
- private static final int MSG_PLAY_SEARCH = 9;
- private static final int MSG_PLAY_URI = 10;
- private static final int MSG_SKIP_TO_ITEM = 11;
- private static final int MSG_PAUSE = 12;
- private static final int MSG_STOP = 13;
- private static final int MSG_NEXT = 14;
- private static final int MSG_PREVIOUS = 15;
- private static final int MSG_FAST_FORWARD = 16;
- private static final int MSG_REWIND = 17;
- private static final int MSG_SEEK_TO = 18;
- private static final int MSG_RATE = 19;
- private static final int MSG_CUSTOM_ACTION = 20;
- private static final int MSG_ADJUST_VOLUME = 21;
- private static final int MSG_SET_VOLUME = 22;
- private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23;
-
- private MediaSession.Callback mCallback;
- private RemoteUserInfo mCurrentControllerInfo;
-
- public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
- super(looper);
- mCallback = callback;
- mCallback.mHandler = this;
- }
-
- public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) {
- Pair<RemoteUserInfo, Object> objWithCaller = Pair.create(caller, obj);
- Message msg = obtainMessage(what, objWithCaller);
- msg.setAsynchronous(true);
- msg.setData(data);
- if (delayMs > 0) {
- sendMessageDelayed(msg, delayMs);
- } else {
- sendMessage(msg);
- }
- }
-
- @Override
- public void handleMessage(Message msg) {
- mCurrentControllerInfo = ((Pair<RemoteUserInfo, Object>) msg.obj).first;
-
- VolumeProvider vp;
- Object obj = ((Pair<RemoteUserInfo, Object>) msg.obj).second;
-
- switch (msg.what) {
- case MSG_COMMAND:
- Command cmd = (Command) obj;
- mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
- break;
- case MSG_MEDIA_BUTTON:
- mCallback.onMediaButtonEvent((Intent) obj);
- break;
- case MSG_PREPARE:
- mCallback.onPrepare();
- break;
- case MSG_PREPARE_MEDIA_ID:
- mCallback.onPrepareFromMediaId((String) obj, msg.getData());
- break;
- case MSG_PREPARE_SEARCH:
- mCallback.onPrepareFromSearch((String) obj, msg.getData());
- break;
- case MSG_PREPARE_URI:
- mCallback.onPrepareFromUri((Uri) obj, msg.getData());
- break;
- case MSG_PLAY:
- mCallback.onPlay();
- break;
- case MSG_PLAY_MEDIA_ID:
- mCallback.onPlayFromMediaId((String) obj, msg.getData());
- break;
- case MSG_PLAY_SEARCH:
- mCallback.onPlayFromSearch((String) obj, msg.getData());
- break;
- case MSG_PLAY_URI:
- mCallback.onPlayFromUri((Uri) obj, msg.getData());
- break;
- case MSG_SKIP_TO_ITEM:
- mCallback.onSkipToQueueItem((Long) obj);
- break;
- case MSG_PAUSE:
- mCallback.onPause();
- break;
- case MSG_STOP:
- mCallback.onStop();
- break;
- case MSG_NEXT:
- mCallback.onSkipToNext();
- break;
- case MSG_PREVIOUS:
- mCallback.onSkipToPrevious();
- break;
- case MSG_FAST_FORWARD:
- mCallback.onFastForward();
- break;
- case MSG_REWIND:
- mCallback.onRewind();
- break;
- case MSG_SEEK_TO:
- mCallback.onSeekTo((Long) obj);
- break;
- case MSG_RATE:
- mCallback.onSetRating((Rating) obj);
- break;
- case MSG_CUSTOM_ACTION:
- mCallback.onCustomAction((String) obj, msg.getData());
- break;
- case MSG_ADJUST_VOLUME:
- synchronized (mLock) {
- vp = mVolumeProvider;
- }
- if (vp != null) {
- vp.onAdjustVolume((int) obj);
- }
- break;
- case MSG_SET_VOLUME:
- synchronized (mLock) {
- vp = mVolumeProvider;
- }
- if (vp != null) {
- vp.onSetVolumeTo((int) obj);
- }
- break;
- case MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT:
- mCallback.handleMediaPlayPauseKeySingleTapIfPending();
- break;
- }
- mCurrentControllerInfo = null;
+ return mImpl.equals(((QueueItem) o).mImpl);
}
}
}
diff --git a/media/java/android/media/session/MediaSessionEngine.java b/media/java/android/media/session/MediaSessionEngine.java
new file mode 100644
index 000000000000..f159a9538835
--- /dev/null
+++ b/media/java/android/media/session/MediaSessionEngine.java
@@ -0,0 +1,1479 @@
+/*
+ * 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.session;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioAttributes;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.Rating;
+import android.media.VolumeProvider;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+import android.service.media.MediaBrowserService;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+@SystemApi
+public final class MediaSessionEngine implements AutoCloseable {
+ private static final String TAG = MediaSession.TAG;
+
+ private final Object mLock = new Object();
+ private final int mMaxBitmapSize;
+
+ private final MediaSession.Token mSessionToken;
+ private final MediaController mController;
+ private final SessionLink mSessionLink;
+ private final SessionCallbackLink mCbLink;
+
+ // Do not change the name of mCallbackWrapper. Support lib accesses this by using reflection.
+ @UnsupportedAppUsage
+ private CallbackMessageHandler mCallbackHandler;
+ private VolumeProvider mVolumeProvider;
+ private PlaybackState mPlaybackState;
+
+ private boolean mActive = false;
+
+ /**
+ * Creates a new session. The session will automatically be registered with
+ * the system but will not be published until {@link #setActive(boolean)
+ * setActive(true)} is called. You must call {@link #close()} when
+ * finished with the session.
+ *
+ * @param context The context to use to create the session.
+ * @param sessionLink A session link for the binder of MediaSessionRecord
+ * @param cbStub A callback link that handles incoming command to {@link MediaSession.Callback}.
+ */
+ public MediaSessionEngine(@NonNull Context context, @NonNull SessionLink sessionLink,
+ @NonNull SessionCallbackLink cbLink, @NonNull CallbackStub cbStub, int maxBitmapSize) {
+ mSessionLink = sessionLink;
+ mCbLink = cbLink;
+ mMaxBitmapSize = maxBitmapSize;
+
+ cbStub.setSessionImpl(this);
+ mSessionToken = new MediaSession.Token(mSessionLink.getController());
+ mController = new MediaController(context, mSessionToken);
+ }
+
+ /**
+ * Set the callback to receive updates for the MediaSession. This includes
+ * media button events and transport controls. The caller's thread will be
+ * used to post updates.
+ * <p>
+ * Set the callback to null to stop receiving updates.
+ *
+ * @param callback The callback object
+ */
+ public void setCallback(@Nullable MediaSession.Callback callback) {
+ setCallback(callback, new Handler());
+ }
+
+ /**
+ * Set the callback to receive updates for the MediaSession. This includes
+ * media button events and transport controls.
+ * <p>
+ * Set the callback to null to stop receiving updates.
+ *
+ * @param callback The callback to receive updates on.
+ * @param handler The handler that events should be posted on.
+ */
+ public void setCallback(@Nullable MediaSession.Callback callback, @NonNull Handler handler) {
+ setCallbackInternal(callback == null ? null : new CallbackWrapper(callback), handler);
+ }
+
+ private void setCallbackInternal(CallbackWrapper callback, Handler handler) {
+ synchronized (mLock) {
+ if (mCallbackHandler != null) {
+ // We're updating the callback, clear the session from the old one.
+ mCallbackHandler.mCallbackWrapper.mSessionImpl = null;
+ mCallbackHandler.removeCallbacksAndMessages(null);
+ }
+ if (callback == null) {
+ mCallbackHandler = null;
+ return;
+ }
+ callback.mSessionImpl = this;
+ CallbackMessageHandler msgHandler = new CallbackMessageHandler(handler.getLooper(),
+ callback);
+ mCallbackHandler = msgHandler;
+ }
+ }
+
+ /**
+ * Set an intent for launching UI for this Session. This can be used as a
+ * quick link to an ongoing media screen. The intent should be for an
+ * activity that may be started using {@link Activity#startActivity(Intent)}.
+ *
+ * @param pi The intent to launch to show UI for this Session.
+ */
+ public void setSessionActivity(@Nullable PendingIntent pi) {
+ try {
+ mSessionLink.setLaunchPendingIntent(pi);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Failure in setLaunchPendingIntent.", e);
+ }
+ }
+
+ /**
+ * Set a pending intent for your media button receiver to allow restarting
+ * playback after the session has been stopped. If your app is started in
+ * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
+ * the pending intent.
+ *
+ * @param mbr The {@link PendingIntent} to send the media button event to.
+ */
+ public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
+ try {
+ mSessionLink.setMediaButtonReceiver(mbr);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
+ }
+ }
+
+ /**
+ * Set any flags for the session.
+ *
+ * @param flags The flags to set for this session.
+ */
+ public void setFlags(@MediaSession.SessionFlags int flags) {
+ try {
+ mSessionLink.setFlags(flags);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Failure in setFlags.", e);
+ }
+ }
+
+ /**
+ * Set the attributes for this session's audio. This will affect the
+ * system's volume handling for this session. If
+ * {@link #setPlaybackToRemote} was previously called it will stop receiving
+ * volume commands and the system will begin sending volume changes to the
+ * appropriate stream.
+ * <p>
+ * By default sessions use attributes for media.
+ *
+ * @param attributes The {@link AudioAttributes} for this session's audio.
+ */
+ public void setPlaybackToLocal(AudioAttributes attributes) {
+ if (attributes == null) {
+ throw new IllegalArgumentException("Attributes cannot be null for local playback.");
+ }
+ try {
+ mSessionLink.setPlaybackToLocal(attributes);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
+ }
+ }
+
+ /**
+ * Configure this session to use remote volume handling. This must be called
+ * to receive volume button events, otherwise the system will adjust the
+ * appropriate stream volume for this session. If
+ * {@link #setPlaybackToLocal} was previously called the system will stop
+ * handling volume changes for this session and pass them to the volume
+ * provider instead.
+ *
+ * @param volumeProvider The provider that will handle volume changes. May
+ * not be null.
+ */
+ public void setPlaybackToRemote(@NonNull VolumeProvider volumeProvider) {
+ if (volumeProvider == null) {
+ throw new IllegalArgumentException("volumeProvider may not be null!");
+ }
+ synchronized (mLock) {
+ mVolumeProvider = volumeProvider;
+ }
+ volumeProvider.setCallback(new VolumeProvider.Callback() {
+ @Override
+ public void onVolumeChanged(VolumeProvider volumeProvider) {
+ notifyRemoteVolumeChanged(volumeProvider);
+ }
+ });
+
+ try {
+ mSessionLink.setPlaybackToRemote(volumeProvider.getVolumeControl(),
+ volumeProvider.getMaxVolume());
+ mSessionLink.setCurrentVolume(volumeProvider.getCurrentVolume());
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
+ }
+ }
+
+ /**
+ * Set if this session is currently active and ready to receive commands. If
+ * set to false your session's controller may not be discoverable. You must
+ * set the session to active before it can start receiving media button
+ * events or transport commands.
+ *
+ * @param active Whether this session is active or not.
+ */
+ public void setActive(boolean active) {
+ if (mActive == active) {
+ return;
+ }
+ try {
+ mSessionLink.setActive(active);
+ mActive = active;
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Failure in setActive.", e);
+ }
+ }
+
+ /**
+ * Get the current active state of this session.
+ *
+ * @return True if the session is active, false otherwise.
+ */
+ public boolean isActive() {
+ return mActive;
+ }
+
+ /**
+ * Send a proprietary event to all MediaControllers listening to this
+ * Session. It's up to the Controller/Session owner to determine the meaning
+ * of any events.
+ *
+ * @param event The name of the event to send
+ * @param extras Any extras included with the event
+ */
+ public void sendSessionEvent(@NonNull String event, @Nullable Bundle extras) {
+ if (TextUtils.isEmpty(event)) {
+ throw new IllegalArgumentException("event cannot be null or empty");
+ }
+ try {
+ mSessionLink.sendEvent(event, extras);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Error sending event", e);
+ }
+ }
+
+ /**
+ * This must be called when an app has finished performing playback. If
+ * playback is expected to start again shortly the session can be left open,
+ * but it must be released if your activity or service is being destroyed.
+ */
+ public void close() {
+ try {
+ mSessionLink.destroySession();
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Error releasing session: ", e);
+ }
+ }
+
+ /**
+ * Retrieve a token object that can be used by apps to create a
+ * {@link MediaController} for interacting with this session. The owner of
+ * the session is responsible for deciding how to distribute these tokens.
+ *
+ * @return A token that can be used to create a MediaController for this
+ * session
+ */
+ public @NonNull MediaSession.Token getSessionToken() {
+ return mSessionToken;
+ }
+
+ /**
+ * Get a controller for this session. This is a convenience method to avoid
+ * having to cache your own controller in process.
+ *
+ * @return A controller for this session.
+ */
+ public @NonNull MediaController getController() {
+ return mController;
+ }
+
+ /**
+ * Update the current playback state.
+ *
+ * @param state The current state of playback
+ */
+ public void setPlaybackState(@Nullable PlaybackState state) {
+ mPlaybackState = state;
+ try {
+ mSessionLink.setPlaybackState(state);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Dead object in setPlaybackState.", e);
+ }
+ }
+
+ /**
+ * Update the current metadata. New metadata can be created using
+ * {@link android.media.MediaMetadata.Builder}. This operation may take time proportional to
+ * the size of the bitmap to replace large bitmaps with a scaled down copy.
+ *
+ * @param metadata The new metadata
+ * @see android.media.MediaMetadata.Builder#putBitmap
+ */
+ public void setMetadata(@Nullable MediaMetadata metadata) {
+ long duration = -1;
+ int fields = 0;
+ MediaDescription description = null;
+ if (metadata != null) {
+ metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build();
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
+ duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
+ }
+ fields = metadata.size();
+ description = metadata.getDescription();
+ }
+ String metadataDescription = "size=" + fields + ", description=" + description;
+
+ try {
+ mSessionLink.setMetadata(metadata, duration, metadataDescription);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Dead object in setPlaybackState.", e);
+ }
+ }
+
+ /**
+ * Update the list of items in the play queue. It is an ordered list and
+ * should contain the current item, and previous or upcoming items if they
+ * exist. Specify null if there is no current play queue.
+ * <p>
+ * The queue should be of reasonable size. If the play queue is unbounded
+ * within your app, it is better to send a reasonable amount in a sliding
+ * window instead.
+ *
+ * @param queue A list of items in the play queue.
+ */
+ public void setQueue(@Nullable List<MediaSession.QueueItem> queue) {
+ try {
+ mSessionLink.setQueue(queue);
+ } catch (RuntimeException e) {
+ Log.wtf("Dead object in setQueue.", e);
+ }
+ }
+
+ /**
+ * Set the title of the play queue. The UI should display this title along
+ * with the play queue itself.
+ * e.g. "Play Queue", "Now Playing", or an album name.
+ *
+ * @param title The title of the play queue.
+ */
+ public void setQueueTitle(@Nullable CharSequence title) {
+ try {
+ mSessionLink.setQueueTitle(title);
+ } catch (RuntimeException e) {
+ Log.wtf("Dead object in setQueueTitle.", e);
+ }
+ }
+
+ /**
+ * Set the style of rating used by this session. Apps trying to set the
+ * rating should use this style. Must be one of the following:
+ * <ul>
+ * <li>{@link Rating#RATING_NONE}</li>
+ * <li>{@link Rating#RATING_3_STARS}</li>
+ * <li>{@link Rating#RATING_4_STARS}</li>
+ * <li>{@link Rating#RATING_5_STARS}</li>
+ * <li>{@link Rating#RATING_HEART}</li>
+ * <li>{@link Rating#RATING_PERCENTAGE}</li>
+ * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
+ * </ul>
+ */
+ public void setRatingType(@Rating.Style int type) {
+ try {
+ mSessionLink.setRatingType(type);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error in setRatingType.", e);
+ }
+ }
+
+ /**
+ * Set some extras that can be associated with the {@link MediaSession}. No assumptions should
+ * be made as to how a {@link MediaController} will handle these extras.
+ * Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
+ *
+ * @param extras The extras associated with the {@link MediaSession}.
+ */
+ public void setExtras(@Nullable Bundle extras) {
+ try {
+ mSessionLink.setExtras(extras);
+ } catch (RuntimeException e) {
+ Log.wtf("Dead object in setExtras.", e);
+ }
+ }
+
+ /**
+ * Gets the controller information who sent the current request.
+ * <p>
+ * Note: This is only valid while in a request callback, such as
+ * {@link MediaSession.Callback#onPlay}.
+ *
+ * @throws IllegalStateException If this method is called outside of
+ * {@link MediaSession.Callback} methods.
+ * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
+ */
+ public @NonNull RemoteUserInfo getCurrentControllerInfo() {
+ if (mCallbackHandler == null || mCallbackHandler.mCurrentControllerInfo == null) {
+ throw new IllegalStateException(
+ "This should be called inside of MediaSession.Callback methods");
+ }
+ return mCallbackHandler.mCurrentControllerInfo;
+ }
+
+ /**
+ * Notify the system that the remote volume changed.
+ *
+ * @param provider The provider that is handling volume changes.
+ * @hide
+ */
+ public void notifyRemoteVolumeChanged(VolumeProvider provider) {
+ synchronized (mLock) {
+ if (provider == null || provider != mVolumeProvider) {
+ Log.w(TAG, "Received update from stale volume provider");
+ return;
+ }
+ }
+ try {
+ mSessionLink.setCurrentVolume(provider.getCurrentVolume());
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error in notifyVolumeChanged", e);
+ }
+ }
+
+ /**
+ * Returns the name of the package that sent the last media button, transport control, or
+ * command from controllers and the system. This is only valid while in a request callback, such
+ * as {@link MediaSession.Callback#onPlay}.
+ */
+ public String getCallingPackage() {
+ if (mCallbackHandler != null && mCallbackHandler.mCurrentControllerInfo != null) {
+ return mCallbackHandler.mCurrentControllerInfo.getPackageName();
+ }
+ return null;
+ }
+
+ private void dispatchPrepare(RemoteUserInfo caller) {
+ postToCallback(caller, CallbackMessageHandler.MSG_PREPARE, null, null);
+ }
+
+ private void dispatchPrepareFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
+ postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_MEDIA_ID, mediaId, extras);
+ }
+
+ private void dispatchPrepareFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
+ postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_SEARCH, query, extras);
+ }
+
+ private void dispatchPrepareFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
+ postToCallback(caller, CallbackMessageHandler.MSG_PREPARE_URI, uri, extras);
+ }
+
+ private void dispatchPlay(RemoteUserInfo caller) {
+ postToCallback(caller, CallbackMessageHandler.MSG_PLAY, null, null);
+ }
+
+ private void dispatchPlayFromMediaId(RemoteUserInfo caller, String mediaId, Bundle extras) {
+ postToCallback(caller, CallbackMessageHandler.MSG_PLAY_MEDIA_ID, mediaId, extras);
+ }
+
+ private void dispatchPlayFromSearch(RemoteUserInfo caller, String query, Bundle extras) {
+ postToCallback(caller, CallbackMessageHandler.MSG_PLAY_SEARCH, query, extras);
+ }
+
+ private void dispatchPlayFromUri(RemoteUserInfo caller, Uri uri, Bundle extras) {
+ postToCallback(caller, CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
+ }
+
+ private void dispatchSkipToItem(RemoteUserInfo caller, long id) {
+ postToCallback(caller, CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, null);
+ }
+
+ private void dispatchPause(RemoteUserInfo caller) {
+ postToCallback(caller, CallbackMessageHandler.MSG_PAUSE, null, null);
+ }
+
+ private void dispatchStop(RemoteUserInfo caller) {
+ postToCallback(caller, CallbackMessageHandler.MSG_STOP, null, null);
+ }
+
+ private void dispatchNext(RemoteUserInfo caller) {
+ postToCallback(caller, CallbackMessageHandler.MSG_NEXT, null, null);
+ }
+
+ private void dispatchPrevious(RemoteUserInfo caller) {
+ postToCallback(caller, CallbackMessageHandler.MSG_PREVIOUS, null, null);
+ }
+
+ private void dispatchFastForward(RemoteUserInfo caller) {
+ postToCallback(caller, CallbackMessageHandler.MSG_FAST_FORWARD, null, null);
+ }
+
+ private void dispatchRewind(RemoteUserInfo caller) {
+ postToCallback(caller, CallbackMessageHandler.MSG_REWIND, null, null);
+ }
+
+ private void dispatchSeekTo(RemoteUserInfo caller, long pos) {
+ postToCallback(caller, CallbackMessageHandler.MSG_SEEK_TO, pos, null);
+ }
+
+ private void dispatchRate(RemoteUserInfo caller, Rating rating) {
+ postToCallback(caller, CallbackMessageHandler.MSG_RATE, rating, null);
+ }
+
+ private void dispatchCustomAction(RemoteUserInfo caller, String action, Bundle args) {
+ postToCallback(caller, CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
+ }
+
+ private void dispatchMediaButton(RemoteUserInfo caller, Intent mediaButtonIntent) {
+ postToCallback(caller, CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, null);
+ }
+
+ private void dispatchMediaButtonDelayed(RemoteUserInfo info, Intent mediaButtonIntent,
+ long delay) {
+ postToCallbackDelayed(info, CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT,
+ mediaButtonIntent, null, delay);
+ }
+
+ private void dispatchAdjustVolume(RemoteUserInfo caller, int direction) {
+ postToCallback(caller, CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, null);
+ }
+
+ private void dispatchSetVolumeTo(RemoteUserInfo caller, int volume) {
+ postToCallback(caller, CallbackMessageHandler.MSG_SET_VOLUME, volume, null);
+ }
+
+ private void dispatchCommand(RemoteUserInfo caller, String command, Bundle args,
+ ResultReceiver resultCb) {
+ Command cmd = new Command(command, args, resultCb);
+ postToCallback(caller, CallbackMessageHandler.MSG_COMMAND, cmd, null);
+ }
+
+ private void postToCallback(RemoteUserInfo caller, int what, Object obj, Bundle data) {
+ postToCallbackDelayed(caller, what, obj, data, 0);
+ }
+
+ private void postToCallbackDelayed(RemoteUserInfo caller, int what, Object obj, Bundle data,
+ long delay) {
+ synchronized (mLock) {
+ if (mCallbackHandler != null) {
+ mCallbackHandler.post(caller, what, obj, data, delay);
+ }
+ }
+ }
+
+ /**
+ * Return true if this is considered an active playback state.
+ */
+ public static boolean isActiveState(int state) {
+ switch (state) {
+ case PlaybackState.STATE_FAST_FORWARDING:
+ case PlaybackState.STATE_REWINDING:
+ case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
+ case PlaybackState.STATE_SKIPPING_TO_NEXT:
+ case PlaybackState.STATE_BUFFERING:
+ case PlaybackState.STATE_CONNECTING:
+ case PlaybackState.STATE_PLAYING:
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Interface for handling MediaButtoneEvent
+ */
+ public interface MediaButtonEventDelegate {
+ /**
+ * Called when a media button is pressed and this session has the
+ * highest priority or a controller sends a media button event to the
+ * session.
+ *
+ * @param mediaButtonIntent an intent containing the KeyEvent as an extra
+ * @return True if the event was handled, false otherwise.
+ */
+ boolean onMediaButtonIntent(Intent mediaButtonIntent);
+ }
+
+ /**
+ * Receives media buttons, transport controls, and commands from controllers
+ * and the system. A callback may be set using {@link #setCallback}.
+ * @hide
+ */
+ public static class CallbackWrapper implements MediaButtonEventDelegate {
+
+ private final MediaSession.Callback mCallback;
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ MediaSessionEngine mSessionImpl;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ CallbackMessageHandler mHandler;
+ private boolean mMediaPlayPauseKeyPending;
+
+ public CallbackWrapper(MediaSession.Callback callback) {
+ mCallback = callback;
+ if (mCallback != null) {
+ mCallback.onSetMediaButtonEventDelegate(this);
+ }
+ }
+
+ /**
+ * Called when a controller has sent a command to this session.
+ * The owner of the session may handle custom commands but is not
+ * required to.
+ *
+ * @param command The command name.
+ * @param args Optional parameters for the command, may be null.
+ * @param cb A result receiver to which a result may be sent by the command, may be null.
+ */
+ public void onCommand(@NonNull String command, @Nullable Bundle args,
+ @Nullable ResultReceiver cb) {
+ if (mCallback != null) {
+ mCallback.onCommand(command, args, cb);
+ }
+ }
+
+ /**
+ * Called when a media button is pressed and this session has the
+ * highest priority or a controller sends a media button event to the
+ * session. The default behavior will call the relevant method if the
+ * action for it was set.
+ * <p>
+ * The intent will be of type {@link Intent#ACTION_MEDIA_BUTTON} with a
+ * KeyEvent in {@link Intent#EXTRA_KEY_EVENT}
+ *
+ * @param mediaButtonIntent an intent containing the KeyEvent as an
+ * extra
+ * @return True if the event was handled, false otherwise.
+ */
+ public boolean onMediaButtonEvent(@NonNull Intent mediaButtonIntent) {
+ return mCallback == null ? false : mCallback.onMediaButtonEvent(mediaButtonIntent);
+ }
+
+ private void handleMediaPlayPauseKeySingleTapIfPending() {
+ if (!mMediaPlayPauseKeyPending) {
+ return;
+ }
+ mMediaPlayPauseKeyPending = false;
+ mHandler.removeMessages(CallbackMessageHandler.MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
+ PlaybackState state = mSessionImpl.mPlaybackState;
+ long validActions = state == null ? 0 : state.getActions();
+ boolean isPlaying = state != null
+ && state.getState() == PlaybackState.STATE_PLAYING;
+ boolean canPlay = (validActions & (PlaybackState.ACTION_PLAY_PAUSE
+ | PlaybackState.ACTION_PLAY)) != 0;
+ boolean canPause = (validActions & (PlaybackState.ACTION_PLAY_PAUSE
+ | PlaybackState.ACTION_PAUSE)) != 0;
+ if (isPlaying && canPause) {
+ onPause();
+ } else if (!isPlaying && canPlay) {
+ onPlay();
+ }
+ }
+
+ /**
+ * Override to handle requests to prepare playback. During the preparation, a session should
+ * not hold audio focus in order to allow other sessions play seamlessly. The state of
+ * playback should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is
+ * done.
+ */
+ public void onPrepare() {
+ if (mCallback != null) {
+ mCallback.onPrepare();
+ }
+ }
+
+ /**
+ * Override to handle requests to prepare for playing a specific mediaId that was provided
+ * by your app's {@link MediaBrowserService}. During the preparation, a session should not
+ * hold audio focus in order to allow other sessions play seamlessly. The state of playback
+ * should be updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+ * The playback of the prepared content should start in the implementation of
+ * {@link #onPlay}. Override {@link #onPlayFromMediaId} to handle requests for starting
+ * playback without preparation.
+ */
+ public void onPrepareFromMediaId(String mediaId, Bundle extras) {
+ if (mCallback != null) {
+ mCallback.onPrepareFromMediaId(mediaId, extras);
+ }
+ }
+
+ /**
+ * Override to handle requests to prepare playback from a search query. An empty query
+ * indicates that the app may prepare any music. The implementation should attempt to make a
+ * smart choice about what to play. During the preparation, a session should not hold audio
+ * focus in order to allow other sessions play seamlessly. The state of playback should be
+ * updated to {@link PlaybackState#STATE_PAUSED} after the preparation is done. The playback
+ * of the prepared content should start in the implementation of {@link #onPlay}. Override
+ * {@link #onPlayFromSearch} to handle requests for starting playback without preparation.
+ */
+ public void onPrepareFromSearch(String query, Bundle extras) {
+ if (mCallback != null) {
+ mCallback.onPrepareFromSearch(query, extras);
+ }
+ }
+
+ /**
+ * Override to handle requests to prepare a specific media item represented by a URI.
+ * During the preparation, a session should not hold audio focus in order to allow
+ * other sessions play seamlessly. The state of playback should be updated to
+ * {@link PlaybackState#STATE_PAUSED} after the preparation is done.
+ * The playback of the prepared content should start in the implementation of
+ * {@link #onPlay}. Override {@link #onPlayFromUri} to handle requests
+ * for starting playback without preparation.
+ */
+ public void onPrepareFromUri(Uri uri, Bundle extras) {
+ if (mCallback != null) {
+ mCallback.onPrepareFromUri(uri, extras);
+ }
+ }
+
+ /**
+ * Override to handle requests to begin playback.
+ */
+ public void onPlay() {
+ if (mCallback != null) {
+ mCallback.onPlay();
+ }
+ }
+
+ /**
+ * Override to handle requests to begin playback from a search query. An
+ * empty query indicates that the app may play any music. The
+ * implementation should attempt to make a smart choice about what to
+ * play.
+ */
+ public void onPlayFromSearch(String query, Bundle extras) {
+ if (mCallback != null) {
+ mCallback.onPlayFromSearch(query, extras);
+ }
+ }
+
+ /**
+ * Override to handle requests to play a specific mediaId that was
+ * provided by your app's {@link MediaBrowserService}.
+ */
+ public void onPlayFromMediaId(String mediaId, Bundle extras) {
+ if (mCallback != null) {
+ mCallback.onPlayFromMediaId(mediaId, extras);
+ }
+ }
+
+ /**
+ * Override to handle requests to play a specific media item represented by a URI.
+ */
+ public void onPlayFromUri(Uri uri, Bundle extras) {
+ if (mCallback != null) {
+ mCallback.onPlayFromUri(uri, extras);
+ }
+ }
+
+ /**
+ * Override to handle requests to play an item with a given id from the
+ * play queue.
+ */
+ public void onSkipToQueueItem(long id) {
+ if (mCallback != null) {
+ mCallback.onSkipToQueueItem(id);
+ }
+ }
+
+ /**
+ * Override to handle requests to pause playback.
+ */
+ public void onPause() {
+ if (mCallback != null) {
+ mCallback.onPause();
+ }
+ }
+
+ /**
+ * Override to handle requests to skip to the next media item.
+ */
+ public void onSkipToNext() {
+ if (mCallback != null) {
+ mCallback.onSkipToNext();
+ }
+ }
+
+ /**
+ * Override to handle requests to skip to the previous media item.
+ */
+ public void onSkipToPrevious() {
+ if (mCallback != null) {
+ mCallback.onSkipToPrevious();
+ }
+ }
+
+ /**
+ * Override to handle requests to fast forward.
+ */
+ public void onFastForward() {
+ if (mCallback != null) {
+ mCallback.onFastForward();
+ }
+ }
+
+ /**
+ * Override to handle requests to rewind.
+ */
+ public void onRewind() {
+ if (mCallback != null) {
+ mCallback.onRewind();
+ }
+ }
+
+ /**
+ * Override to handle requests to stop playback.
+ */
+ public void onStop() {
+ if (mCallback != null) {
+ mCallback.onStop();
+ }
+ }
+
+ /**
+ * Override to handle requests to seek to a specific position in ms.
+ *
+ * @param pos New position to move to, in milliseconds.
+ */
+ public void onSeekTo(long pos) {
+ if (mCallback != null) {
+ mCallback.onSeekTo(pos);
+ }
+ }
+
+ /**
+ * Override to handle the item being rated.
+ *
+ * @param rating
+ */
+ public void onSetRating(@NonNull Rating rating) {
+ if (mCallback != null) {
+ mCallback.onSetRating(rating);
+ }
+ }
+
+ /**
+ * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
+ * performed.
+ *
+ * @param action The action that was originally sent in the
+ * {@link PlaybackState.CustomAction}.
+ * @param extras Optional extras specified by the {@link MediaController}.
+ */
+ public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
+ if (mCallback != null) {
+ mCallback.onCustomAction(action, extras);
+ }
+ }
+
+ @Override
+ public boolean onMediaButtonIntent(Intent mediaButtonIntent) {
+ if (mSessionImpl != null && mHandler != null
+ && Intent.ACTION_MEDIA_BUTTON.equals(mediaButtonIntent.getAction())) {
+ KeyEvent ke = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+ if (ke != null && ke.getAction() == KeyEvent.ACTION_DOWN) {
+ PlaybackState state = mSessionImpl.mPlaybackState;
+ long validActions = state == null ? 0 : state.getActions();
+ switch (ke.getKeyCode()) {
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ if (ke.getRepeatCount() > 0) {
+ // Consider long-press as a single tap.
+ handleMediaPlayPauseKeySingleTapIfPending();
+ } else if (mMediaPlayPauseKeyPending) {
+ // Consider double tap as the next.
+ mHandler.removeMessages(CallbackMessageHandler
+ .MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT);
+ mMediaPlayPauseKeyPending = false;
+ if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
+ onSkipToNext();
+ }
+ } else {
+ mMediaPlayPauseKeyPending = true;
+ mSessionImpl.dispatchMediaButtonDelayed(
+ mSessionImpl.getCurrentControllerInfo(),
+ mediaButtonIntent, ViewConfiguration.getDoubleTapTimeout());
+ }
+ return true;
+ default:
+ // If another key is pressed within double tap timeout, consider the
+ // pending play/pause as a single tap to handle media keys in order.
+ handleMediaPlayPauseKeySingleTapIfPending();
+ break;
+ }
+
+ switch (ke.getKeyCode()) {
+ case KeyEvent.KEYCODE_MEDIA_PLAY:
+ if ((validActions & PlaybackState.ACTION_PLAY) != 0) {
+ onPlay();
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_PAUSE:
+ if ((validActions & PlaybackState.ACTION_PAUSE) != 0) {
+ onPause();
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ if ((validActions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0) {
+ onSkipToNext();
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ if ((validActions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0) {
+ onSkipToPrevious();
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ if ((validActions & PlaybackState.ACTION_STOP) != 0) {
+ onStop();
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ if ((validActions & PlaybackState.ACTION_FAST_FORWARD) != 0) {
+ onFastForward();
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ if ((validActions & PlaybackState.ACTION_REWIND) != 0) {
+ onRewind();
+ return true;
+ }
+ break;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public static final class CallbackStub extends SessionCallbackLink.CallbackStub {
+ private WeakReference<MediaSessionEngine> mSessionImpl;
+
+ private static RemoteUserInfo createRemoteUserInfo(String packageName, int pid, int uid) {
+ return new RemoteUserInfo(packageName, pid, uid);
+ }
+
+ public CallbackStub() {
+ }
+
+ @Override
+ public void onCommand(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchCommand(createRemoteUserInfo(packageName, pid, uid),
+ command, args, cb);
+ }
+ }
+
+ @Override
+ public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent,
+ int sequenceNumber, ResultReceiver cb) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ try {
+ if (sessionImpl != null) {
+ sessionImpl.dispatchMediaButton(
+ createRemoteUserInfo(packageName, pid, uid), mediaButtonIntent);
+ }
+ } finally {
+ if (cb != null) {
+ cb.send(sequenceNumber, null);
+ }
+ }
+ }
+
+ @Override
+ public void onMediaButtonFromController(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, Intent mediaButtonIntent) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchMediaButton(createRemoteUserInfo(packageName, pid, uid),
+ mediaButtonIntent);
+ }
+ }
+
+ @Override
+ public void onPrepare(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchPrepare(createRemoteUserInfo(packageName, pid, uid));
+ }
+ }
+
+ @Override
+ public void onPrepareFromMediaId(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String mediaId,
+ Bundle extras) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchPrepareFromMediaId(
+ createRemoteUserInfo(packageName, pid, uid), mediaId, extras);
+ }
+ }
+
+ @Override
+ public void onPrepareFromSearch(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String query,
+ Bundle extras) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchPrepareFromSearch(
+ createRemoteUserInfo(packageName, pid, uid), query, extras);
+ }
+ }
+
+ @Override
+ public void onPrepareFromUri(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, Uri uri, Bundle extras) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchPrepareFromUri(
+ createRemoteUserInfo(packageName, pid, uid), uri, extras);
+ }
+ }
+
+ @Override
+ public void onPlay(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchPlay(createRemoteUserInfo(packageName, pid, uid));
+ }
+ }
+
+ @Override
+ public void onPlayFromMediaId(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String mediaId,
+ Bundle extras) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchPlayFromMediaId(
+ createRemoteUserInfo(packageName, pid, uid), mediaId, extras);
+ }
+ }
+
+ @Override
+ public void onPlayFromSearch(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String query,
+ Bundle extras) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchPlayFromSearch(
+ createRemoteUserInfo(packageName, pid, uid), query, extras);
+ }
+ }
+
+ @Override
+ public void onPlayFromUri(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, Uri uri, Bundle extras) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchPlayFromUri(
+ createRemoteUserInfo(packageName, pid, uid), uri, extras);
+ }
+ }
+
+ @Override
+ public void onSkipToTrack(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, long id) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchSkipToItem(
+ createRemoteUserInfo(packageName, pid, uid), id);
+ }
+ }
+
+ @Override
+ public void onPause(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchPause(createRemoteUserInfo(packageName, pid, uid));
+ }
+ }
+
+ @Override
+ public void onStop(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchStop(createRemoteUserInfo(packageName, pid, uid));
+ }
+ }
+
+ @Override
+ public void onNext(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchNext(createRemoteUserInfo(packageName, pid, uid));
+ }
+ }
+
+ @Override
+ public void onPrevious(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchPrevious(createRemoteUserInfo(packageName, pid, uid));
+ }
+ }
+
+ @Override
+ public void onFastForward(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchFastForward(
+ createRemoteUserInfo(packageName, pid, uid));
+ }
+ }
+
+ @Override
+ public void onRewind(String packageName, int pid, int uid,
+ ControllerCallbackLink caller) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchRewind(createRemoteUserInfo(packageName, pid, uid));
+ }
+ }
+
+ @Override
+ public void onSeekTo(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, long pos) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchSeekTo(
+ createRemoteUserInfo(packageName, pid, uid), pos);
+ }
+ }
+
+ @Override
+ public void onRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
+ Rating rating) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchRate(
+ createRemoteUserInfo(packageName, pid, uid), rating);
+ }
+ }
+
+ @Override
+ public void onCustomAction(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, String action, Bundle args) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchCustomAction(
+ createRemoteUserInfo(packageName, pid, uid), action, args);
+ }
+ }
+
+ @Override
+ public void onAdjustVolume(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, int direction) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchAdjustVolume(
+ createRemoteUserInfo(packageName, pid, uid), direction);
+ }
+ }
+
+ @Override
+ public void onSetVolumeTo(String packageName, int pid, int uid,
+ ControllerCallbackLink caller, int value) {
+ MediaSessionEngine sessionImpl = mSessionImpl.get();
+ if (sessionImpl != null) {
+ sessionImpl.dispatchSetVolumeTo(
+ createRemoteUserInfo(packageName, pid, uid), value);
+ }
+ }
+
+ void setSessionImpl(MediaSessionEngine sessionImpl) {
+ mSessionImpl = new WeakReference<>(sessionImpl);
+ }
+ }
+
+ /**
+ * A single item that is part of the play queue. It contains a description
+ * of the item and its id in the queue.
+ */
+ public static final class QueueItem {
+ /**
+ * This id is reserved. No items can be explicitly assigned this id.
+ */
+ public static final int UNKNOWN_ID = -1;
+
+ private final MediaDescription mDescription;
+ private final long mId;
+
+ /**
+ * Create a new {@link MediaSession.QueueItem}.
+ *
+ * @param description The {@link MediaDescription} for this item.
+ * @param id An identifier for this item. It must be unique within the
+ * play queue and cannot be {@link #UNKNOWN_ID}.
+ */
+ public QueueItem(MediaDescription description, long id) {
+ if (description == null) {
+ throw new IllegalArgumentException("Description cannot be null.");
+ }
+ if (id == UNKNOWN_ID) {
+ throw new IllegalArgumentException("Id cannot be QueueItem.UNKNOWN_ID");
+ }
+ mDescription = description;
+ mId = id;
+ }
+
+ public QueueItem(Parcel in) {
+ mDescription = MediaDescription.CREATOR.createFromParcel(in);
+ mId = in.readLong();
+ }
+
+ /**
+ * Get the description for this item.
+ */
+ public MediaDescription getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * Get the queue id for this item.
+ */
+ public long getQueueId() {
+ return mId;
+ }
+
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ mDescription.writeToParcel(dest, flags);
+ dest.writeLong(mId);
+ }
+
+ @Override
+ public String toString() {
+ return "MediaSession.QueueItem {" + "Description=" + mDescription + ", Id=" + mId
+ + " }";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+
+ if (!(o instanceof QueueItem)) {
+ return false;
+ }
+
+ final QueueItem item = (QueueItem) o;
+ if (mId != item.mId) {
+ return false;
+ }
+
+ if (!Objects.equals(mDescription, item.mDescription)) {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ private static final class Command {
+ public final String command;
+ public final Bundle extras;
+ public final ResultReceiver stub;
+
+ Command(String command, Bundle extras, ResultReceiver stub) {
+ this.command = command;
+ this.extras = extras;
+ this.stub = stub;
+ }
+ }
+
+ private class CallbackMessageHandler extends Handler {
+ private static final int MSG_COMMAND = 1;
+ private static final int MSG_MEDIA_BUTTON = 2;
+ private static final int MSG_PREPARE = 3;
+ private static final int MSG_PREPARE_MEDIA_ID = 4;
+ private static final int MSG_PREPARE_SEARCH = 5;
+ private static final int MSG_PREPARE_URI = 6;
+ private static final int MSG_PLAY = 7;
+ private static final int MSG_PLAY_MEDIA_ID = 8;
+ private static final int MSG_PLAY_SEARCH = 9;
+ private static final int MSG_PLAY_URI = 10;
+ private static final int MSG_SKIP_TO_ITEM = 11;
+ private static final int MSG_PAUSE = 12;
+ private static final int MSG_STOP = 13;
+ private static final int MSG_NEXT = 14;
+ private static final int MSG_PREVIOUS = 15;
+ private static final int MSG_FAST_FORWARD = 16;
+ private static final int MSG_REWIND = 17;
+ private static final int MSG_SEEK_TO = 18;
+ private static final int MSG_RATE = 19;
+ private static final int MSG_CUSTOM_ACTION = 20;
+ private static final int MSG_ADJUST_VOLUME = 21;
+ private static final int MSG_SET_VOLUME = 22;
+ private static final int MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT = 23;
+
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ CallbackWrapper mCallbackWrapper;
+ @SuppressWarnings("WeakerAccess") /* synthetic access */
+ RemoteUserInfo mCurrentControllerInfo;
+
+ CallbackMessageHandler(Looper looper, CallbackWrapper callbackWrapper) {
+ super(looper);
+ mCallbackWrapper = callbackWrapper;
+ mCallbackWrapper.mHandler = this;
+ }
+
+ void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) {
+ Pair<RemoteUserInfo, Object> objWithCaller = Pair.create(caller, obj);
+ Message msg = obtainMessage(what, objWithCaller);
+ msg.setAsynchronous(true);
+ msg.setData(data);
+ if (delayMs > 0) {
+ sendMessageDelayed(msg, delayMs);
+ } else {
+ sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ mCurrentControllerInfo = ((Pair<RemoteUserInfo, Object>) msg.obj).first;
+
+ VolumeProvider vp;
+ Object obj = ((Pair<RemoteUserInfo, Object>) msg.obj).second;
+
+ switch (msg.what) {
+ case MSG_COMMAND:
+ Command cmd = (Command) obj;
+ mCallbackWrapper.onCommand(cmd.command, cmd.extras, cmd.stub);
+ break;
+ case MSG_MEDIA_BUTTON:
+ mCallbackWrapper.onMediaButtonEvent((Intent) obj);
+ break;
+ case MSG_PREPARE:
+ mCallbackWrapper.onPrepare();
+ break;
+ case MSG_PREPARE_MEDIA_ID:
+ mCallbackWrapper.onPrepareFromMediaId((String) obj, msg.getData());
+ break;
+ case MSG_PREPARE_SEARCH:
+ mCallbackWrapper.onPrepareFromSearch((String) obj, msg.getData());
+ break;
+ case MSG_PREPARE_URI:
+ mCallbackWrapper.onPrepareFromUri((Uri) obj, msg.getData());
+ break;
+ case MSG_PLAY:
+ mCallbackWrapper.onPlay();
+ break;
+ case MSG_PLAY_MEDIA_ID:
+ mCallbackWrapper.onPlayFromMediaId((String) obj, msg.getData());
+ break;
+ case MSG_PLAY_SEARCH:
+ mCallbackWrapper.onPlayFromSearch((String) obj, msg.getData());
+ break;
+ case MSG_PLAY_URI:
+ mCallbackWrapper.onPlayFromUri((Uri) obj, msg.getData());
+ break;
+ case MSG_SKIP_TO_ITEM:
+ mCallbackWrapper.onSkipToQueueItem((Long) obj);
+ break;
+ case MSG_PAUSE:
+ mCallbackWrapper.onPause();
+ break;
+ case MSG_STOP:
+ mCallbackWrapper.onStop();
+ break;
+ case MSG_NEXT:
+ mCallbackWrapper.onSkipToNext();
+ break;
+ case MSG_PREVIOUS:
+ mCallbackWrapper.onSkipToPrevious();
+ break;
+ case MSG_FAST_FORWARD:
+ mCallbackWrapper.onFastForward();
+ break;
+ case MSG_REWIND:
+ mCallbackWrapper.onRewind();
+ break;
+ case MSG_SEEK_TO:
+ mCallbackWrapper.onSeekTo((Long) obj);
+ break;
+ case MSG_RATE:
+ mCallbackWrapper.onSetRating((Rating) obj);
+ break;
+ case MSG_CUSTOM_ACTION:
+ mCallbackWrapper.onCustomAction((String) obj, msg.getData());
+ break;
+ case MSG_ADJUST_VOLUME:
+ synchronized (mLock) {
+ vp = mVolumeProvider;
+ }
+ if (vp != null) {
+ vp.onAdjustVolume((int) obj);
+ }
+ break;
+ case MSG_SET_VOLUME:
+ synchronized (mLock) {
+ vp = mVolumeProvider;
+ }
+ if (vp != null) {
+ vp.onSetVolumeTo((int) obj);
+ }
+ break;
+ case MSG_PLAY_PAUSE_KEY_DOUBLE_TAP_TIMEOUT:
+ mCallbackWrapper.handleMediaPlayPauseKeySingleTapIfPending();
+ break;
+ }
+ mCurrentControllerInfo = null;
+ }
+ }
+}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 77e758fcf2dd..c64c452be3ef 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -28,7 +28,6 @@ import android.media.AudioManager;
import android.media.IRemoteVolumeController;
import android.media.MediaSession2;
import android.media.Session2Token;
-import android.media.browse.MediaBrowser;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -37,6 +36,7 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.media.MediaBrowserService;
import android.service.notification.NotificationListenerService;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.view.KeyEvent;
@@ -122,7 +122,6 @@ public final class MediaSessionManager {
* {@link MediaSession2.Builder} instead.
*
* @param token newly created session2 token
- * @hide
*/
public void notifySession2Created(@NonNull Session2Token token) {
if (token == null) {
@@ -196,9 +195,7 @@ public final class MediaSessionManager {
* reject your uses of {@link MediaSession2}.
*
* @return A list of {@link Session2Token}.
- * @hide
*/
- // TODO: unhide
@NonNull
public List<Session2Token> getSession2Tokens() {
return getSession2Tokens(UserHandle.myUserId());
@@ -209,13 +206,12 @@ public final class MediaSessionManager {
* given user.
* <p>
* If you want to get tokens for another user, you must hold the
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission.
+ * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL permission.
*
* @param userId The user id to fetch sessions for.
* @return A list of {@link Session2Token}
* @hide
*/
- // TODO: unhide
@NonNull
public List<Session2Token> getSession2Tokens(int userId) {
try {
@@ -342,13 +338,25 @@ public final class MediaSessionManager {
* for consistent behavior across all devices.
*
* @param listener The listener to add
- * @param handler The handler to call listener on. If {@code null}, calling thread's looper will
- * be used.
- * @hide
*/
- // TODO(jaewan): Unhide
public void addOnSession2TokensChangedListener(
- @NonNull OnSession2TokensChangedListener listener, @Nullable Handler handler) {
+ @NonNull OnSession2TokensChangedListener listener) {
+ addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, new Handler());
+ }
+
+ /**
+ * Adds a listener to be notified when the {@link #getSession2Tokens()} changes.
+ * <p>
+ * This API is not generally intended for third party application developers.
+ * Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
+ * <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
+ * for consistent behavior across all devices.
+ *
+ * @param listener The listener to add
+ * @param handler The handler to call listener on.
+ */
+ public void addOnSession2TokensChangedListener(
+ @NonNull OnSession2TokensChangedListener listener, @NonNull Handler handler) {
addOnSession2TokensChangedListener(UserHandle.myUserId(), listener, handler);
}
@@ -392,9 +400,7 @@ public final class MediaSessionManager {
* Removes the {@link OnSession2TokensChangedListener} to stop receiving session token updates.
*
* @param listener The listener to remove.
- * @hide
*/
- // TODO(jaewan): Unhide
public void removeOnSession2TokensChangedListener(
@NonNull OnSession2TokensChangedListener listener) {
if (listener == null) {
@@ -688,8 +694,6 @@ public final class MediaSessionManager {
* Use the <a href="{@docRoot}jetpack/androidx.html">AndroidX</a>
* <a href="{@docRoot}reference/androidx/media2/package-summary.html">Media2 Library</a>
* for consistent behavior across all devices.
- *
- * @hide
*/
public interface OnSession2TokensChangedListener {
/**
@@ -793,7 +797,6 @@ public final class MediaSessionManager {
private final String mPackageName;
private final int mPid;
private final int mUid;
- private final IBinder mCallerBinder;
/**
* Create a new remote user information.
@@ -803,22 +806,9 @@ public final class MediaSessionManager {
* @param uid The uid of the remote user
*/
public RemoteUserInfo(@NonNull String packageName, int pid, int uid) {
- this(packageName, pid, uid, null);
- }
-
- /**
- * Create a new remote user information.
- *
- * @param packageName The package name of the remote user
- * @param pid The pid of the remote user
- * @param uid The uid of the remote user
- * @param callerBinder The binder of the remote user. Can be {@code null}.
- */
- public RemoteUserInfo(String packageName, int pid, int uid, IBinder callerBinder) {
mPackageName = packageName;
mPid = pid;
mUid = uid;
- mCallerBinder = callerBinder;
}
/**
@@ -843,13 +833,8 @@ public final class MediaSessionManager {
}
/**
- * Returns equality of two RemoteUserInfo. Two RemoteUserInfos are the same only if they're
- * sent to the same controller (either {@link MediaController} or
- * {@link MediaBrowser}. If it's not nor one of them is triggered by the key presses, they
- * would be considered as different one.
- * <p>
- * If you only want to compare the caller's package, compare them with the
- * {@link #getPackageName()}, {@link #getPid()}, and/or {@link #getUid()} directly.
+ * Returns equality of two RemoteUserInfo. Two RemoteUserInfo objects are equal
+ * if and only if they have the same package name, same pid, and same uid.
*
* @param obj the reference object with which to compare.
* @return {@code true} if equals, {@code false} otherwise
@@ -863,8 +848,9 @@ public final class MediaSessionManager {
return true;
}
RemoteUserInfo otherUserInfo = (RemoteUserInfo) obj;
- return (mCallerBinder == null || otherUserInfo.mCallerBinder == null) ? false
- : mCallerBinder.equals(otherUserInfo.mCallerBinder);
+ return TextUtils.equals(mPackageName, otherUserInfo.mPackageName)
+ && mPid == otherUserInfo.mPid
+ && mUid == otherUserInfo.mUid;
}
@Override
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 2c57d1f676ed..0d0ec4c78394 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -19,7 +19,6 @@ import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.LongDef;
import android.annotation.Nullable;
-import android.media.RemoteControlClient;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -481,161 +480,6 @@ public final class PlaybackState implements Parcelable {
return mExtras;
}
- /**
- * Get the {@link PlaybackState} state for the given
- * {@link RemoteControlClient} state.
- *
- * @param rccState The state used by {@link RemoteControlClient}.
- * @return The equivalent state used by {@link PlaybackState}.
- * @hide
- */
- public static int getStateFromRccState(int rccState) {
- switch (rccState) {
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- return STATE_BUFFERING;
- case RemoteControlClient.PLAYSTATE_ERROR:
- return STATE_ERROR;
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- return STATE_FAST_FORWARDING;
- case RemoteControlClient.PLAYSTATE_NONE:
- return STATE_NONE;
- case RemoteControlClient.PLAYSTATE_PAUSED:
- return STATE_PAUSED;
- case RemoteControlClient.PLAYSTATE_PLAYING:
- return STATE_PLAYING;
- case RemoteControlClient.PLAYSTATE_REWINDING:
- return STATE_REWINDING;
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- return STATE_SKIPPING_TO_PREVIOUS;
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- return STATE_SKIPPING_TO_NEXT;
- case RemoteControlClient.PLAYSTATE_STOPPED:
- return STATE_STOPPED;
- default:
- return -1;
- }
- }
-
- /**
- * Get the {@link RemoteControlClient} state for the given
- * {@link PlaybackState} state.
- *
- * @param state The state used by {@link PlaybackState}.
- * @return The equivalent state used by {@link RemoteControlClient}.
- * @hide
- */
- public static int getRccStateFromState(int state) {
- switch (state) {
- case STATE_BUFFERING:
- return RemoteControlClient.PLAYSTATE_BUFFERING;
- case STATE_ERROR:
- return RemoteControlClient.PLAYSTATE_ERROR;
- case STATE_FAST_FORWARDING:
- return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
- case STATE_NONE:
- return RemoteControlClient.PLAYSTATE_NONE;
- case STATE_PAUSED:
- return RemoteControlClient.PLAYSTATE_PAUSED;
- case STATE_PLAYING:
- return RemoteControlClient.PLAYSTATE_PLAYING;
- case STATE_REWINDING:
- return RemoteControlClient.PLAYSTATE_REWINDING;
- case STATE_SKIPPING_TO_PREVIOUS:
- return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
- case STATE_SKIPPING_TO_NEXT:
- return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
- case STATE_STOPPED:
- return RemoteControlClient.PLAYSTATE_STOPPED;
- default:
- return -1;
- }
- }
-
- /**
- * @hide
- */
- public static long getActionsFromRccControlFlags(int rccFlags) {
- long actions = 0;
- long flag = 1;
- while (flag <= rccFlags) {
- if ((flag & rccFlags) != 0) {
- actions |= getActionForRccFlag((int) flag);
- }
- flag = flag << 1;
- }
- return actions;
- }
-
- /**
- * @hide
- */
- public static int getRccControlFlagsFromActions(long actions) {
- int rccFlags = 0;
- long action = 1;
- while (action <= actions && action < Integer.MAX_VALUE) {
- if ((action & actions) != 0) {
- rccFlags |= getRccFlagForAction(action);
- }
- action = action << 1;
- }
- return rccFlags;
- }
-
- private static long getActionForRccFlag(int flag) {
- switch (flag) {
- case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS:
- return ACTION_SKIP_TO_PREVIOUS;
- case RemoteControlClient.FLAG_KEY_MEDIA_REWIND:
- return ACTION_REWIND;
- case RemoteControlClient.FLAG_KEY_MEDIA_PLAY:
- return ACTION_PLAY;
- case RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE:
- return ACTION_PLAY_PAUSE;
- case RemoteControlClient.FLAG_KEY_MEDIA_PAUSE:
- return ACTION_PAUSE;
- case RemoteControlClient.FLAG_KEY_MEDIA_STOP:
- return ACTION_STOP;
- case RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD:
- return ACTION_FAST_FORWARD;
- case RemoteControlClient.FLAG_KEY_MEDIA_NEXT:
- return ACTION_SKIP_TO_NEXT;
- case RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE:
- return ACTION_SEEK_TO;
- case RemoteControlClient.FLAG_KEY_MEDIA_RATING:
- return ACTION_SET_RATING;
- }
- return 0;
- }
-
- private static int getRccFlagForAction(long action) {
- // We only care about the lower set of actions that can map to rcc
- // flags.
- int testAction = action < Integer.MAX_VALUE ? (int) action : 0;
- switch (testAction) {
- case (int) ACTION_SKIP_TO_PREVIOUS:
- return RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
- case (int) ACTION_REWIND:
- return RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
- case (int) ACTION_PLAY:
- return RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
- case (int) ACTION_PLAY_PAUSE:
- return RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
- case (int) ACTION_PAUSE:
- return RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
- case (int) ACTION_STOP:
- return RemoteControlClient.FLAG_KEY_MEDIA_STOP;
- case (int) ACTION_FAST_FORWARD:
- return RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
- case (int) ACTION_SKIP_TO_NEXT:
- return RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
- case (int) ACTION_SEEK_TO:
- return RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
- case (int) ACTION_SET_RATING:
- return RemoteControlClient.FLAG_KEY_MEDIA_RATING;
- }
- return 0;
- }
-
public static final Parcelable.Creator<PlaybackState> CREATOR =
new Parcelable.Creator<PlaybackState>() {
@Override
diff --git a/media/java/android/media/session/SessionCallbackLink.java b/media/java/android/media/session/SessionCallbackLink.java
index 0265687bcd54..0dbf427b048d 100644
--- a/media/java/android/media/session/SessionCallbackLink.java
+++ b/media/java/android/media/session/SessionCallbackLink.java
@@ -669,7 +669,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyCommand(String packageName, int pid, int uid,
ControllerCallbackLink caller, String command, Bundle args, ResultReceiver cb) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onCommand(packageName, pid, uid, caller, command, args, cb);
@@ -681,7 +681,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyMediaButton(String packageName, int pid, int uid,
Intent mediaButtonIntent, int sequenceNumber, ResultReceiver cb) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onMediaButton(packageName, pid, uid, mediaButtonIntent,
@@ -694,7 +694,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyMediaButtonFromController(String packageName, int pid, int uid,
ControllerCallbackLink caller, Intent mediaButtonIntent) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onMediaButtonFromController(packageName, pid, uid, caller,
@@ -707,7 +707,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyPrepare(String packageName, int pid, int uid,
ControllerCallbackLink caller) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPrepare(packageName, pid, uid, caller);
@@ -719,7 +719,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyPrepareFromMediaId(String packageName, int pid, int uid,
ControllerCallbackLink caller, String mediaId, Bundle extras) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPrepareFromMediaId(packageName, pid, uid, caller, mediaId, extras);
@@ -731,7 +731,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyPrepareFromSearch(String packageName, int pid, int uid,
ControllerCallbackLink caller, String query, Bundle extras) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPrepareFromSearch(packageName, pid, uid, caller, query, extras);
@@ -743,7 +743,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyPrepareFromUri(String packageName, int pid, int uid,
ControllerCallbackLink caller, Uri uri, Bundle extras) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPrepareFromUri(packageName, pid, uid, caller, uri, extras);
@@ -755,7 +755,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyPlay(String packageName, int pid, int uid,
ControllerCallbackLink caller) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPlay(packageName, pid, uid, caller);
@@ -767,7 +767,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyPlayFromMediaId(String packageName, int pid, int uid,
ControllerCallbackLink caller, String mediaId, Bundle extras) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPlayFromMediaId(packageName, pid, uid, caller, mediaId, extras);
@@ -779,7 +779,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyPlayFromSearch(String packageName, int pid, int uid,
ControllerCallbackLink caller, String query, Bundle extras) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPlayFromSearch(packageName, pid, uid, caller, query, extras);
@@ -791,7 +791,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyPlayFromUri(String packageName, int pid, int uid,
ControllerCallbackLink caller, Uri uri, Bundle extras) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPlayFromUri(packageName, pid, uid, caller, uri, extras);
@@ -803,7 +803,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifySkipToTrack(String packageName, int pid, int uid,
ControllerCallbackLink caller, long id) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onSkipToTrack(packageName, pid, uid, caller, id);
@@ -815,7 +815,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyPause(String packageName, int pid, int uid,
ControllerCallbackLink caller) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPause(packageName, pid, uid, caller);
@@ -827,7 +827,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyStop(String packageName, int pid, int uid,
ControllerCallbackLink caller) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onStop(packageName, pid, uid, caller);
@@ -839,7 +839,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyNext(String packageName, int pid, int uid,
ControllerCallbackLink caller) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onNext(packageName, pid, uid, caller);
@@ -851,7 +851,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyPrevious(String packageName, int pid, int uid,
ControllerCallbackLink caller) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onPrevious(packageName, pid, uid, caller);
@@ -863,7 +863,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyFastForward(String packageName, int pid, int uid,
ControllerCallbackLink caller) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onFastForward(packageName, pid, uid, caller);
@@ -875,7 +875,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyRewind(String packageName, int pid, int uid,
ControllerCallbackLink caller) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onRewind(packageName, pid, uid, caller);
@@ -887,7 +887,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifySeekTo(String packageName, int pid, int uid,
ControllerCallbackLink caller, long pos) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onSeekTo(packageName, pid, uid, caller, pos);
@@ -899,7 +899,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyRate(String packageName, int pid, int uid, ControllerCallbackLink caller,
Rating rating) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onRate(packageName, pid, uid, caller, rating);
@@ -910,7 +910,7 @@ public final class SessionCallbackLink implements Parcelable {
public void notifyCustomAction(String packageName, int pid, int uid,
ControllerCallbackLink caller, String action, Bundle args) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onCustomAction(packageName, pid, uid, caller, action, args);
@@ -922,7 +922,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifyAdjustVolume(String packageName, int pid, int uid,
ControllerCallbackLink caller, int direction) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onAdjustVolume(packageName, pid, uid, caller, direction);
@@ -934,7 +934,7 @@ public final class SessionCallbackLink implements Parcelable {
@Override
public void notifySetVolumeTo(String packageName, int pid, int uid,
ControllerCallbackLink caller, int value) {
- ensureMediasControlPermission();
+ ensureMediaControlPermission();
final long token = Binder.clearCallingIdentity();
try {
mCallbackStub.onSetVolumeTo(packageName, pid, uid, caller, value);
@@ -943,7 +943,7 @@ public final class SessionCallbackLink implements Parcelable {
}
}
- private void ensureMediasControlPermission() {
+ private void ensureMediaControlPermission() {
// Allow API calls from the System UI
if (mContext.checkCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
== PackageManager.PERMISSION_GRANTED) {
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 5cb8fb8bee3b..30907a5cf205 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -33,7 +33,10 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiUtils;
+import android.hardware.hdmi.HdmiUtils.HdmiAddressRelativePosition;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -49,7 +52,6 @@ import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Retention;
@@ -145,6 +147,8 @@ public final class TvInputInfo implements Parcelable {
// Attributes specific to HDMI
private final HdmiDeviceInfo mHdmiDeviceInfo;
private final boolean mIsConnectedToHdmiSwitch;
+ @HdmiAddressRelativePosition
+ private final int mHdmiConnectionRelativePosition;
private final String mParentId;
private final Bundle mExtras;
@@ -260,7 +264,9 @@ public final class TvInputInfo implements Parcelable {
private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
String setupActivity, boolean canRecord, int tunerCount, HdmiDeviceInfo hdmiDeviceInfo,
- boolean isConnectedToHdmiSwitch, String parentId, Bundle extras) {
+ boolean isConnectedToHdmiSwitch,
+ @HdmiAddressRelativePosition int hdmiConnectionRelativePosition, String parentId,
+ Bundle extras) {
mService = service;
mId = id;
mType = type;
@@ -275,6 +281,7 @@ public final class TvInputInfo implements Parcelable {
mTunerCount = tunerCount;
mHdmiDeviceInfo = hdmiDeviceInfo;
mIsConnectedToHdmiSwitch = isConnectedToHdmiSwitch;
+ mHdmiConnectionRelativePosition = hdmiConnectionRelativePosition;
mParentId = parentId;
mExtras = extras;
}
@@ -419,6 +426,7 @@ public final class TvInputInfo implements Parcelable {
/**
* Returns {@code true}, if a CEC device for this TV input is connected to an HDMI switch, i.e.,
* the device isn't directly connected to a HDMI port.
+ * TODO(b/110094868): add @Deprecated for Q
* @hide
*/
@SystemApi
@@ -427,6 +435,16 @@ public final class TvInputInfo implements Parcelable {
}
/**
+ * Returns the relative position of this HDMI input.
+ * TODO(b/110094868): unhide for Q
+ * @hide
+ */
+ @HdmiAddressRelativePosition
+ public int getHdmiConnectionRelativePosition() {
+ return mHdmiConnectionRelativePosition;
+ }
+
+ /**
* Checks if this TV input is marked hidden by the user in the settings.
*
* @param context Supplies a {@link Context} used to check if this TV input is hidden.
@@ -555,6 +573,7 @@ public final class TvInputInfo implements Parcelable {
&& mTunerCount == obj.mTunerCount
&& Objects.equals(mHdmiDeviceInfo, obj.mHdmiDeviceInfo)
&& mIsConnectedToHdmiSwitch == obj.mIsConnectedToHdmiSwitch
+ && mHdmiConnectionRelativePosition == obj.mHdmiConnectionRelativePosition
&& TextUtils.equals(mParentId, obj.mParentId)
&& Objects.equals(mExtras, obj.mExtras);
}
@@ -589,6 +608,7 @@ public final class TvInputInfo implements Parcelable {
dest.writeInt(mTunerCount);
dest.writeParcelable(mHdmiDeviceInfo, flags);
dest.writeByte(mIsConnectedToHdmiSwitch ? (byte) 1 : 0);
+ dest.writeInt(mHdmiConnectionRelativePosition);
dest.writeString(mParentId);
dest.writeBundle(mExtras);
}
@@ -630,6 +650,7 @@ public final class TvInputInfo implements Parcelable {
mTunerCount = in.readInt();
mHdmiDeviceInfo = in.readParcelable(null);
mIsConnectedToHdmiSwitch = in.readByte() == 1;
+ mHdmiConnectionRelativePosition = in.readInt();
mParentId = in.readString();
mExtras = in.readBundle();
}
@@ -883,12 +904,17 @@ public final class TvInputInfo implements Parcelable {
int type;
boolean isHardwareInput = false;
boolean isConnectedToHdmiSwitch = false;
+ @HdmiAddressRelativePosition
+ int hdmiConnectionRelativePosition = HdmiUtils.HDMI_RELATIVE_POSITION_UNKNOWN;
if (mHdmiDeviceInfo != null) {
id = generateInputId(componentName, mHdmiDeviceInfo);
type = TYPE_HDMI;
isHardwareInput = true;
- isConnectedToHdmiSwitch = (mHdmiDeviceInfo.getPhysicalAddress() & 0x0FFF) != 0;
+ hdmiConnectionRelativePosition = getRelativePosition(mContext, mHdmiDeviceInfo);
+ isConnectedToHdmiSwitch =
+ hdmiConnectionRelativePosition
+ != HdmiUtils.HDMI_RELATIVE_POSITION_DIRECTLY_BELOW;
} else if (mTvInputHardwareInfo != null) {
id = generateInputId(componentName, mTvInputHardwareInfo);
type = sHardwareTypeToTvInputType.get(mTvInputHardwareInfo.getType(), TYPE_TUNER);
@@ -901,7 +927,8 @@ public final class TvInputInfo implements Parcelable {
return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId,
mIcon, mIconStandby, mIconDisconnected, mSetupActivity,
mCanRecord == null ? false : mCanRecord, mTunerCount == null ? 0 : mTunerCount,
- mHdmiDeviceInfo, isConnectedToHdmiSwitch, mParentId, mExtras);
+ mHdmiDeviceInfo, isConnectedToHdmiSwitch, hdmiConnectionRelativePosition,
+ mParentId, mExtras);
}
private static String generateInputId(ComponentName name) {
@@ -923,6 +950,16 @@ public final class TvInputInfo implements Parcelable {
+ tvInputHardwareInfo.getDeviceId();
}
+ private static int getRelativePosition(Context context, HdmiDeviceInfo info) {
+ HdmiControlManager hcm =
+ (HdmiControlManager) context.getSystemService(Context.HDMI_CONTROL_SERVICE);
+ if (hcm == null) {
+ return HdmiUtils.HDMI_RELATIVE_POSITION_UNKNOWN;
+ }
+ return HdmiUtils.getHdmiAddressRelativePosition(
+ info.getPhysicalAddress(), hcm.getPhysicalAddress());
+ }
+
private void parseServiceMetadata(int inputType) {
ServiceInfo si = mResolveInfo.serviceInfo;
PackageManager pm = mContext.getPackageManager();
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index d19d1177cae7..2fbc6998576a 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -541,8 +541,7 @@ public abstract class MediaBrowserService extends Service {
throw new IllegalStateException("This should be called inside of onGetRoot or"
+ " onLoadChildren or onLoadItem methods");
}
- return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid,
- mCurConnection.callbacks.asBinder());
+ return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid);
}
/**
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 73d4c4522010..7c1af4a81f9d 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -56,6 +56,7 @@ cc_library_shared {
shared_libs: [
"liblog",
+ "libhidlbase",
"libcutils",
"libandroidfw",
"libinput",
@@ -70,6 +71,8 @@ cc_library_shared {
"libnetd_client",
"libhwui",
"libxml2",
+ "android.hardware.configstore@1.0",
+ "android.hardware.configstore-utils",
],
static_libs: [
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 8be8eda06a59..8b45af0c3450 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -172,6 +172,7 @@ LIBANDROID {
ASensorEventQueue_hasEvents;
ASensorEventQueue_registerSensor; # introduced=26
ASensorEventQueue_setEventRate;
+ ASensorEventQueue_requestAdditionalInfoEvents; # introduced=29
ASensorManager_configureDirectReport; # introduced=26
ASensorManager_createEventQueue;
ASensorManager_createHardwareBufferDirectChannel; # introduced=26
@@ -185,6 +186,7 @@ LIBANDROID {
ASensorManager_getSensorList;
ASensor_getFifoMaxEventCount; # introduced=21
ASensor_getFifoReservedEventCount; # introduced=21
+ ASensor_getHandle; # introduced=29
ASensor_getHighestDirectReportRateLevel; # introduced=26
ASensor_getMinDelay;
ASensor_getName;
@@ -207,7 +209,7 @@ LIBANDROID {
AStorageManager_unmountObb;
ASurfaceControl_create; # introduced=29
ASurfaceControl_createFromWindow; # introduced=29
- ASurfaceControl_destroy; # introduced=29
+ ASurfaceControl_release; # introduced=29
ASurfaceTexture_acquireANativeWindow; # introduced=28
ASurfaceTexture_attachToGLContext; # introduced=28
ASurfaceTexture_detachFromGLContext; # introduced=28
@@ -216,13 +218,24 @@ LIBANDROID {
ASurfaceTexture_getTransformMatrix; # introduced=28
ASurfaceTexture_release; # introduced=28
ASurfaceTexture_updateTexImage; # introduced=28
+ ASurfaceTransactionStats_getAcquireTime; # introduced=29
+ ASurfaceTransactionStats_getASurfaceControls; # introduced=29
+ ASurfaceTransactionStats_getLatchTime; # introduced=29
+ ASurfaceTransactionStats_getPresentFenceFd; # introduced=29
+ ASurfaceTransactionStats_getPreviousReleaseFenceFd; # introduced=29
+ ASurfaceTransactionStats_releaseASurfaceControls; # introduced=29
ASurfaceTransaction_apply; # introduced=29
ASurfaceTransaction_create; # introduced=29
ASurfaceTransaction_delete; # introduced=29
+ ASurfaceTransaction_reparent; # introduced=29
ASurfaceTransaction_setBuffer; # introduced=29
+ ASurfaceTransaction_setBufferAlpha; # introduced=29
ASurfaceTransaction_setBufferTransparency; # introduced=29
ASurfaceTransaction_setDamageRegion; # introduced=29
+ ASurfaceTransaction_setDesiredPresentTime; # introduced=29
ASurfaceTransaction_setGeometry; # introduced=29
+ ASurfaceTransaction_setHdrMetadata_cta861_3; # introduced=29
+ ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29
ASurfaceTransaction_setOnComplete; # introduced=29
ASurfaceTransaction_setVisibility; # introduced=29
ASurfaceTransaction_setZOrder; # introduced=29
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index c3b2e2526ea8..63082fd70bc6 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -115,6 +115,7 @@ ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager,
if (queue != 0) {
ALooper_addFd(looper, queue->getFd(), ident, ALOOPER_EVENT_INPUT, callback, data);
queue->looper = looper;
+ queue->requestAdditionalInfo = false;
queue->incStrong(manager);
}
return static_cast<ASensorEventQueue*>(queue.get());
@@ -274,11 +275,19 @@ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* even
return android::BAD_VALUE;
}
- ssize_t actual = static_cast<SensorEventQueue*>(queue)->read(events, count);
+ SensorEventQueue* sensorQueue = static_cast<SensorEventQueue*>(queue);
+ ssize_t actual = sensorQueue->read(events, count);
if (actual > 0) {
- static_cast<SensorEventQueue*>(queue)->sendAck(events, actual);
+ sensorQueue->sendAck(events, actual);
}
- return actual;
+
+ return sensorQueue->filterEvents(events, actual);
+}
+
+int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable) {
+ RETURN_IF_QUEUE_IS_NULL(android::BAD_VALUE);
+ queue->requestAdditionalInfo = enable;
+ return android::OK;
}
/*****************************************************************************/
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index ead5b0b37f4b..f0100a9fd4f9 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -14,14 +14,26 @@
* limitations under the License.
*/
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/native_window.h>
#include <android/surface_control.h>
+#include <configstore/Utils.h>
+
+#include <gui/HdrMetadata.h>
+#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
+#include <ui/HdrCapabilities.h>
+
+#include <utils/Timers.h>
+
+using namespace android::hardware::configstore;
+using namespace android::hardware::configstore::V1_0;
using namespace android;
+using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs;
using Transaction = SurfaceComposerClient::Transaction;
@@ -95,10 +107,9 @@ ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* deb
return reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
}
-void ASurfaceControl_destroy(ASurfaceControl* aSurfaceControl) {
+void ASurfaceControl_release(ASurfaceControl* aSurfaceControl) {
sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
- Transaction().reparent(surfaceControl, nullptr).apply();
SurfaceControl_release(surfaceControl.get());
}
@@ -120,6 +131,86 @@ void ASurfaceTransaction_apply(ASurfaceTransaction* aSurfaceTransaction) {
transaction->apply();
}
+typedef struct ASurfaceControlStats {
+ int64_t acquireTime;
+ sp<Fence> previousReleaseFence;
+} ASurfaceControlStats;
+
+struct ASurfaceTransactionStats {
+ std::unordered_map<ASurfaceControl*, ASurfaceControlStats> aSurfaceControlStats;
+ int64_t latchTime;
+ sp<Fence> presentFence;
+};
+
+int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* aSurfaceTransactionStats) {
+ CHECK_NOT_NULL(aSurfaceTransactionStats);
+ return aSurfaceTransactionStats->latchTime;
+}
+
+int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* aSurfaceTransactionStats) {
+ CHECK_NOT_NULL(aSurfaceTransactionStats);
+ auto& presentFence = aSurfaceTransactionStats->presentFence;
+ return (presentFence) ? presentFence->dup() : -1;
+}
+
+void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* aSurfaceTransactionStats,
+ ASurfaceControl*** outASurfaceControls,
+ size_t* outASurfaceControlsSize) {
+ CHECK_NOT_NULL(aSurfaceTransactionStats);
+ CHECK_NOT_NULL(outASurfaceControls);
+ CHECK_NOT_NULL(outASurfaceControlsSize);
+
+ size_t size = aSurfaceTransactionStats->aSurfaceControlStats.size();
+
+ SurfaceControl** surfaceControls = new SurfaceControl*[size];
+ ASurfaceControl** aSurfaceControls = reinterpret_cast<ASurfaceControl**>(surfaceControls);
+
+ size_t i = 0;
+ for (auto& [aSurfaceControl, aSurfaceControlStats] : aSurfaceTransactionStats->aSurfaceControlStats) {
+ aSurfaceControls[i] = aSurfaceControl;
+ i++;
+ }
+
+ *outASurfaceControls = aSurfaceControls;
+ *outASurfaceControlsSize = size;
+}
+
+int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* aSurfaceTransactionStats,
+ ASurfaceControl* aSurfaceControl) {
+ CHECK_NOT_NULL(aSurfaceTransactionStats);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ const auto& aSurfaceControlStats =
+ aSurfaceTransactionStats->aSurfaceControlStats.find(aSurfaceControl);
+ LOG_ALWAYS_FATAL_IF(
+ aSurfaceControlStats == aSurfaceTransactionStats->aSurfaceControlStats.end(),
+ "ASurfaceControl not found");
+
+ return aSurfaceControlStats->second.acquireTime;
+}
+
+int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
+ ASurfaceTransactionStats* aSurfaceTransactionStats, ASurfaceControl* aSurfaceControl) {
+ CHECK_NOT_NULL(aSurfaceTransactionStats);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ const auto& aSurfaceControlStats =
+ aSurfaceTransactionStats->aSurfaceControlStats.find(aSurfaceControl);
+ LOG_ALWAYS_FATAL_IF(
+ aSurfaceControlStats == aSurfaceTransactionStats->aSurfaceControlStats.end(),
+ "ASurfaceControl not found");
+
+ auto& previousReleaseFence = aSurfaceControlStats->second.previousReleaseFence;
+ return (previousReleaseFence) ? previousReleaseFence->dup() : -1;
+}
+
+void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** aSurfaceControls) {
+ CHECK_NOT_NULL(aSurfaceControls);
+
+ SurfaceControl** surfaceControls = reinterpret_cast<SurfaceControl**>(aSurfaceControls);
+ delete[] surfaceControls;
+}
+
void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction, void* context,
ASurfaceTransaction_OnComplete func) {
CHECK_NOT_NULL(aSurfaceTransaction);
@@ -127,9 +218,23 @@ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction,
CHECK_NOT_NULL(func);
TransactionCompletedCallbackTakesContext callback = [func](void* callback_context,
- const TransactionStats& stats) {
- int fence = (stats.presentFence) ? stats.presentFence->dup() : -1;
- (*func)(callback_context, fence);
+ nsecs_t latchTime,
+ const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& surfaceControlStats) {
+ ASurfaceTransactionStats aSurfaceTransactionStats;
+
+ aSurfaceTransactionStats.latchTime = latchTime;
+ aSurfaceTransactionStats.presentFence = presentFence;
+
+ auto& aSurfaceControlStats = aSurfaceTransactionStats.aSurfaceControlStats;
+
+ for (const auto& [surfaceControl, acquireTime, previousReleaseFence] : surfaceControlStats) {
+ ASurfaceControl* aSurfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControl.get());
+ aSurfaceControlStats[aSurfaceControl].acquireTime = acquireTime;
+ aSurfaceControlStats[aSurfaceControl].previousReleaseFence = previousReleaseFence;
+ }
+
+ (*func)(callback_context, &aSurfaceTransactionStats);
};
Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
@@ -137,7 +242,23 @@ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction,
transaction->addTransactionCompletedCallback(callback, context);
}
-void ASurfaceTransaction_setVisibility(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+void ASurfaceTransaction_reparent(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ ASurfaceControl* newParentASurfaceControl) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ sp<SurfaceControl> newParentSurfaceControl = ASurfaceControl_to_SurfaceControl(
+ newParentASurfaceControl);
+ sp<IBinder> newParentHandle = (newParentSurfaceControl)? newParentSurfaceControl->getHandle() : nullptr;
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ transaction->reparent(surfaceControl, newParentHandle);
+}
+
+void ASurfaceTransaction_setVisibility(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
int8_t visibility) {
CHECK_NOT_NULL(aSurfaceTransaction);
CHECK_NOT_NULL(aSurfaceControl);
@@ -157,7 +278,8 @@ void ASurfaceTransaction_setVisibility(ASurfaceTransaction* aSurfaceTransaction,
}
}
-void ASurfaceTransaction_setZOrder(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+void ASurfaceTransaction_setZOrder(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
int32_t z_order) {
CHECK_NOT_NULL(aSurfaceTransaction);
CHECK_NOT_NULL(aSurfaceControl);
@@ -168,8 +290,9 @@ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* aSurfaceTransaction, ASu
transaction->setLayer(surfaceControl, z_order);
}
-void ASurfaceTransaction_setBuffer(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
- AHardwareBuffer* buffer, int fence_fd) {
+void ASurfaceTransaction_setBuffer(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ AHardwareBuffer* buffer, int acquire_fence_fd) {
CHECK_NOT_NULL(aSurfaceTransaction);
CHECK_NOT_NULL(aSurfaceControl);
@@ -179,8 +302,8 @@ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* aSurfaceTransaction, ASu
sp<GraphicBuffer> graphic_buffer(reinterpret_cast<GraphicBuffer*>(buffer));
transaction->setBuffer(surfaceControl, graphic_buffer);
- if (fence_fd != -1) {
- sp<Fence> fence = new Fence(fence_fd);
+ if (acquire_fence_fd != -1) {
+ sp<Fence> fence = new Fence(acquire_fence_fd);
transaction->setAcquireFence(surfaceControl, fence);
}
}
@@ -215,7 +338,8 @@ void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* aSurfaceTran
transaction->setFlags(surfaceControl, flags, layer_state_t::eLayerOpaque);
}
-void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
+void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
const ARect rects[], uint32_t count) {
CHECK_NOT_NULL(aSurfaceTransaction);
CHECK_NOT_NULL(aSurfaceControl);
@@ -230,3 +354,80 @@ void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* aSurfaceTransactio
transaction->setSurfaceDamageRegion(surfaceControl, region);
}
+
+void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* aSurfaceTransaction,
+ int64_t desiredPresentTime) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ transaction->setDesiredPresentTime(static_cast<nsecs_t>(desiredPresentTime));
+}
+
+void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ float alpha) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ LOG_ALWAYS_FATAL_IF(alpha < 0.0 || alpha > 1.0, "invalid alpha");
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ transaction->setAlpha(surfaceControl, alpha);
+}
+
+void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ struct AHdrMetadata_smpte2086* metadata) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ HdrMetadata hdrMetadata;
+
+ if (metadata) {
+ hdrMetadata.smpte2086.displayPrimaryRed.x = metadata->displayPrimaryRed.x;
+ hdrMetadata.smpte2086.displayPrimaryRed.y = metadata->displayPrimaryRed.y;
+ hdrMetadata.smpte2086.displayPrimaryGreen.x = metadata->displayPrimaryGreen.x;
+ hdrMetadata.smpte2086.displayPrimaryGreen.y = metadata->displayPrimaryGreen.y;
+ hdrMetadata.smpte2086.displayPrimaryBlue.x = metadata->displayPrimaryBlue.x;
+ hdrMetadata.smpte2086.displayPrimaryBlue.y = metadata->displayPrimaryBlue.y;
+ hdrMetadata.smpte2086.whitePoint.x = metadata->whitePoint.x;
+ hdrMetadata.smpte2086.whitePoint.y = metadata->whitePoint.y;
+ hdrMetadata.smpte2086.minLuminance = metadata->minLuminance;
+ hdrMetadata.smpte2086.maxLuminance = metadata->maxLuminance;
+
+ hdrMetadata.validTypes |= HdrMetadata::SMPTE2086;
+ } else {
+ hdrMetadata.validTypes &= ~HdrMetadata::SMPTE2086;
+ }
+
+ transaction->setHdrMetadata(surfaceControl, hdrMetadata);
+}
+
+void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ struct AHdrMetadata_cta861_3* metadata) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ HdrMetadata hdrMetadata;
+
+ if (metadata) {
+ hdrMetadata.cta8613.maxContentLightLevel = metadata->maxContentLightLevel;
+ hdrMetadata.cta8613.maxFrameAverageLightLevel = metadata->maxFrameAverageLightLevel;
+
+ hdrMetadata.validTypes |= HdrMetadata::CTA861_3;
+ } else {
+ hdrMetadata.validTypes &= ~HdrMetadata::CTA861_3;
+ }
+
+ transaction->setHdrMetadata(surfaceControl, hdrMetadata);
+}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 8d04702ea5f6..da3416b886ad 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -76,7 +76,7 @@ public class ExternalStorageProvider extends FileSystemProvider {
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
- Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES,
+ Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES, Root.COLUMN_QUERY_ARGS
};
private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
@@ -444,6 +444,7 @@ public class ExternalStorageProvider extends FileSystemProvider {
row.add(Root.COLUMN_FLAGS, root.flags);
row.add(Root.COLUMN_TITLE, root.title);
row.add(Root.COLUMN_DOCUMENT_ID, root.docId);
+ row.add(Root.COLUMN_QUERY_ARGS, SUPPORTED_QUERY_ARGS);
long availableBytes = -1;
if (root.reportAvailableBytes) {
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
index 14e293694ebd..7b112dfc125c 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
@@ -29,7 +29,6 @@ import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BINDTODEVICE;
import static android.system.OsConstants.SO_BROADCAST;
import static android.system.OsConstants.SO_REUSEADDR;
@@ -45,6 +44,7 @@ import android.net.MacAddress;
import android.net.NetworkUtils;
import android.net.TrafficStats;
import android.net.util.SharedLog;
+import android.net.util.SocketUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -629,14 +629,10 @@ public class DhcpServer extends IDhcpServer.Stub {
final int oldTag = TrafficStats.getAndSetThreadStatsTag(TAG_SYSTEM_DHCP_SERVER);
try {
mSocket = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ SocketUtils.bindSocketToInterface(mSocket, mIfName);
Os.setsockoptInt(mSocket, SOL_SOCKET, SO_REUSEADDR, 1);
- // SO_BINDTODEVICE actually takes a string. This works because the first member
- // of struct ifreq is a NULL-terminated interface name.
- // TODO: add a setsockoptString()
- Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1);
Os.bind(mSocket, Inet4Address.ANY, DHCP_SERVER);
- NetworkUtils.protectFromVpn(mSocket);
return mSocket;
} catch (IOException | ErrnoException e) {
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
index f38888aafbd6..868f3bed4eed 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
@@ -30,10 +30,10 @@ import android.annotation.Nullable;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.NetworkUtils;
-
-import com.google.android.collect.Sets;
+import android.util.ArraySet;
import java.net.Inet4Address;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -208,7 +208,7 @@ public class DhcpServingParams {
* but it must always be set explicitly before building the {@link DhcpServingParams}.
*/
public Builder setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
- return setDefaultRouters(Sets.newArraySet(defaultRouters));
+ return setDefaultRouters(new ArraySet<>(Arrays.asList(defaultRouters)));
}
/**
@@ -238,7 +238,7 @@ public class DhcpServingParams {
* building the {@link DhcpServingParams}.
*/
public Builder setDnsServers(@NonNull Inet4Address... dnsServers) {
- return setDnsServers(Sets.newArraySet(dnsServers));
+ return setDnsServers(new ArraySet<>(Arrays.asList(dnsServers)));
}
/**
@@ -268,7 +268,7 @@ public class DhcpServingParams {
* and do not need to be set here.
*/
public Builder setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
- return setExcludedAddrs(Sets.newArraySet(excludedAddrs));
+ return setExcludedAddrs(new ArraySet<>(Arrays.asList(excludedAddrs)));
}
/**
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index c8a8e1f8e3db..a3d7852c9f4a 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -24,6 +24,7 @@ import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE;
@@ -80,7 +81,6 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Protocol;
import com.android.internal.util.RingBufferIndices;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -150,29 +150,28 @@ public class NetworkMonitor extends StateMachine {
}
}
- private static final int BASE = Protocol.BASE_NETWORK_MONITOR;
/**
* ConnectivityService has sent a notification to indicate that network has connected.
* Initiates Network Validation.
*/
- private static final int CMD_NETWORK_CONNECTED = BASE + 1;
+ private static final int CMD_NETWORK_CONNECTED = 1;
/**
* Message to self indicating it's time to evaluate a network's connectivity.
* arg1 = Token to ignore old messages.
*/
- private static final int CMD_REEVALUATE = BASE + 6;
+ private static final int CMD_REEVALUATE = 6;
/**
* ConnectivityService has sent a notification to indicate that network has disconnected.
*/
- private static final int CMD_NETWORK_DISCONNECTED = BASE + 7;
+ private static final int CMD_NETWORK_DISCONNECTED = 7;
/**
* Force evaluation even if it has succeeded in the past.
* arg1 = UID responsible for requesting this reeval. Will be billed for data.
*/
- private static final int CMD_FORCE_REEVALUATION = BASE + 8;
+ private static final int CMD_FORCE_REEVALUATION = 8;
/**
* Message to self indicating captive portal app finished.
@@ -181,7 +180,7 @@ public class NetworkMonitor extends StateMachine {
* APP_RETURN_WANTED_AS_IS
* obj = mCaptivePortalLoggedInResponseToken as String
*/
- private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
+ private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = 9;
/**
* Message indicating sign-in app should be launched.
@@ -190,14 +189,14 @@ public class NetworkMonitor extends StateMachine {
* ConnectivityService when the user touches the "sign into
* network" button in the wifi access point detail page.
*/
- private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
+ private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = 11;
/**
* Retest network to see if captive portal is still in place.
* arg1 = UID responsible for requesting this reeval. Will be billed for data.
* 0 indicates self-initiated, so nobody to blame.
*/
- private static final int CMD_CAPTIVE_PORTAL_RECHECK = BASE + 12;
+ private static final int CMD_CAPTIVE_PORTAL_RECHECK = 12;
/**
* ConnectivityService notifies NetworkMonitor of settings changes to
@@ -210,20 +209,20 @@ public class NetworkMonitor extends StateMachine {
* states, including being ignored until after an ongoing captive portal
* validation phase is completed.
*/
- private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13;
- private static final int CMD_EVALUATE_PRIVATE_DNS = BASE + 15;
+ private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = 13;
+ private static final int CMD_EVALUATE_PRIVATE_DNS = 15;
/**
* Message to self indicating captive portal detection is completed.
* obj = CaptivePortalProbeResult for detection result;
*/
- public static final int CMD_PROBE_COMPLETE = BASE + 16;
+ public static final int CMD_PROBE_COMPLETE = 16;
/**
* ConnectivityService notifies NetworkMonitor of DNS query responses event.
* arg1 = returncode in OnDnsEvent which indicates the response code for the DNS query.
*/
- public static final int EVENT_DNS_NOTIFICATION = BASE + 17;
+ public static final int EVENT_DNS_NOTIFICATION = 17;
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
@@ -245,7 +244,6 @@ public class NetworkMonitor extends StateMachine {
private final INetworkMonitorCallbacks mCallback;
private final Network mNetwork;
private final Network mNonPrivateDnsBypassNetwork;
- private final int mNetId;
private final TelephonyManager mTelephonyManager;
private final WifiManager mWifiManager;
private final ConnectivityManager mCm;
@@ -322,7 +320,7 @@ public class NetworkMonitor extends StateMachine {
NetworkRequest defaultRequest, IpConnectivityLog logger, SharedLog validationLogs,
Dependencies deps) {
// Add suffix indicating which NetworkMonitor we're talking about.
- super(TAG + "/" + network.netId);
+ super(TAG + "/" + network.toString());
// Logs with a tag of the form given just above, e.g.
// <timestamp> 862 2402 D NetworkMonitor/NetworkAgentInfo [WIFI () - 100]: ...
@@ -335,7 +333,6 @@ public class NetworkMonitor extends StateMachine {
mDependencies = deps;
mNonPrivateDnsBypassNetwork = network;
mNetwork = deps.getPrivateDnsBypassNetwork(network);
- mNetId = mNetwork.netId;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -471,7 +468,7 @@ public class NetworkMonitor extends StateMachine {
@Override
protected void log(String s) {
- if (DBG) Log.d(TAG + "/" + mNetwork.netId, s);
+ if (DBG) Log.d(TAG + "/" + mNetwork.toString(), s);
}
private void validationLog(int probeType, Object url, String msg) {
@@ -795,7 +792,7 @@ public class NetworkMonitor extends StateMachine {
CustomIntentReceiver(String action, int token, int what) {
mToken = token;
mWhat = what;
- mAction = action + "_" + mNetId + "_" + token;
+ mAction = action + "_" + mNetwork.getNetworkHandle() + "_" + token;
mContext.registerReceiver(this, new IntentFilter(mAction));
}
public PendingIntent getPendingIntent() {
@@ -1088,11 +1085,6 @@ public class NetworkMonitor extends StateMachine {
return mDependencies.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
}
- private boolean getWifiScansAlwaysAvailableDisabled() {
- return mDependencies.getSetting(
- mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
- }
-
private String getCaptivePortalServerHttpsUrl() {
return mDependencies.getSetting(mContext,
Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
@@ -1484,7 +1476,7 @@ public class NetworkMonitor extends StateMachine {
*/
private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
long requestTimestampMs, long responseTimestampMs) {
- if (getWifiScansAlwaysAvailableDisabled()) {
+ if (!mWifiManager.isScanAlwaysAvailable()) {
return;
}
@@ -1568,7 +1560,7 @@ public class NetworkMonitor extends StateMachine {
private void logNetworkEvent(int evtype) {
int[] transports = mNetworkCapabilities.getTransportTypes();
- mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype));
+ mMetricsLog.log(mNetwork, transports, new NetworkEvent(evtype));
}
private int networkEventType(ValidationStage s, EvaluationResult r) {
@@ -1590,7 +1582,8 @@ public class NetworkMonitor extends StateMachine {
private void maybeLogEvaluationResult(int evtype) {
if (mEvaluationTimer.isRunning()) {
int[] transports = mNetworkCapabilities.getTransportTypes();
- mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype, mEvaluationTimer.stop()));
+ mMetricsLog.log(mNetwork, transports,
+ new NetworkEvent(evtype, mEvaluationTimer.stop()));
mEvaluationTimer.reset();
}
}
@@ -1603,7 +1596,7 @@ public class NetworkMonitor extends StateMachine {
.setReturnCode(probeResult)
.setDurationMs(durationMs)
.build();
- mMetricsLog.log(mNetId, transports, ev);
+ mMetricsLog.log(mNetwork, transports, ev);
}
@VisibleForTesting
@@ -1743,7 +1736,7 @@ public class NetworkMonitor extends StateMachine {
boolean result = false;
// Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the
// possible traffic cost in metered network.
- if (mNetworkCapabilities.isMetered()
+ if (!mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
&& (SystemClock.elapsedRealtime() - getLastProbeTime()
< mDataStallMinEvaluateTime)) {
return false;
diff --git a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml
index 9f30eda242f6..716fc8ded734 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml
+++ b/packages/SettingsLib/EntityHeaderWidgets/res/layout/app_entities_header.xml
@@ -33,7 +33,7 @@
android:textAppearance="@style/AppEntitiesHeader.Text.HeaderTitle"/>
<LinearLayout
- android:id="@+id/all_apps_view"
+ android:id="@+id/app_views_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
@@ -61,4 +61,12 @@
android:layout_height="48dp"
android:gravity="center"/>
+ <TextView
+ android:id="@+id/empty_view"
+ android:layout_width="match_parent"
+ android:layout_height="106dp"
+ android:gravity="center"
+ android:visibility="gone"
+ android:textAppearance="@style/AppEntitiesHeader.Text.Summary"/>
+
</LinearLayout>
diff --git a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
index 73cb8db136b5..330049fc6673 100644
--- a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
+++ b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java
@@ -77,9 +77,11 @@ public class AppEntitiesHeaderController {
private final Context mContext;
private final TextView mHeaderTitleView;
+ private final TextView mHeaderEmptyView;
private final Button mHeaderDetailsView;
private final AppEntityInfo[] mAppEntityInfos;
+ private final View mAppViewsContainer;
private final View[] mAppEntityViews;
private final ImageView[] mAppIconViews;
private final TextView[] mAppTitleViews;
@@ -87,6 +89,7 @@ public class AppEntitiesHeaderController {
private int mHeaderTitleRes;
private int mHeaderDetailsRes;
+ private int mHeaderEmptyRes;
private View.OnClickListener mDetailsOnClickListener;
/**
@@ -104,6 +107,8 @@ public class AppEntitiesHeaderController {
mContext = context;
mHeaderTitleView = appEntitiesHeaderView.findViewById(R.id.header_title);
mHeaderDetailsView = appEntitiesHeaderView.findViewById(R.id.header_details);
+ mHeaderEmptyView = appEntitiesHeaderView.findViewById(R.id.empty_view);
+ mAppViewsContainer = appEntitiesHeaderView.findViewById(R.id.app_views_container);
mAppEntityInfos = new AppEntityInfo[MAXIMUM_APPS];
mAppIconViews = new ImageView[MAXIMUM_APPS];
@@ -152,6 +157,14 @@ public class AppEntitiesHeaderController {
}
/**
+ * Sets the string resource id for the empty text.
+ */
+ public AppEntitiesHeaderController setHeaderEmptyRes(@StringRes int emptyRes) {
+ mHeaderEmptyRes = emptyRes;
+ return this;
+ }
+
+ /**
* Set an app entity at a specified position view.
*
* @param index the index at which the specified view is to be inserted
@@ -192,6 +205,12 @@ public class AppEntitiesHeaderController {
*/
public void apply() {
bindHeaderTitleView();
+
+ if (isAppEntityInfosEmpty()) {
+ setEmptyViewVisible(true);
+ return;
+ }
+ setEmptyViewVisible(false);
bindHeaderDetailsView();
// Rebind all apps view
@@ -245,4 +264,22 @@ public class AppEntitiesHeaderController {
mAppSummaryViews[index].setText(summary);
}
}
+
+ private void setEmptyViewVisible(boolean visible) {
+ if (mHeaderEmptyRes != 0) {
+ mHeaderEmptyView.setText(mHeaderEmptyRes);
+ }
+ mHeaderEmptyView.setVisibility(visible ? View.VISIBLE : View.GONE);
+ mHeaderDetailsView.setVisibility(visible ? View.GONE : View.VISIBLE);
+ mAppViewsContainer.setVisibility(visible ? View.GONE : View.VISIBLE);
+ }
+
+ private boolean isAppEntityInfosEmpty() {
+ for (AppEntityInfo info : mAppEntityInfos) {
+ if (info != null) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 27dc628ac094..9b12a31dc149 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -622,7 +622,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
.append(KEY_PREFIX_FQDN)
.append(config.FQDN).toString();
} else {
- return getKey(config.SSID, config.BSSID, getSecurity(config));
+ return getKey(removeDoubleQuotes(config.SSID), config.BSSID, getSecurity(config));
}
}
@@ -1555,7 +1555,7 @@ public class AccessPoint implements Comparable<AccessPoint> {
mOsuFailure = mContext.getString(
R.string.osu_failure_provisioning_not_available);
break;
- case OSU_FAILURE_INVALID_SERVER_URL:
+ case OSU_FAILURE_INVALID_URL_FORMAT_FOR_OSU:
mOsuFailure = mContext.getString(R.string.osu_failure_invalid_server_url);
break;
case OSU_FAILURE_UNEXPECTED_COMMAND_TYPE:
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
index 8c18c356159e..4c68c1476cff 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java
@@ -60,6 +60,7 @@ public class AppEntitiesHeaderControllerTest {
.setOnClickListener(v -> {
})
.build();
+ mController.setAppEntity(0, mAppEntityInfo);
}
@Test
@@ -172,6 +173,8 @@ public class AppEntitiesHeaderControllerTest {
mController.setAppEntity(0, mAppEntityInfo)
.setAppEntity(1, mAppEntityInfo)
.setAppEntity(2, mAppEntityInfo).apply();
+ final View appViewsContainer = mAppEntitiesHeaderView.findViewById(
+ R.id.app_views_container);
final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view);
final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view);
final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view);
@@ -181,8 +184,28 @@ public class AppEntitiesHeaderControllerTest {
assertThat(app3View.getVisibility()).isEqualTo(View.VISIBLE);
mController.clearAllAppEntities().apply();
- assertThat(app1View.getVisibility()).isEqualTo(View.GONE);
- assertThat(app2View.getVisibility()).isEqualTo(View.GONE);
- assertThat(app3View.getVisibility()).isEqualTo(View.GONE);
+
+ assertThat(appViewsContainer.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void apply_noAppEntitySet_shouldOnlyShowTitleAndEmptyView() {
+ mController.setHeaderTitleRes(R.string.expand_button_title)
+ .setAppEntity(0, mAppEntityInfo)
+ .setAppEntity(1, mAppEntityInfo)
+ .setAppEntity(2, mAppEntityInfo).apply();
+ final View titleView = mAppEntitiesHeaderView.findViewById(R.id.header_title);
+ final View detailsView = mAppEntitiesHeaderView.findViewById(R.id.header_details);
+ final View emptyView = mAppEntitiesHeaderView.findViewById(R.id.empty_view);
+ final View appViewsContainer = mAppEntitiesHeaderView.findViewById(
+ R.id.app_views_container);
+
+ mController.clearAllAppEntities().apply();
+
+ assertThat(titleView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(emptyView.getVisibility()).isEqualTo(View.VISIBLE);
+
+ assertThat(detailsView.getVisibility()).isEqualTo(View.GONE);
+ assertThat(appViewsContainer.getVisibility()).isEqualTo(View.GONE);
}
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b903142c44c6..c3c3f25a9f03 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -166,6 +166,8 @@
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
<uses-permission android:name="android.permission.SUSPEND_APPS" />
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
+ <!-- Permission needed to wipe the device for Test Harness Mode -->
+ <uses-permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE" />
<uses-permission android:name="android.permission.MANAGE_APPOPS" />
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 98f0cbe29110..f2be2e7d8f28 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -458,6 +458,11 @@
heads-up notifications. -->
<bool name="config_smart_replies_in_notifications_show_in_heads_up">true</bool>
+ <!-- Smart replies in notifications: Minimum number of system generated smart replies that
+ should be shown in a notification. If we cannot show at least this many replies we instead
+ show none. -->
+ <integer name="config_smart_replies_in_notifications_min_num_system_generated_replies">0</integer>
+
<!-- Screenshot editing default activity. Must handle ACTION_EDIT image/png intents.
Blank sends the user to the Chooser first.
This name is in the ComponentName flattened format (package/class) -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f384d8f5b357..d64e2f9d21ce 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -150,6 +150,9 @@
<!-- Option to always allow USB debugging from the attached computer -->
<string name="usb_debugging_always">Always allow from this computer</string>
+ <!-- Button label for confirming acceptance of enabling USB debugging [CHAR LIMIT=15] -->
+ <string name="usb_debugging_allow">Allow</string>
+
<!-- Title of notification shown when trying to enable USB debugging but a secondary user is the current foreground user. -->
<string name="usb_debugging_secondary_user_title">USB debugging not allowed</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 1aff3949a74b..7218acf614d4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,15 +1,9 @@
package com.android.keyguard;
-import android.content.ContentResolver;
import android.content.Context;
-import android.database.ContentObserver;
import android.graphics.Paint;
import android.graphics.Paint.Style;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
import android.util.AttributeSet;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -18,29 +12,19 @@ import android.widget.TextClock;
import androidx.annotation.VisibleForTesting;
-import com.android.keyguard.clock.BubbleClockController;
-import com.android.keyguard.clock.StretchAnalogClockController;
-import com.android.keyguard.clock.TypeClockController;
+import com.android.keyguard.clock.ClockManager;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionController.Extension;
-import java.util.Objects;
import java.util.TimeZone;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
/**
* Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
*/
public class KeyguardClockSwitch extends RelativeLayout {
- private LayoutInflater mLayoutInflater;
-
- private final ContentResolver mContentResolver;
/**
* Optional/alternative clock injected via plugin.
*/
@@ -63,14 +47,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
*/
private View mKeyguardStatusArea;
/**
- * Used to select between plugin or default implementations of ClockPlugin interface.
- */
- private Extension<ClockPlugin> mClockExtension;
- /**
- * Consumer that accepts the a new ClockPlugin implementation when the Extension reloads.
- */
- private final Consumer<ClockPlugin> mClockPluginConsumer = plugin -> setClockPlugin(plugin);
- /**
* Maintain state so that a newly connected plugin can be initialized.
*/
private float mDarkAmount;
@@ -94,16 +70,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
}
};
- private final ContentObserver mContentObserver =
- new ContentObserver(new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- if (mClockExtension != null) {
- mClockExtension.reload();
- }
- }
- };
+ private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
public KeyguardClockSwitch(Context context) {
this(context, null);
@@ -111,8 +78,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
public KeyguardClockSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
- mLayoutInflater = LayoutInflater.from(context);
- mContentResolver = context.getContentResolver();
}
/**
@@ -133,45 +98,14 @@ public class KeyguardClockSwitch extends RelativeLayout {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mClockExtension = Dependency.get(ExtensionController.class).newExtension(ClockPlugin.class)
- .withPlugin(ClockPlugin.class)
- .withCallback(mClockPluginConsumer)
- // Using withDefault even though this isn't the default as a workaround.
- // ExtensionBulider doesn't provide the ability to supply a ClockPlugin
- // instance based off of the value of a setting. Since multiple "default"
- // can be provided, using a supplier that changes the settings value.
- // A null return will cause Extension#reload to look at the next "default"
- // supplier.
- .withDefault(
- new SettingsGattedSupplier(
- mContentResolver,
- Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
- BubbleClockController.class.getName(),
- () -> BubbleClockController.build(mLayoutInflater)))
- .withDefault(
- new SettingsGattedSupplier(
- mContentResolver,
- Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
- StretchAnalogClockController.class.getName(),
- () -> StretchAnalogClockController.build(mLayoutInflater)))
- .withDefault(
- new SettingsGattedSupplier(
- mContentResolver,
- Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
- TypeClockController.class.getName(),
- () -> TypeClockController.build(mLayoutInflater)))
- .build();
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
- false, mContentObserver);
+ Dependency.get(ClockManager.class).addOnClockChangedListener(mClockChangedListener);
Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- mClockExtension.destroy();
- mContentResolver.unregisterContentObserver(mContentObserver);
+ Dependency.get(ClockManager.class).removeOnClockChangedListener(mClockChangedListener);
Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
}
@@ -313,52 +247,12 @@ public class KeyguardClockSwitch extends RelativeLayout {
}
@VisibleForTesting (otherwise = VisibleForTesting.NONE)
- Consumer<ClockPlugin> getClockPluginConsumer() {
- return mClockPluginConsumer;
+ ClockManager.ClockChangedListener getClockChangedListener() {
+ return mClockChangedListener;
}
@VisibleForTesting (otherwise = VisibleForTesting.NONE)
StatusBarStateController.StateListener getStateListener() {
return mStateListener;
}
-
- /**
- * Supplier that only gets an instance when a settings value matches expected value.
- */
- private static class SettingsGattedSupplier implements Supplier<ClockPlugin> {
-
- private final ContentResolver mContentResolver;
- private final String mKey;
- private final String mValue;
- private final Supplier<ClockPlugin> mSupplier;
-
- /**
- * Constructs a supplier that changes secure setting key against value.
- *
- * @param contentResolver Used to look up settings value.
- * @param key Settings key.
- * @param value If the setting matches this values that get supplies a ClockPlugin
- * instance.
- * @param supplier Supplier of ClockPlugin instance, only used if the setting
- * matches value.
- */
- SettingsGattedSupplier(ContentResolver contentResolver, String key, String value,
- Supplier<ClockPlugin> supplier) {
- mContentResolver = contentResolver;
- mKey = key;
- mValue = value;
- mSupplier = supplier;
- }
-
- /**
- * Returns null if the settings value doesn't match the expected value.
- *
- * A null return causes Extension#reload to skip this supplier and move to the next.
- */
- @Override
- public ClockPlugin get() {
- final String currentValue = Settings.Secure.getString(mContentResolver, mKey);
- return Objects.equals(currentValue, mValue) ? mSupplier.get() : null;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
new file mode 100644
index 000000000000..3217ca6f489c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -0,0 +1,202 @@
+/*
+ * 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.keyguard.clock;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+
+import com.android.systemui.plugins.ClockPlugin;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionController.Extension;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Manages custom clock faces.
+ */
+@Singleton
+public final class ClockManager {
+
+ private final LayoutInflater mLayoutInflater;
+ private final ContentResolver mContentResolver;
+
+ /**
+ * Observe settings changes to know when to switch the clock face.
+ */
+ private final ContentObserver mContentObserver =
+ new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ if (mClockExtension != null) {
+ mClockExtension.reload();
+ }
+ }
+ };
+
+ private final ExtensionController mExtensionController;
+ /**
+ * Used to select between plugin or default implementations of ClockPlugin interface.
+ */
+ private Extension<ClockPlugin> mClockExtension;
+ /**
+ * Consumer that accepts the a new ClockPlugin implementation when the Extension reloads.
+ */
+ private final Consumer<ClockPlugin> mClockPluginConsumer = this::setClockPlugin;
+
+ private final List<ClockChangedListener> mListeners = new ArrayList<>();
+
+ @Inject
+ public ClockManager(Context context, ExtensionController extensionController) {
+ mExtensionController = extensionController;
+ mLayoutInflater = LayoutInflater.from(context);
+ mContentResolver = context.getContentResolver();
+ }
+
+ /**
+ * Add listener to be notified when clock implementation should change.
+ */
+ public void addOnClockChangedListener(ClockChangedListener listener) {
+ if (mListeners.isEmpty()) {
+ register();
+ }
+ mListeners.add(listener);
+ if (mClockExtension != null) {
+ mClockExtension.reload();
+ }
+ }
+
+ /**
+ * Remove listener added with {@link addOnClockChangedListener}.
+ */
+ public void removeOnClockChangedListener(ClockChangedListener listener) {
+ mListeners.remove(listener);
+ if (mListeners.isEmpty()) {
+ unregister();
+ }
+ }
+
+ private void setClockPlugin(ClockPlugin plugin) {
+ for (int i = 0; i < mListeners.size(); i++) {
+ // It probably doesn't make sense to supply the same plugin instances to multiple
+ // listeners. This should be fine for now since there is only a single listener.
+ mListeners.get(i).onClockChanged(plugin);
+ }
+ }
+
+ private void register() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
+ false, mContentObserver);
+ mClockExtension = mExtensionController.newExtension(ClockPlugin.class)
+ .withPlugin(ClockPlugin.class)
+ .withCallback(mClockPluginConsumer)
+ // Using withDefault even though this isn't the default as a workaround.
+ // ExtensionBuilder doesn't provide the ability to supply a ClockPlugin
+ // instance based off of the value of a setting. Since multiple "default"
+ // can be provided, using a supplier that changes the settings value.
+ // A null return will cause Extension#reload to look at the next "default"
+ // supplier.
+ .withDefault(
+ new SettingsGattedSupplier(
+ mContentResolver,
+ Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+ BubbleClockController.class.getName(),
+ () -> BubbleClockController.build(mLayoutInflater)))
+ .withDefault(
+ new SettingsGattedSupplier(
+ mContentResolver,
+ Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+ StretchAnalogClockController.class.getName(),
+ () -> StretchAnalogClockController.build(mLayoutInflater)))
+ .withDefault(
+ new SettingsGattedSupplier(
+ mContentResolver,
+ Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
+ TypeClockController.class.getName(),
+ () -> TypeClockController.build(mLayoutInflater)))
+ .build();
+ }
+
+ private void unregister() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ mClockExtension.destroy();
+ }
+
+ /**
+ * Listener for events that should cause the custom clock face to change.
+ */
+ public interface ClockChangedListener {
+ /**
+ * Called when custom clock should change.
+ *
+ * @param clock Custom clock face to use. A null value indicates the default clock face.
+ */
+ void onClockChanged(ClockPlugin clock);
+ }
+
+ /**
+ * Supplier that only gets an instance when a settings value matches expected value.
+ */
+ private static class SettingsGattedSupplier implements Supplier<ClockPlugin> {
+
+ private final ContentResolver mContentResolver;
+ private final String mKey;
+ private final String mValue;
+ private final Supplier<ClockPlugin> mSupplier;
+
+ /**
+ * Constructs a supplier that changes secure setting key against value.
+ *
+ * @param contentResolver Used to look up settings value.
+ * @param key Settings key.
+ * @param value If the setting matches this values that get supplies a ClockPlugin
+ * instance.
+ * @param supplier Supplier of ClockPlugin instance, only used if the setting
+ * matches value.
+ */
+ SettingsGattedSupplier(ContentResolver contentResolver, String key, String value,
+ Supplier<ClockPlugin> supplier) {
+ mContentResolver = contentResolver;
+ mKey = key;
+ mValue = value;
+ mSupplier = supplier;
+ }
+
+ /**
+ * Returns null if the settings value doesn't match the expected value.
+ *
+ * A null return causes Extension#reload to skip this supplier and move to the next.
+ */
+ @Override
+ public ClockPlugin get() {
+ final String currentValue = Settings.Secure.getString(mContentResolver, mKey);
+ return Objects.equals(currentValue, mValue) ? mSupplier.get() : null;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index ec6ecc64d07e..d99f234c26c8 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -29,6 +29,7 @@ import com.android.internal.app.ColorDisplayController;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.Preconditions;
+import com.android.keyguard.clock.ClockManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.appops.AppOpsController;
import com.android.systemui.assist.AssistManager;
@@ -283,6 +284,7 @@ public class Dependency extends SystemUI {
@Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler;
@Nullable
@Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail;
+ @Inject Lazy<ClockManager> mClockManager;
@Inject
public Dependency() {
@@ -449,6 +451,7 @@ public class Dependency extends SystemUI {
mProviders.put(NotificationAlertingManager.class, mNotificationAlertingManager::get);
mProviders.put(ForegroundServiceNotificationListener.class,
mForegroundServiceNotificationListener::get);
+ mProviders.put(ClockManager.class, mClockManager::get);
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 1d2d7fafe800..2aecc24e83c0 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -16,14 +16,18 @@
package com.android.systemui;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import android.app.WallpaperManager;
import android.content.ComponentCallbacks2;
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region.Op;
+import android.hardware.display.DisplayManager;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Trace;
@@ -33,7 +37,6 @@ import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceHolder;
-import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
@@ -94,7 +97,7 @@ public class ImageWallpaper extends WallpaperService {
float mYOffset = 0f;
float mScale = 1f;
- private Display mDefaultDisplay;
+ private Display mDisplay;
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
boolean mVisible = true;
@@ -138,10 +141,20 @@ public class ImageWallpaper extends WallpaperService {
super.onCreate(surfaceHolder);
//noinspection ConstantConditions
- mDefaultDisplay = getSystemService(WindowManager.class).getDefaultDisplay();
+ final Context displayContext = getDisplayContext();
+ final int displayId = displayContext == null ? DEFAULT_DISPLAY :
+ displayContext.getDisplayId();
+ DisplayManager dm = getSystemService(DisplayManager.class);
+ if (dm != null) {
+ mDisplay = dm.getDisplay(displayId);
+ if (mDisplay == null) {
+ Log.e(TAG, "Cannot find display! Fallback to default.");
+ mDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+ }
+ }
setOffsetNotificationsEnabled(false);
- updateSurfaceSize(surfaceHolder, getDefaultDisplayInfo(), false /* forDraw */);
+ updateSurfaceSize(surfaceHolder, getDisplayInfo(), false /* forDraw */);
}
@Override
@@ -165,9 +178,26 @@ public class ImageWallpaper extends WallpaperService {
hasWallpaper = false;
}
- // Set surface size equal to bitmap size, prevent memory waste
- int surfaceWidth = Math.max(MIN_BACKGROUND_WIDTH, mBackgroundWidth);
- int surfaceHeight = Math.max(MIN_BACKGROUND_HEIGHT, mBackgroundHeight);
+ // Expected surface size.
+ int surfaceWidth = Math.max(displayInfo.logicalWidth, mBackgroundWidth);
+ int surfaceHeight = Math.max(displayInfo.logicalHeight, mBackgroundHeight);
+
+ // Calculate the minimum drawing area of the surface, which saves memory and does not
+ // distort the image.
+ final float scale = Math.min(
+ (float) mBackgroundHeight / (float) surfaceHeight,
+ (float) mBackgroundWidth / (float) surfaceWidth);
+ surfaceHeight = (int) (scale * surfaceHeight);
+ surfaceWidth = (int) (scale * surfaceWidth);
+
+ // Set surface size to at least MIN size.
+ if (surfaceWidth < MIN_BACKGROUND_WIDTH || surfaceHeight < MIN_BACKGROUND_HEIGHT) {
+ final float scaleUp = Math.max(
+ (float) MIN_BACKGROUND_WIDTH / (float) surfaceWidth,
+ (float) MIN_BACKGROUND_HEIGHT / (float) surfaceHeight);
+ surfaceWidth = (int) ((float) surfaceWidth * scaleUp);
+ surfaceHeight = (int) ((float) surfaceHeight * scaleUp);
+ }
// Used a fixed size surface, because we are special. We can do
// this because we know the current design of window animations doesn't
@@ -267,8 +297,8 @@ public class ImageWallpaper extends WallpaperService {
}
@VisibleForTesting
- DisplayInfo getDefaultDisplayInfo() {
- mDefaultDisplay.getDisplayInfo(mTmpDisplayInfo);
+ DisplayInfo getDisplayInfo() {
+ mDisplay.getDisplayInfo(mTmpDisplayInfo);
return mTmpDisplayInfo;
}
@@ -278,7 +308,7 @@ public class ImageWallpaper extends WallpaperService {
}
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawWallpaper");
- DisplayInfo displayInfo = getDefaultDisplayInfo();
+ DisplayInfo displayInfo = getDisplayInfo();
int newRotation = displayInfo.rotation;
// Sometimes a wallpaper is not large enough to cover the screen in one dimension.
@@ -445,7 +475,7 @@ public class ImageWallpaper extends WallpaperService {
if (DEBUG) {
Log.d(TAG, "Wallpaper loaded: " + mBackground);
}
- updateSurfaceSize(getSurfaceHolder(), getDefaultDisplayInfo(),
+ updateSurfaceSize(getSurfaceHolder(), getDisplayInfo(),
false /* forDraw */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 36664004a4a6..9efa656b3ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -604,8 +604,14 @@ public class SwipeHelper implements Gefingerpoken {
if (absDelta >= size) {
delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
} else {
- delta = maxScrollDistance * (float) Math.sin(
- (delta / size) * (Math.PI / 2));
+ int startPosition = mCallback.getConstrainSwipeStartPosition();
+ if (absDelta > startPosition) {
+ int signedStartPosition =
+ (int) (startPosition * Math.signum(delta));
+ delta = signedStartPosition
+ + maxScrollDistance * (float) Math.sin(
+ ((delta - signedStartPosition) / size) * (Math.PI / 2));
+ }
}
}
@@ -742,6 +748,14 @@ public class SwipeHelper implements Gefingerpoken {
float getFalsingThresholdFactor();
/**
+ * @return The position, in pixels, at which a constrained swipe should start being
+ * constrained.
+ */
+ default int getConstrainSwipeStartPosition() {
+ return 0;
+ }
+
+ /**
* @return If true, the given view is draggable.
*/
default boolean canChildBeDragged(@NonNull View animView) { return true; }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 957d772be730..a457deed7ba4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -267,8 +267,9 @@ public class BubbleController {
BubbleView bubble = (BubbleView) mInflater.inflate(
R.layout.bubble_view, mStackView, false /* attachToRoot */);
bubble.setNotif(notif);
- if (shouldUseActivityView(mContext)) {
- bubble.setAppOverlayIntent(getAppOverlayIntent(notif));
+ PendingIntent bubbleIntent = getValidBubbleIntent(notif);
+ if (shouldUseActivityView(mContext) || bubbleIntent != null) {
+ bubble.setBubbleIntent(getValidBubbleIntent(notif));
}
mBubbles.put(bubble.getKey(), bubble);
mStackView.addBubble(bubble);
@@ -282,7 +283,7 @@ public class BubbleController {
}
@Nullable
- private PendingIntent getAppOverlayIntent(NotificationEntry notif) {
+ private PendingIntent getValidBubbleIntent(NotificationEntry notif) {
Notification notification = notif.notification.getNotification();
if (canLaunchInActivityView(notification.getBubbleMetadata() != null
? notification.getBubbleMetadata().getIntent() : null)) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 9a11b965b319..dcd121bdb239 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -38,9 +38,11 @@ import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -226,6 +228,19 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F
}
/**
+ * Sets the entry that should be expanded and expands if needed.
+ */
+ @VisibleForTesting
+ public void setExpandedBubble(NotificationEntry entry) {
+ for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+ BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
+ if (entry.equals(bv.getEntry())) {
+ setExpandedBubble(bv);
+ }
+ }
+ }
+
+ /**
* Adds a bubble to the top of the stack.
*
* @param bubbleView the view to add to the stack.
@@ -456,7 +471,8 @@ public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.F
if (mExpandedBubble.hasAppOverlayIntent()) {
// Bubble with activity view expanded state
ActivityView expandedView = mExpandedBubble.getActivityView();
- expandedView.setLayoutParams(new ViewGroup.LayoutParams(
+ // XXX: gets added to linear layout
+ expandedView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight));
final PendingIntent intent = mExpandedBubble.getAppOverlayIntent();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 91893ef3db00..7b6e79be64db 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -298,7 +298,7 @@ public class BubbleView extends FrameLayout implements BubbleTouchHandler.Floati
}
- public void setAppOverlayIntent(PendingIntent intent) {
+ public void setBubbleIntent(PendingIntent intent) {
mAppOverlayIntent = intent;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManager.java b/packages/SystemUI/src/com/android/systemui/dock/DockManager.java
index fa5a114c7187..d332f59a4500 100644
--- a/packages/SystemUI/src/com/android/systemui/dock/DockManager.java
+++ b/packages/SystemUI/src/com/android/systemui/dock/DockManager.java
@@ -48,6 +48,11 @@ public interface DockManager {
*/
void removeListener(DockEventListener callback);
+ /**
+ * Returns true if the device is in docking state.
+ */
+ boolean isDocked();
+
/** Callback for receiving dock events */
interface DockEventListener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index 9fc22340ea22..5353ee6c8ab3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -22,7 +22,6 @@ import android.os.UserHandle;
import android.util.Log;
import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeMachine.State;
@@ -46,12 +45,12 @@ public class DozeDockHandler implements DozeMachine.Part {
private int mDockState = DockManager.STATE_NONE;
public DozeDockHandler(Context context, DozeMachine machine, DozeHost dozeHost,
- AmbientDisplayConfiguration config, Handler handler) {
+ AmbientDisplayConfiguration config, Handler handler, DockManager dockManager) {
mMachine = machine;
mDozeHost = dozeHost;
mConfig = config;
mHandler = handler;
- mDockManager = SysUiServiceProvider.getComponent(context, DockManager.class);
+ mDockManager = dockManager;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 58ae555dbff1..e338a3406d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -27,8 +27,10 @@ import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.classifier.FalsingManager;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.wakelock.DelayedWakeLock;
@@ -44,6 +46,7 @@ public class DozeFactory {
Context context = dozeService;
SensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
+ DockManager dockManager = SysUiServiceProvider.getComponent(context, DockManager.class);
DozeHost host = getHost(dozeService);
AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context);
@@ -63,13 +66,13 @@ public class DozeFactory {
new DozePauser(handler, machine, alarmManager, params.getPolicy()),
new DozeFalsingManagerAdapter(FalsingManager.getInstance(context)),
createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
- handler, wakeLock, machine),
+ handler, wakeLock, machine, dockManager),
createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params),
new DozeScreenState(wrappedService, handler, params, wakeLock),
createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
handler),
new DozeWallpaperState(context),
- new DozeDockHandler(context, machine, host, config, handler)
+ new DozeDockHandler(context, machine, host, config, handler, dockManager)
});
return machine;
@@ -86,10 +89,11 @@ public class DozeFactory {
private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager,
DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
- DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine) {
+ DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine,
+ DockManager dockManager) {
boolean allowPulseTriggers = true;
return new DozeTriggers(context, machine, host, alarmManager, config, params,
- sensorManager, handler, wakeLock, allowPulseTriggers);
+ sensorManager, handler, wakeLock, allowPulseTriggers, dockManager);
}
private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 78374a0b1621..562edd61c867 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -214,6 +214,15 @@ public class DozeSensors {
mPickupSensor.setDisabled(disable);
}
+ /** Ignore the setting value of only the sensors that require the touchscreen. */
+ public void ignoreTouchScreenSensorsSettingInterferingWithDocking(boolean ignore) {
+ for (TriggerSensor sensor : mSensors) {
+ if (sensor.mRequiresTouchscreen) {
+ sensor.ignoreSetting(ignore);
+ }
+ }
+ }
+
/** Dump current state */
public void dump(PrintWriter pw) {
for (TriggerSensor s : mSensors) {
@@ -323,6 +332,7 @@ public class DozeSensors {
protected boolean mRequested;
protected boolean mRegistered;
protected boolean mDisabled;
+ protected boolean mIgnoresSetting;
public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason,
boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
@@ -333,6 +343,13 @@ public class DozeSensors {
public TriggerSensor(Sensor sensor, String setting, boolean settingDef,
boolean configured, int pulseReason, boolean reportsTouchCoordinates,
boolean requiresTouchscreen) {
+ this(sensor, setting, settingDef, configured, pulseReason, reportsTouchCoordinates,
+ requiresTouchscreen, false /* ignoresSetting */);
+ }
+
+ private TriggerSensor(Sensor sensor, String setting, boolean settingDef,
+ boolean configured, int pulseReason, boolean reportsTouchCoordinates,
+ boolean requiresTouchscreen, boolean ignoresSetting) {
mSensor = sensor;
mSetting = setting;
mSettingDefault = settingDef;
@@ -340,6 +357,7 @@ public class DozeSensors {
mPulseReason = pulseReason;
mReportsTouchCoordinates = reportsTouchCoordinates;
mRequiresTouchscreen = requiresTouchscreen;
+ mIgnoresSetting = ignoresSetting;
}
public void setListening(boolean listen) {
@@ -354,9 +372,16 @@ public class DozeSensors {
updateListener();
}
+ public void ignoreSetting(boolean ignored) {
+ if (mIgnoresSetting == ignored) return;
+ mIgnoresSetting = ignored;
+ updateListener();
+ }
+
public void updateListener() {
if (!mConfigured || mSensor == null) return;
- if (mRequested && !mDisabled && enabledBySetting() && !mRegistered) {
+ if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)
+ && !mRegistered) {
mRegistered = mSensorManager.requestTriggerSensor(this, mSensor);
if (DEBUG) Log.d(TAG, "requestTriggerSensor " + mRegistered);
} else if (mRegistered) {
@@ -382,6 +407,7 @@ public class DozeSensors {
.append(", mRequested=").append(mRequested)
.append(", mDisabled=").append(mDisabled)
.append(", mConfigured=").append(mConfigured)
+ .append(", mIgnoresSetting=").append(mIgnoresSetting)
.append(", mSensor=").append(mSensor).append("}").toString();
}
@@ -464,7 +490,8 @@ public class DozeSensors {
public void updateListener() {
if (!mConfigured) return;
AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager;
- if (mRequested && !mDisabled && enabledBySetting() && !mRegistered) {
+ if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)
+ && !mRegistered) {
asyncSensorManager.registerPluginListener(mPluginSensor, mTriggerEventListener);
mRegistered = true;
if (DEBUG) Log.d(TAG, "registerPluginListener");
@@ -481,6 +508,7 @@ public class DozeSensors {
.append(", mRequested=").append(mRequested)
.append(", mDisabled=").append(mDisabled)
.append(", mConfigured=").append(mConfigured)
+ .append(", mIgnoresSetting=").append(mIgnoresSetting)
.append(", mSensor=").append(mPluginSensor).append("}").toString();
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index e2e448bb0d0c..dc505b57eb6b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -33,8 +33,10 @@ import android.os.UserHandle;
import android.text.format.Formatter;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.util.Preconditions;
+import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
import com.android.systemui.util.wakelock.WakeLock;
@@ -71,6 +73,8 @@ public class DozeTriggers implements DozeMachine.Part {
private final boolean mAllowPulseTriggers;
private final UiModeManager mUiModeManager;
private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
+ private final DockEventListener mDockEventListener = new DockEventListener();
+ private final DockManager mDockManager;
private long mNotificationPulseTime;
private boolean mPulsePending;
@@ -79,7 +83,7 @@ public class DozeTriggers implements DozeMachine.Part {
public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters dozeParameters, SensorManager sensorManager, Handler handler,
- WakeLock wakeLock, boolean allowPulseTriggers) {
+ WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager) {
mContext = context;
mMachine = machine;
mDozeHost = dozeHost;
@@ -93,6 +97,7 @@ public class DozeTriggers implements DozeMachine.Part {
config, wakeLock, this::onSensor, this::onProximityFar,
dozeParameters.getPolicy());
mUiModeManager = mContext.getSystemService(UiModeManager.class);
+ mDockManager = dockManager;
}
private void onNotification() {
@@ -129,7 +134,8 @@ public class DozeTriggers implements DozeMachine.Part {
}
}
- private void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
+ @VisibleForTesting
+ void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
float screenX, float screenY, float[] rawValues) {
boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
boolean isTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_TAP;
@@ -159,7 +165,7 @@ public class DozeTriggers implements DozeMachine.Part {
} else {
mDozeHost.extendPulse();
}
- }, sensorPerformedProxCheck, pulseReason);
+ }, sensorPerformedProxCheck || mDockManager.isDocked(), pulseReason);
}
if (isPickup) {
@@ -223,6 +229,7 @@ public class DozeTriggers implements DozeMachine.Part {
case INITIALIZED:
mBroadcastReceiver.register(mContext);
mDozeHost.addCallback(mHostCallback);
+ mDockManager.addListener(mDockEventListener);
checkTriggersAtInit();
break;
case DOZE:
@@ -248,6 +255,7 @@ public class DozeTriggers implements DozeMachine.Part {
case FINISH:
mBroadcastReceiver.unregister(mContext);
mDozeHost.removeCallback(mHostCallback);
+ mDockManager.removeListener(mDockEventListener);
mDozeSensors.setListening(false);
mDozeSensors.setProxListening(false);
break;
@@ -423,6 +431,24 @@ public class DozeTriggers implements DozeMachine.Part {
}
}
+ private class DockEventListener implements DockManager.DockEventListener {
+ @Override
+ public void onEvent(int event) {
+ if (DEBUG) Log.d(TAG, "dock event = " + event);
+ switch (event) {
+ case DockManager.STATE_DOCKED:
+ case DockManager.STATE_DOCKED_HIDE:
+ mDozeSensors.ignoreTouchScreenSensorsSettingInterferingWithDocking(true);
+ break;
+ case DockManager.STATE_NONE:
+ mDozeSensors.ignoreTouchScreenSensorsSettingInterferingWithDocking(false);
+ break;
+ default:
+ // no-op
+ }
+ }
+ }
+
private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
@Override
public void onNotificationAlerted() {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index 77e25e324915..26c6d501f2cb 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -30,6 +30,7 @@ import android.widget.TextView
import com.android.systemui.Dependency
import com.android.systemui.R
import com.android.systemui.plugins.ActivityStarter
+import java.util.concurrent.TimeUnit
class OngoingPrivacyDialog constructor(
val context: Context,
@@ -60,7 +61,8 @@ class OngoingPrivacyDialog constructor(
setNegativeButton(R.string.ongoing_privacy_dialog_cancel, null)
setPositiveButton(R.string.ongoing_privacy_dialog_open_settings,
object : DialogInterface.OnClickListener {
- val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE)
+ val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).putExtra(
+ Intent.EXTRA_DURATION_MILLIS, TimeUnit.MINUTES.toMillis(1))
@Suppress("DEPRECATION")
override fun onClick(dialog: DialogInterface?, which: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 5ba9b4b34042..76d394d197eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -32,6 +32,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarStateController;
@@ -460,7 +461,9 @@ public class NotificationLogger implements StateListener {
mUiOffloadThread.submit(() -> {
try {
mBarService.onNotificationExpansionChanged(
- key, stateToBeLogged.mIsUserAction, stateToBeLogged.mIsExpanded);
+ key, stateToBeLogged.mIsUserAction, stateToBeLogged.mIsExpanded,
+ // TODO (b/120767764): fill in location
+ ExpandableViewState.LOCATION_UNKNOWN /* notificationLocation */);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call onNotificationExpansionChanged: ", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index d0665670bf8f..0c37666a79d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -5818,6 +5818,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
}
@Override
+ public int getConstrainSwipeStartPosition() {
+ NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
+ if (menuRow != null) {
+ return Math.abs(menuRow.getMenuSnapTarget());
+ }
+ return 0;
+ }
+
+ @Override
public boolean canChildBeDismissed(View v) {
return NotificationStackScrollLayout.this.canChildBeDismissed(v);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
index 2a11c2676b0c..d02280836fb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
@@ -97,10 +97,11 @@ public class NearestTouchFrame extends FrameLayout {
}
return mClickableChildren
.stream()
- .filter(v -> v.isAttachedToWindow())
+ .filter(View::isAttachedToWindow)
.map(v -> new Pair<>(distance(v, event), v))
.min(Comparator.comparingInt(f -> f.first))
- .get().second;
+ .map(data -> data.second)
+ .orElse(null);
}
private int distance(View v, MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 88f904882c8f..ffaa236218fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -69,13 +69,13 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
private final WindowManager mWindowManager;
private final IActivityManager mActivityManager;
private final DozeParameters mDozeParameters;
+ private final WindowManager.LayoutParams mLpChanged;
+ private final boolean mKeyguardScreenRotation;
private ViewGroup mStatusBarView;
private WindowManager.LayoutParams mLp;
- private WindowManager.LayoutParams mLpChanged;
private boolean mHasTopUi;
private boolean mHasTopUiChanged;
private int mBarHeight;
- private final boolean mKeyguardScreenRotation;
private float mScreenBrightnessDoze;
private final State mCurrentState = new State();
private OtherwisedCollapsedListener mListener;
@@ -97,6 +97,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
mDozeParameters = dozeParameters;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
+ mLpChanged = new WindowManager.LayoutParams();
Dependency.get(StatusBarStateController.class).addCallback(
mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
Dependency.get(ConfigurationController.class).addCallback(this);
@@ -138,7 +139,6 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
mStatusBarView = statusBarView;
mBarHeight = barHeight;
mWindowManager.addView(mStatusBarView, mLp);
- mLpChanged = new WindowManager.LayoutParams();
mLpChanged.copyFrom(mLp);
onThemeChanged();
}
@@ -228,7 +228,9 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
private void applyHeight(State state) {
boolean expanded = isExpanded(state);
if (state.forcePluginOpen) {
- mListener.setWouldOtherwiseCollapse(expanded);
+ if (mListener != null) {
+ mListener.setWouldOtherwiseCollapse(expanded);
+ }
expanded = true;
}
if (expanded) {
@@ -247,7 +249,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
private void applyFitsSystemWindows(State state) {
boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded();
- if (mStatusBarView.getFitsSystemWindows() != fitsSystemWindows) {
+ if (mStatusBarView != null && mStatusBarView.getFitsSystemWindows() != fitsSystemWindows) {
mStatusBarView.setFitsSystemWindows(fitsSystemWindows);
mStatusBarView.requestApplyInsets();
}
@@ -289,7 +291,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat
applyBrightness(state);
applyHasTopUi(state);
applyNotTouchable(state);
- if (mLp.copyFrom(mLpChanged) != 0) {
+ if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
mWindowManager.updateViewLayout(mStatusBarView, mLp);
}
if (mHasTopUi != mHasTopUiChanged) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 3bd0d456dbd3..db0462095daf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -46,18 +46,21 @@ public final class SmartReplyConstants extends ContentObserver {
private static final String KEY_EDIT_CHOICES_BEFORE_SENDING =
"edit_choices_before_sending";
private static final String KEY_SHOW_IN_HEADS_UP = "show_in_heads_up";
+ private static final String KEY_MIN_NUM_REPLIES = "min_num_system_generated_replies";
private final boolean mDefaultEnabled;
private final boolean mDefaultRequiresP;
private final int mDefaultMaxSqueezeRemeasureAttempts;
private final boolean mDefaultEditChoicesBeforeSending;
private final boolean mDefaultShowInHeadsUp;
+ private final int mDefaultMinNumSystemGeneratedReplies;
private boolean mEnabled;
private boolean mRequiresTargetingP;
private int mMaxSqueezeRemeasureAttempts;
private boolean mEditChoicesBeforeSending;
private boolean mShowInHeadsUp;
+ private int mMinNumSystemGeneratedReplies;
private final Context mContext;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -78,6 +81,8 @@ public final class SmartReplyConstants extends ContentObserver {
R.bool.config_smart_replies_in_notifications_edit_choices_before_sending);
mDefaultShowInHeadsUp = resources.getBoolean(
R.bool.config_smart_replies_in_notifications_show_in_heads_up);
+ mDefaultMinNumSystemGeneratedReplies = resources.getInteger(
+ R.integer.config_smart_replies_in_notifications_min_num_system_generated_replies);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS),
@@ -105,6 +110,8 @@ public final class SmartReplyConstants extends ContentObserver {
mEditChoicesBeforeSending = mParser.getBoolean(
KEY_EDIT_CHOICES_BEFORE_SENDING, mDefaultEditChoicesBeforeSending);
mShowInHeadsUp = mParser.getBoolean(KEY_SHOW_IN_HEADS_UP, mDefaultShowInHeadsUp);
+ mMinNumSystemGeneratedReplies =
+ mParser.getInt(KEY_MIN_NUM_REPLIES, mDefaultMinNumSystemGeneratedReplies);
}
}
@@ -155,4 +162,12 @@ public final class SmartReplyConstants extends ContentObserver {
public boolean getShowInHeadsUp() {
return mShowInHeadsUp;
}
+
+ /**
+ * Returns the minimum number of system generated replies to show in a notification.
+ * If we cannot show at least this many system generated replies we should show none.
+ */
+ public int getMinNumSystemGeneratedReplies() {
+ return mMinNumSystemGeneratedReplies;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index d6eff941ed70..c4f027f2abea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -88,6 +88,12 @@ public class SmartReplyView extends ViewGroup {
private View mSmartReplyContainer;
+ /**
+ * Whether the smart replies in this view were generated by the notification assistant. If not
+ * they're provided by the app.
+ */
+ private boolean mSmartRepliesGeneratedByAssistant = false;
+
@ColorInt
private int mCurrentBackgroundColor;
@ColorInt
@@ -202,6 +208,7 @@ public class SmartReplyView extends ViewGroup {
getContext(), this, i, smartReplies, smartReplyController, entry);
addView(replyButton);
}
+ this.mSmartRepliesGeneratedByAssistant = smartReplies.fromAssistant;
}
}
reallocateCandidateButtonQueueForSqueezing();
@@ -344,10 +351,11 @@ public class SmartReplyView extends ViewGroup {
mCandidateButtonQueueForSqueezing.clear();
}
- int measuredWidth = mPaddingLeft + mPaddingRight;
- int maxChildHeight = 0;
+ SmartSuggestionMeasures accumulatedMeasures = new SmartSuggestionMeasures(
+ mPaddingLeft + mPaddingRight,
+ 0 /* maxChildHeight */,
+ mSingleLineButtonPaddingHorizontal);
int displayedChildCount = 0;
- int buttonPaddingHorizontal = mSingleLineButtonPaddingHorizontal;
// Set up a list of suggestions where actions come before replies. Note that the Buttons
// themselves have already been added to the view hierarchy in an order such that Smart
@@ -360,11 +368,15 @@ public class SmartReplyView extends ViewGroup {
smartSuggestions.addAll(smartReplies);
List<View> coveredSuggestions = new ArrayList<>();
+ // SmartSuggestionMeasures for all action buttons, this will be filled in when the first
+ // reply button is added.
+ SmartSuggestionMeasures actionsMeasures = null;
+
for (View child : smartSuggestions) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
- buttonPaddingHorizontal, child.getPaddingBottom());
+ child.setPadding(accumulatedMeasures.mButtonPaddingHorizontal, child.getPaddingTop(),
+ accumulatedMeasures.mButtonPaddingHorizontal, child.getPaddingBottom());
child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
coveredSuggestions.add(child);
@@ -380,45 +392,52 @@ public class SmartReplyView extends ViewGroup {
}
// Remember the current measurements in case the current button doesn't fit in.
- final int originalMaxChildHeight = maxChildHeight;
- final int originalMeasuredWidth = measuredWidth;
- final int originalButtonPaddingHorizontal = buttonPaddingHorizontal;
+ SmartSuggestionMeasures originalMeasures = accumulatedMeasures.clone();
+ if (actionsMeasures == null && lp.buttonType == SmartButtonType.REPLY) {
+ // We've added all actions (we go through actions first), now add their
+ // measurements.
+ actionsMeasures = accumulatedMeasures.clone();
+ }
final int spacing = displayedChildCount == 0 ? 0 : mSpacing;
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
- measuredWidth += spacing + childWidth;
- maxChildHeight = Math.max(maxChildHeight, childHeight);
+ accumulatedMeasures.mMeasuredWidth += spacing + childWidth;
+ accumulatedMeasures.mMaxChildHeight =
+ Math.max(accumulatedMeasures.mMaxChildHeight, childHeight);
// Do we need to increase the number of lines in smart reply buttons to two?
final boolean increaseToTwoLines =
- buttonPaddingHorizontal == mSingleLineButtonPaddingHorizontal
- && (lineCount == 2 || measuredWidth > targetWidth);
+ (accumulatedMeasures.mButtonPaddingHorizontal
+ == mSingleLineButtonPaddingHorizontal)
+ && (lineCount == 2 || accumulatedMeasures.mMeasuredWidth > targetWidth);
if (increaseToTwoLines) {
- measuredWidth += (displayedChildCount + 1) * mSingleToDoubleLineButtonWidthIncrease;
- buttonPaddingHorizontal = mDoubleLineButtonPaddingHorizontal;
+ accumulatedMeasures.mMeasuredWidth +=
+ (displayedChildCount + 1) * mSingleToDoubleLineButtonWidthIncrease;
+ accumulatedMeasures.mButtonPaddingHorizontal =
+ mDoubleLineButtonPaddingHorizontal;
}
// If the last button doesn't fit into the remaining width, try squeezing preceding
// smart reply buttons.
- if (measuredWidth > targetWidth) {
+ if (accumulatedMeasures.mMeasuredWidth > targetWidth) {
// Keep squeezing preceding and current smart reply buttons until they all fit.
- while (measuredWidth > targetWidth
+ while (accumulatedMeasures.mMeasuredWidth > targetWidth
&& !mCandidateButtonQueueForSqueezing.isEmpty()) {
final Button candidate = mCandidateButtonQueueForSqueezing.poll();
final int squeezeReduction = squeezeButton(candidate, heightMeasureSpec);
if (squeezeReduction != SQUEEZE_FAILED) {
- maxChildHeight = Math.max(maxChildHeight, candidate.getMeasuredHeight());
- measuredWidth -= squeezeReduction;
+ accumulatedMeasures.mMaxChildHeight =
+ Math.max(accumulatedMeasures.mMaxChildHeight,
+ candidate.getMeasuredHeight());
+ accumulatedMeasures.mMeasuredWidth -= squeezeReduction;
}
}
// If the current button still doesn't fit after squeezing all buttons, undo the
// last squeezing round.
- if (measuredWidth > targetWidth) {
- measuredWidth = originalMeasuredWidth;
- maxChildHeight = originalMaxChildHeight;
- buttonPaddingHorizontal = originalButtonPaddingHorizontal;
+ if (accumulatedMeasures.mMeasuredWidth > targetWidth) {
+ accumulatedMeasures = originalMeasures;
// Mark all buttons from the last squeezing round as "failed to squeeze", so
// that they're re-measured without squeezing later.
@@ -440,16 +459,75 @@ public class SmartReplyView extends ViewGroup {
displayedChildCount++;
}
+ if (mSmartRepliesGeneratedByAssistant) {
+ if (!gotEnoughSmartReplies(smartReplies)) {
+ // We don't have enough smart replies - hide all of them.
+ for (View smartReplyButton : smartReplies) {
+ final LayoutParams lp = (LayoutParams) smartReplyButton.getLayoutParams();
+ lp.show = false;
+ }
+ // Reset our measures back to when we had only added actions (before adding
+ // replies).
+ accumulatedMeasures = actionsMeasures;
+ }
+ }
+
// We're done squeezing buttons, so we can clear the priority queue.
mCandidateButtonQueueForSqueezing.clear();
// Finally, we need to re-measure some buttons.
- remeasureButtonsIfNecessary(buttonPaddingHorizontal, maxChildHeight);
+ remeasureButtonsIfNecessary(accumulatedMeasures.mButtonPaddingHorizontal,
+ accumulatedMeasures.mMaxChildHeight);
setMeasuredDimension(
- resolveSize(Math.max(getSuggestedMinimumWidth(), measuredWidth), widthMeasureSpec),
- resolveSize(Math.max(getSuggestedMinimumHeight(),
- mPaddingTop + maxChildHeight + mPaddingBottom), heightMeasureSpec));
+ resolveSize(Math.max(getSuggestedMinimumWidth(),
+ accumulatedMeasures.mMeasuredWidth),
+ widthMeasureSpec),
+ resolveSize(Math.max(getSuggestedMinimumHeight(), mPaddingTop
+ + accumulatedMeasures.mMaxChildHeight + mPaddingBottom),
+ heightMeasureSpec));
+ }
+
+ /**
+ * Fields we keep track of inside onMeasure() to correctly measure the SmartReplyView depending
+ * on which suggestions are added.
+ */
+ private static class SmartSuggestionMeasures {
+ int mMeasuredWidth = -1;
+ int mMaxChildHeight = -1;
+ int mButtonPaddingHorizontal = -1;
+
+ SmartSuggestionMeasures(int measuredWidth, int maxChildHeight,
+ int buttonPaddingHorizontal) {
+ this.mMeasuredWidth = measuredWidth;
+ this.mMaxChildHeight = maxChildHeight;
+ this.mButtonPaddingHorizontal = buttonPaddingHorizontal;
+ }
+
+ public SmartSuggestionMeasures clone() {
+ return new SmartSuggestionMeasures(
+ mMeasuredWidth, mMaxChildHeight, mButtonPaddingHorizontal);
+ }
+ }
+
+ /**
+ * Returns whether our notification contains at least N smart replies (or 0) where N is
+ * determined by {@link SmartReplyConstants}.
+ */
+ private boolean gotEnoughSmartReplies(List<View> smartReplies) {
+ int numShownReplies = 0;
+ for (View smartReplyButton : smartReplies) {
+ final LayoutParams lp = (LayoutParams) smartReplyButton.getLayoutParams();
+ if (lp.show) {
+ numShownReplies++;
+ }
+ }
+ if (numShownReplies == 0
+ || numShownReplies >= mConstants.getMinNumSystemGeneratedReplies()) {
+ // We have enough replies, yay!
+ return true;
+ }
+ return false;
}
private List<View> filterActionsOrReplies(SmartButtonType buttonType) {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index ed2ad79bdb50..12006fab2c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -75,7 +75,7 @@ public class UsbDebuggingActivity extends AlertActivity
final AlertController.AlertParams ap = mAlertParams;
ap.mTitle = getString(R.string.usb_debugging_title);
ap.mMessage = getString(R.string.usb_debugging_message, fingerprints);
- ap.mPositiveButtonText = getString(android.R.string.ok);
+ ap.mPositiveButtonText = getString(R.string.usb_debugging_allow);
ap.mNegativeButtonText = getString(android.R.string.cancel);
ap.mPositiveButtonListener = this;
ap.mNegativeButtonListener = this;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java b/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
index 712ea27982fb..8b00eee29c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
@@ -25,9 +25,9 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.media.IRemoteVolumeController;
import android.media.MediaMetadata;
-import android.media.session.ISessionController;
import android.media.session.MediaController;
import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.MediaSession;
import android.media.session.MediaSession.QueueItem;
import android.media.session.MediaSession.Token;
import android.media.session.MediaSessionManager;
@@ -113,17 +113,17 @@ public class MediaSessions {
r.controller.setVolumeTo(level, 0);
}
- private void onRemoteVolumeChangedH(ISessionController session, int flags) {
- final MediaController controller = new MediaController(mContext, session);
+ private void onRemoteVolumeChangedH(MediaSession.Token sessionToken, int flags) {
+ final MediaController controller = new MediaController(mContext, sessionToken);
if (D.BUG) Log.d(TAG, "remoteVolumeChangedH " + controller.getPackageName() + " "
+ Util.audioManagerFlagsToString(flags));
final Token token = controller.getSessionToken();
mCallbacks.onRemoteVolumeChanged(token, flags);
}
- private void onUpdateRemoteControllerH(ISessionController session) {
- final MediaController controller = session != null ? new MediaController(mContext, session)
- : null;
+ private void onUpdateRemoteControllerH(MediaSession.Token sessionToken) {
+ final MediaController controller =
+ sessionToken != null ? new MediaController(mContext, sessionToken) : null;
final String pkg = controller != null ? controller.getPackageName() : null;
if (D.BUG) Log.d(TAG, "updateRemoteControllerH " + pkg);
// this may be our only indication that a remote session is changed, refresh
@@ -332,15 +332,16 @@ public class MediaSessions {
private final IRemoteVolumeController mRvc = new IRemoteVolumeController.Stub() {
@Override
- public void remoteVolumeChanged(ISessionController session, int flags)
+ public void remoteVolumeChanged(MediaSession.Token sessionToken, int flags)
throws RemoteException {
- mHandler.obtainMessage(H.REMOTE_VOLUME_CHANGED, flags, 0, session).sendToTarget();
+ mHandler.obtainMessage(H.REMOTE_VOLUME_CHANGED, flags, 0,
+ sessionToken).sendToTarget();
}
@Override
- public void updateRemoteController(final ISessionController session)
+ public void updateRemoteController(final MediaSession.Token sessionToken)
throws RemoteException {
- mHandler.obtainMessage(H.UPDATE_REMOTE_CONTROLLER, session).sendToTarget();
+ mHandler.obtainMessage(H.UPDATE_REMOTE_CONTROLLER, sessionToken).sendToTarget();
}
};
@@ -360,10 +361,10 @@ public class MediaSessions {
onActiveSessionsUpdatedH(mMgr.getActiveSessions(null));
break;
case REMOTE_VOLUME_CHANGED:
- onRemoteVolumeChangedH((ISessionController) msg.obj, msg.arg1);
+ onRemoteVolumeChangedH((MediaSession.Token) msg.obj, msg.arg1);
break;
case UPDATE_REMOTE_CONTROLLER:
- onUpdateRemoteControllerH((ISessionController) msg.obj);
+ onUpdateRemoteControllerH((MediaSession.Token) msg.obj);
break;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index fbc1c20755a1..d80b444ad64b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -39,6 +39,7 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextClock;
+import com.android.keyguard.clock.ClockManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.statusbar.StatusBarState;
@@ -51,8 +52,6 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.function.Consumer;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
// Need to run on the main thread because KeyguardSliceView$Row init checks for
@@ -85,7 +84,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
TextClock pluginView = new TextClock(getContext());
when(plugin.getView()).thenReturn(pluginView);
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
verify(mClockView).setVisibility(GONE);
assertThat(plugin.getView().getParent()).isEqualTo(mClockContainer);
@@ -102,7 +101,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
TextClock pluginView = new TextClock(getContext());
when(plugin.getBigClockView()).thenReturn(pluginView);
// WHEN the plugin is connected
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
// THEN the big clock container is visible and it is the parent of the
// big clock view.
assertThat(bigClockContainer.getVisibility()).isEqualTo(VISIBLE);
@@ -112,7 +111,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
@Test
public void onPluginConnected_nullView() {
ClockPlugin plugin = mock(ClockPlugin.class);
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
verify(mClockView, never()).setVisibility(GONE);
}
@@ -121,11 +120,11 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
// GIVEN a plugin has already connected
ClockPlugin plugin1 = mock(ClockPlugin.class);
when(plugin1.getView()).thenReturn(new TextClock(getContext()));
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin1);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin1);
// WHEN a second plugin is connected
ClockPlugin plugin2 = mock(ClockPlugin.class);
when(plugin2.getView()).thenReturn(new TextClock(getContext()));
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin2);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin2);
// THEN only the view from the second plugin should be a child of KeyguardClockSwitch.
assertThat(plugin2.getView().getParent()).isEqualTo(mClockContainer);
assertThat(plugin1.getView().getParent()).isNull();
@@ -137,7 +136,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
mKeyguardClockSwitch.setDarkAmount(0.5f);
// WHEN a plugin is connected
ClockPlugin plugin = mock(ClockPlugin.class);
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
// THEN dark amount should be initalized on the plugin.
verify(plugin).setDarkAmount(0.5f);
}
@@ -149,8 +148,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
when(plugin.getView()).thenReturn(pluginView);
mClockView.setVisibility(GONE);
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
- mKeyguardClockSwitch.getClockPluginConsumer().accept(null);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(null);
verify(mClockView).setVisibility(VISIBLE);
assertThat(plugin.getView().getParent()).isNull();
@@ -167,8 +166,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
TextClock pluginView = new TextClock(getContext());
when(plugin.getBigClockView()).thenReturn(pluginView);
// WHEN the plugin is connected and then disconnected
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
- mKeyguardClockSwitch.getClockPluginConsumer().accept(null);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(null);
// THEN the big lock container is GONE and the big clock view doesn't have
// a parent.
assertThat(bigClockContainer.getVisibility()).isEqualTo(GONE);
@@ -178,8 +177,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
@Test
public void onPluginDisconnected_nullView() {
ClockPlugin plugin = mock(ClockPlugin.class);
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
- mKeyguardClockSwitch.getClockPluginConsumer().accept(null);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(null);
verify(mClockView, never()).setVisibility(GONE);
}
@@ -188,13 +187,13 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
// GIVEN two plugins are connected
ClockPlugin plugin1 = mock(ClockPlugin.class);
when(plugin1.getView()).thenReturn(new TextClock(getContext()));
- Consumer<ClockPlugin> consumer = mKeyguardClockSwitch.getClockPluginConsumer();
- consumer.accept(plugin1);
+ ClockManager.ClockChangedListener listener = mKeyguardClockSwitch.getClockChangedListener();
+ listener.onClockChanged(plugin1);
ClockPlugin plugin2 = mock(ClockPlugin.class);
when(plugin2.getView()).thenReturn(new TextClock(getContext()));
- consumer.accept(plugin2);
+ listener.onClockChanged(plugin2);
// WHEN the second plugin is disconnected
- consumer.accept(null);
+ listener.onClockChanged(null);
// THEN the default clock should be shown.
verify(mClockView).setVisibility(VISIBLE);
assertThat(plugin1.getView().getParent()).isNull();
@@ -213,7 +212,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
ClockPlugin plugin = mock(ClockPlugin.class);
TextClock pluginView = new TextClock(getContext());
when(plugin.getView()).thenReturn(pluginView);
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
mKeyguardClockSwitch.setTextColor(Color.WHITE);
@@ -237,7 +236,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
TextClock pluginView = new TextClock(getContext());
when(plugin.getView()).thenReturn(pluginView);
Style style = mock(Style.class);
- mKeyguardClockSwitch.getClockPluginConsumer().accept(plugin);
+ mKeyguardClockSwitch.getClockChangedListener().onClockChanged(plugin);
mKeyguardClockSwitch.setStyle(style);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 53ad0b5132c0..fc57909f42b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -68,7 +68,7 @@ public class ImageWallpaperTest extends SysuiTestCase {
public Engine onCreateEngine() {
return new DrawableEngine() {
@Override
- DisplayInfo getDefaultDisplayInfo() {
+ DisplayInfo getDisplayInfo() {
return mDisplayInfo;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index fa5cf04d56f4..60a20cf15cc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.bubbles;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.atLeastOnce;
@@ -160,6 +161,12 @@ public class BubbleControllerTest extends SysuiTestCase {
stackView.expandStack();
assertTrue(mBubbleController.isStackExpanded());
+ stackView.setExpandedBubble(mRow.getEntry());
+ assertEquals(stackView.getExpandedBubble().getEntry(), mRow.getEntry());
+
+ stackView.setExpandedBubble(mRow2.getEntry());
+ assertEquals(stackView.getExpandedBubble().getEntry(), mRow2.getEntry());
+
mBubbleController.collapseStack();
assertFalse(mBubbleController.isStackExpanded());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
index b368876d2ae7..839b5e4472c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java
@@ -32,6 +32,11 @@ public class DockManagerFake implements DockManager {
this.mCallback = null;
}
+ @Override
+ public boolean isDocked() {
+ return false;
+ }
+
public void setDockEvent(int event) {
mCallback.onEvent(event);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index f45500a87274..e4558df962e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -36,6 +36,8 @@ public class DozeConfigurationUtil {
when(params.getPickupVibrationThreshold()).thenReturn(0);
when(params.getProxCheckBeforePulse()).thenReturn(true);
when(params.getPickupSubtypePerformsProxCheck(anyInt())).thenReturn(true);
+ when(params.getPolicy()).thenReturn(mock(AlwaysOnDisplayPolicy.class));
+ when(params.doubleTapReportsTouchCoordinates()).thenReturn(false);
doneHolder[0] = true;
return params;
@@ -48,9 +50,14 @@ public class DozeConfigurationUtil {
when(config.doubleTapGestureEnabled(anyInt())).thenReturn(false);
when(config.pickupGestureEnabled(anyInt())).thenReturn(false);
when(config.pulseOnNotificationEnabled(anyInt())).thenReturn(true);
+ when(config.alwaysOnEnabled(anyInt())).thenReturn(false);
when(config.doubleTapSensorType()).thenReturn(null);
+ when(config.tapSensorType()).thenReturn(null);
+ when(config.longPressSensorType()).thenReturn(null);
+
when(config.dozePickupSensorAvailable()).thenReturn(false);
+ when(config.wakeScreenGestureAvailable()).thenReturn(false);
doneHolder[0] = true;
return config;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index 926ff69651be..0fc0953f333e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -75,7 +75,7 @@ public class DozeDockHandlerTest extends SysuiTestCase {
mContext.putComponent(DockManager.class, mDockManagerFake);
mDockHandler = new DozeDockHandler(mContext, mMachine, mHost, mConfig,
- Handler.createAsync(Looper.myLooper()));
+ Handler.createAsync(Looper.myLooper()), mDockManagerFake);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 31fc625d34dd..7b358b98b459 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -18,8 +18,10 @@ package com.android.systemui.doze;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -34,6 +36,8 @@ import android.testing.TestableLooper.RunWithLooper;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -59,6 +63,7 @@ public class DozeTriggersTest extends SysuiTestCase {
private WakeLock mWakeLock;
private Instrumentation mInstrumentation;
private AlarmManager mAlarmManager;
+ private DockManagerFake mDockManagerFake;
@BeforeClass
public static void setupSuite() {
@@ -76,9 +81,12 @@ public class DozeTriggersTest extends SysuiTestCase {
mParameters = DozeConfigurationUtil.createMockParameters();
mSensors = new FakeSensorManager(mContext);
mWakeLock = new WakeLockFake();
+ mDockManagerFake = spy(new DockManagerFake());
+ mContext.putComponent(DockManager.class, mDockManagerFake);
mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters,
- mSensors, Handler.createAsync(Looper.myLooper()), mWakeLock, true);
+ mSensors, Handler.createAsync(Looper.myLooper()), mWakeLock, true,
+ mDockManagerFake);
}
@Test
@@ -102,4 +110,38 @@ public class DozeTriggersTest extends SysuiTestCase {
verify(mMachine).requestPulse(anyInt());
}
+ @Test
+ public void testDockEventListener_registerAndUnregister() {
+ mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
+
+ verify(mDockManagerFake).addListener(any());
+
+ mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH);
+
+ verify(mDockManagerFake).removeListener(any());
+ }
+
+ @Test
+ public void testOnSensor_whenUndockedWithNearAndDoubleTapScreen_shouldNotWakeUp() {
+ mSensors.getMockProximitySensor().sendProximityResult(false /* far */);
+
+ mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP,
+ false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */,
+ null /* rawValues */);
+
+ verify(mMachine, never()).wakeUp();
+ }
+
+ @Test
+ public void testOnSensor_whenDockedWithNearAndDoubleTapScreen_shouldWakeUp() {
+ doReturn(true).when(mDockManagerFake).isDocked();
+ mSensors.getMockProximitySensor().sendProximityResult(false /* far */);
+
+ mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP,
+ false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */,
+ null /* rawValues */);
+
+ verify(mMachine).wakeUp();
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
index 4b03399a5e92..2f6b22117a10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.logging;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -27,6 +28,7 @@ import android.testing.TestableLooper;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.UiOffloadThread;
@@ -66,7 +68,7 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
waitForUiOffloadThread();
verify(mBarService, Mockito.never()).onNotificationExpansionChanged(
- eq(NOTIFICATION_KEY), anyBoolean(), anyBoolean());
+ eq(NOTIFICATION_KEY), anyBoolean(), anyBoolean(), anyInt());
}
@Test
@@ -75,7 +77,7 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
waitForUiOffloadThread();
verify(mBarService, Mockito.never()).onNotificationExpansionChanged(
- eq(NOTIFICATION_KEY), anyBoolean(), anyBoolean());
+ eq(NOTIFICATION_KEY), anyBoolean(), anyBoolean(), anyInt());
}
@Test
@@ -87,7 +89,7 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
waitForUiOffloadThread();
verify(mBarService, Mockito.never()).onNotificationExpansionChanged(
- eq(NOTIFICATION_KEY), anyBoolean(), anyBoolean());
+ eq(NOTIFICATION_KEY), anyBoolean(), anyBoolean(), anyInt());
}
@Test
@@ -99,7 +101,7 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
waitForUiOffloadThread();
verify(mBarService).onNotificationExpansionChanged(
- NOTIFICATION_KEY, true, true);
+ NOTIFICATION_KEY, true, true, ExpandableViewState.LOCATION_UNKNOWN);
}
@Test
@@ -111,7 +113,7 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
waitForUiOffloadThread();
verify(mBarService).onNotificationExpansionChanged(
- NOTIFICATION_KEY, false, true);
+ NOTIFICATION_KEY, false, true, ExpandableViewState.LOCATION_UNKNOWN);
}
@Test
@@ -123,7 +125,7 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
waitForUiOffloadThread();
verify(mBarService).onNotificationExpansionChanged(
- NOTIFICATION_KEY, false, true);
+ NOTIFICATION_KEY, false, true, ExpandableViewState.LOCATION_UNKNOWN);
}
@Test
@@ -136,7 +138,7 @@ public class ExpansionStateLoggerTest extends SysuiTestCase {
waitForUiOffloadThread();
verify(mBarService).onNotificationExpansionChanged(
- NOTIFICATION_KEY, false, true);
+ NOTIFICATION_KEY, false, true, ExpandableViewState.LOCATION_UNKNOWN);
}
private NotificationVisibility createNotificationVisibility(String key, boolean visibility) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
index 667a5082892d..4dee43857c65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
@@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone;
import static org.mockito.ArgumentMatchers.any;
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.spy;
import static org.mockito.Mockito.verify;
@@ -171,6 +170,19 @@ public class NearestTouchFrameTest extends SysuiTestCase {
ev.recycle();
}
+ @Test
+ public void testViewNotAttachedNoCrash() {
+ View view = mockViewAt(0, 20, 10, 10);
+ when(view.isAttachedToWindow()).thenReturn(false);
+ mNearestTouchFrame.addView(view);
+ mNearestTouchFrame.onMeasure(0, 0);
+
+ MotionEvent ev = MotionEvent.obtain(0, 0, 0, 5 /* x */, 18 /* y */, 0);
+ mNearestTouchFrame.onTouchEvent(ev);
+ verify(view, never()).onTouchEvent(eq(ev));
+ ev.recycle();
+ }
+
private View mockViewAt(int x, int y, int width, int height) {
View v = spy(new View(mContext));
doAnswer(invocation -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
index 98d0c6b4de16..9996a9eddc02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowControllerTest.java
@@ -95,4 +95,11 @@ public class StatusBarWindowControllerTest extends SysuiTestCase {
public void testAdd_updatesVisibilityFlags() {
verify(mStatusBarView).setSystemUiVisibility(anyInt());
}
+
+ @Test
+ public void testSetForcePluginOpen_beforeStatusBarInitialization() {
+ mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
+ mActivityManager, mDozeParameters);
+ mStatusBarWindowController.setForcePluginOpen(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index 3cbf902f1d40..03b7c9507dc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -55,6 +55,9 @@ public class SmartReplyConstantsTest extends SysuiTestCase {
resources.addOverride(
R.bool.config_smart_replies_in_notifications_edit_choices_before_sending, false);
resources.addOverride(R.bool.config_smart_replies_in_notifications_show_in_heads_up, true);
+ resources.addOverride(
+ R.integer.config_smart_replies_in_notifications_min_num_system_generated_replies,
+ 2);
mConstants = new SmartReplyConstants(Handler.createAsync(Looper.myLooper()), mContext);
}
@@ -178,6 +181,19 @@ public class SmartReplyConstantsTest extends SysuiTestCase {
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS, flags);
}
+ @Test
+ public void testGetMinNumSystemGeneratedRepliesWithNoConfig() {
+ assertTrue(mConstants.isEnabled());
+ assertEquals(2, mConstants.getMinNumSystemGeneratedReplies());
+ }
+
+ @Test
+ public void testGetMinNumSystemGeneratedRepliesWithValidConfig() {
+ overrideSetting("enabled=true,min_num_system_generated_replies=5");
+ triggerConstantsOnChange();
+ assertEquals(5, mConstants.getMinNumSystemGeneratedReplies());
+ }
+
private void triggerConstantsOnChange() {
// Since Settings.Global is mocked in TestableContext, we need to manually trigger the
// content observer.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 1066bc1a04df..d1c4d0134f6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -96,6 +96,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
@Mock private SmartReplyController mLogger;
private NotificationEntry mEntry;
private Notification mNotification;
+ @Mock private SmartReplyConstants mConstants;
@Mock ActivityStarter mActivityStarter;
@Mock HeadsUpManager mHeadsUpManager;
@@ -108,10 +109,14 @@ public class SmartReplyViewTest extends SysuiTestCase {
mDependency.get(KeyguardDismissUtil.class).setDismissHandler(action -> action.onDismiss());
mDependency.injectMockDependency(ShadeController.class);
mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter);
+ mDependency.injectTestDependency(SmartReplyConstants.class, mConstants);
mContainer = new View(mContext, null);
mView = SmartReplyView.inflate(mContext, null);
+ // Any number of replies are fine.
+ when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(0);
+ when(mConstants.getMaxSqueezeRemeasureAttempts()).thenReturn(3);
final Resources res = mContext.getResources();
mSingleLinePaddingHorizontal = res.getDimensionPixelSize(
@@ -403,7 +408,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
}
private void setSmartReplies(CharSequence[] choices) {
- setSmartReplies(choices, false);
+ setSmartReplies(choices, false /* fromAssistant */);
}
private void setSmartReplies(CharSequence[] choices, boolean fromAssistant) {
@@ -440,9 +445,14 @@ public class SmartReplyViewTest extends SysuiTestCase {
}
private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
- setSmartReplies(choices);
+ setSmartRepliesAndActions(choices, actionTitles, false /* fromAssistant */);
+ }
+
+ private void setSmartRepliesAndActions(
+ CharSequence[] choices, String[] actionTitles, boolean fromAssistant) {
+ setSmartReplies(choices, fromAssistant);
mView.addSmartActions(
- new SmartReplyView.SmartActions(createActions(actionTitles), false),
+ new SmartReplyView.SmartActions(createActions(actionTitles), fromAssistant),
mLogger,
mEntry,
mHeadsUpManager);
@@ -943,4 +953,78 @@ public class SmartReplyViewTest extends SysuiTestCase {
expectedView.getChildAt(3), mView.getChildAt(4)); // a1
assertReplyButtonHidden(mView.getChildAt(5)); // long action
}
+
+ @Test
+ public void testMeasure_minNumSystemGeneratedSmartReplies_notEnoughReplies() {
+ when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(3);
+
+ // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
+ String[] choices = new String[] {"reply1", "reply2"};
+ String[] actions = new String[] {"action1"};
+
+ ViewGroup expectedView = buildExpectedView(new String[] {}, 1,
+ createActions(new String[] {"action1"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions, true /* fromAssistant */);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ // smart replies
+ assertReplyButtonHidden(mView.getChildAt(0));
+ assertReplyButtonHidden(mView.getChildAt(1));
+ // smart actions
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(2));
+ }
+
+ @Test
+ public void testMeasure_minNumSystemGeneratedSmartReplies_enoughReplies() {
+ when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(2);
+
+ // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
+ String[] choices = new String[] {"reply1", "reply2"};
+ String[] actions = new String[] {"action1"};
+
+ ViewGroup expectedView = buildExpectedView(new String[] {"reply1", "reply2"}, 1,
+ createActions(new String[] {"action1"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions, true /* fromAssistant */);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ // smart replies
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ // smart actions
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
+ }
+
+ /**
+ * Ensure actions that are squeezed when shown together with smart replies are unsqueezed if the
+ * replies are never added (because of the SmartReplyConstants.getMinNumSystemGeneratedReplies()
+ * flag).
+ */
+ @Test
+ public void testMeasure_minNumSystemGeneratedSmartReplies_unSqueezeActions() {
+ when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(2);
+
+ // Add 2 replies when the minimum is 3 -> we should end up with 0 replies.
+ String[] choices = new String[] {"This is a very long two-line reply."};
+ String[] actions = new String[] {"Short action"};
+
+ // The action should be displayed on one line only - since it fits!
+ ViewGroup expectedView = buildExpectedView(new String[] {}, 1 /* lineCount */,
+ createActions(new String[] {"Short action"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions, true /* fromAssistant */);
+ mView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ // smart replies
+ assertReplyButtonHidden(mView.getChildAt(0));
+ // smart actions
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
+ }
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 8261fe89f778..efa4e79cc318 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -73,6 +73,10 @@ message MetricsEvent {
// The view switched to summary mode (most relevant for notifications)
TYPE_COLLAPSE = 14;
+
+ // The notification was adjusted by the assistant. Enum value is
+ // out of sequence due to b/122737498.
+ TYPE_NOTIFICATION_ASSISTANT_ADJUSTMENT = 1573;
}
// Types of alerts, as bit field values
@@ -232,6 +236,17 @@ message MetricsEvent {
BLOCKING_HELPER_CLICK_UNDO = 7;
}
+ // The (visual) location of a Notification.
+ enum NotificationLocation {
+ LOCATION_UNKNOWN = 0;
+ LOCATION_FIRST_HEADS_UP = 1; // visible heads-up
+ LOCATION_HIDDEN_TOP = 2; // hidden/scrolled away on the top
+ LOCATION_MAIN_AREA = 3; // visible in the shade
+ LOCATION_BOTTOM_STACK_PEEKING = 4; // in the bottom stack, and peeking
+ LOCATION_BOTTOM_STACK_HIDDEN = 5; // in the bottom stack, and hidden
+ LOCATION_GONE = 6; // the view isn't laid out at all
+ }
+
// Known visual elements: views or controls.
enum View {
// Unknown view
@@ -4049,6 +4064,8 @@ message MetricsEvent {
// - AUTOFILL_INVALID_DATASET_AUTHENTICATION
// NOTE: starting on OS Q, it also added the following fields:
// Tag FIELD_AUTOFILL_TEXT_LEN: length of the error message provided by the service
+ // Tag FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS: number of requests made to the augmented
+ // autofill service
AUTOFILL_REQUEST = 907;
// Tag of a field for a package of an autofill service
@@ -6820,6 +6837,31 @@ message MetricsEvent {
// OS: Q
MOBILE_NETWORK_LIST = 1627;
+ // OPEN: Settings > Display > Adaptive sleep
+ // OS: Q
+ SETTINGS_ADAPTIVE_SLEEP = 1628;
+
+ // Tagged data for SMART_REPLY_VISIBLE and NOTIFICATION_ITEM_ACTION.
+ // The UI location of the notification containing the smart suggestions.
+ // This is a NotificationLocation object (see the NotificationLocation
+ // enum).
+ // OS: Q
+ NOTIFICATION_LOCATION = 1629;
+
+ // The autofill system made request to the system-provided augmented autofill service.
+ // OS: Q
+ // Package: Package of app that is autofilled
+ // Tag FIELD_CLASS_NAME: Class name of the activity that is autofilled.
+ // Tag FIELD_AUTOFILL_SERVICE: Package of the augmented autofill service that processed the
+ // request
+ // Tag FIELD_AUTOFILL_SESSION_ID: id of the autofill session associated with this metric.
+ // Tag FIELD_AUTOFILL_COMPAT_MODE: package is being autofilled on compatibility mode.
+ AUTOFILL_AUGMENTED_REQUEST = 1630;
+
+ // Tag of a field for the number of augmented autofill requests in a session
+ // OS: Q
+ FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS = 1631;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 121267675329..6ff2b35d49a0 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -216,9 +216,9 @@ message SystemMessage {
// Package: android
NOTE_SOFTAP_CONFIG_CHANGED = 50;
- // Notify the user that connected to app suggested network.
+ // Notify the user that an app suggested network is available for connection.
// Package: android
- NOTE_CONNECTED_TO_NETWORK_SUGGESTION = 51;
+ NOTE_NETWORK_SUGGESTION_AVAILABLE = 51;
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index bcc43a763599..79b63bc55102 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -488,6 +488,9 @@ message WifiLog {
// Counts the occurrences of each Wifi usability score provided by external app
repeated WifiUsabilityScoreCount wifi_usability_score_count = 127;
+
+ // List of PNO scan stats, one element for each mobility state
+ repeated DeviceMobilityStatePnoScanStats mobility_state_pno_stats_list = 128;
}
// Information that gets logged for every WiFi connection.
@@ -1816,4 +1819,33 @@ message WifiUsabilityStats {
// The list of timestamped wifi usability stats
repeated WifiUsabilityStatsEntry stats = 2;
-} \ No newline at end of file
+}
+
+message DeviceMobilityStatePnoScanStats {
+ // see WifiManager.DEVICE_MOBILITY_STATE_* constants
+ enum DeviceMobilityState {
+ // Unknown mobility
+ UNKNOWN = 0;
+
+ // High movement
+ HIGH_MVMT = 1;
+
+ // Low movement
+ LOW_MVMT = 2;
+
+ // Stationary
+ STATIONARY = 3;
+ }
+
+ // The device mobility state
+ optional DeviceMobilityState device_mobility_state = 1;
+
+ // The number of times that this state was entered
+ optional int32 num_times_entered_state = 2;
+
+ // The total duration elapsed while in this mobility state, in ms
+ optional int64 total_duration_ms = 3;
+
+ // the total duration elapsed while in this mobility state with PNO scans running, in ms
+ optional int64 pno_duration_ms = 4;
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 6eba914f15bc..2c075dc809d0 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -53,6 +53,7 @@ import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
import com.android.server.accessibility.AccessibilityManagerService.RemoteAccessibilityConnection;
@@ -751,7 +752,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
@Override
- public float getMagnificationScale() {
+ public float getMagnificationScale(int displayId) {
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
return 1.0f;
@@ -759,14 +760,14 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
final long identity = Binder.clearCallingIdentity();
try {
- return mSystemSupport.getMagnificationController().getScale();
+ return mSystemSupport.getMagnificationController().getScale(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public Region getMagnificationRegion() {
+ public Region getMagnificationRegion(int displayId) {
synchronized (mLock) {
final Region region = Region.obtain();
if (!isCalledForCurrentUserLocked()) {
@@ -775,22 +776,22 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
MagnificationController magnificationController =
mSystemSupport.getMagnificationController();
boolean registeredJustForThisCall =
- registerMagnificationIfNeeded(magnificationController);
+ registerMagnificationIfNeeded(displayId, magnificationController);
final long identity = Binder.clearCallingIdentity();
try {
- magnificationController.getMagnificationRegion(region);
+ magnificationController.getMagnificationRegion(displayId, region);
return region;
} finally {
Binder.restoreCallingIdentity(identity);
if (registeredJustForThisCall) {
- magnificationController.unregister();
+ magnificationController.unregister(displayId);
}
}
}
}
@Override
- public float getMagnificationCenterX() {
+ public float getMagnificationCenterX(int displayId) {
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
return 0.0f;
@@ -798,21 +799,21 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
MagnificationController magnificationController =
mSystemSupport.getMagnificationController();
boolean registeredJustForThisCall =
- registerMagnificationIfNeeded(magnificationController);
+ registerMagnificationIfNeeded(displayId, magnificationController);
final long identity = Binder.clearCallingIdentity();
try {
- return magnificationController.getCenterX();
+ return magnificationController.getCenterX(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
if (registeredJustForThisCall) {
- magnificationController.unregister();
+ magnificationController.unregister(displayId);
}
}
}
}
@Override
- public float getMagnificationCenterY() {
+ public float getMagnificationCenterY(int displayId) {
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
return 0.0f;
@@ -820,31 +821,31 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
MagnificationController magnificationController =
mSystemSupport.getMagnificationController();
boolean registeredJustForThisCall =
- registerMagnificationIfNeeded(magnificationController);
+ registerMagnificationIfNeeded(displayId, magnificationController);
final long identity = Binder.clearCallingIdentity();
try {
- return magnificationController.getCenterY();
+ return magnificationController.getCenterY(displayId);
} finally {
Binder.restoreCallingIdentity(identity);
if (registeredJustForThisCall) {
- magnificationController.unregister();
+ magnificationController.unregister(displayId);
}
}
}
}
- private boolean registerMagnificationIfNeeded(
+ private boolean registerMagnificationIfNeeded(int displayId,
MagnificationController magnificationController) {
- if (!magnificationController.isRegisteredLocked()
+ if (!magnificationController.isRegistered(displayId)
&& mSecurityPolicy.canControlMagnification(this)) {
- magnificationController.register();
+ magnificationController.register(displayId);
return true;
}
return false;
}
@Override
- public boolean resetMagnification(boolean animate) {
+ public boolean resetMagnification(int displayId, boolean animate) {
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
return false;
@@ -857,16 +858,16 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
try {
MagnificationController magnificationController =
mSystemSupport.getMagnificationController();
- return (magnificationController.reset(animate)
- || !magnificationController.isMagnifying());
+ return (magnificationController.reset(displayId, animate)
+ || !magnificationController.isMagnifying(displayId));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
- public boolean setMagnificationScaleAndCenter(float scale, float centerX, float centerY,
- boolean animate) {
+ public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
+ float centerY, boolean animate) {
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
return false;
@@ -878,11 +879,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
try {
MagnificationController magnificationController =
mSystemSupport.getMagnificationController();
- if (!magnificationController.isRegisteredLocked()) {
- magnificationController.register();
+ if (!magnificationController.isRegistered(displayId)) {
+ magnificationController.register(displayId);
}
return magnificationController
- .setScaleAndCenter(scale, centerX, centerY, animate, mId);
+ .setScaleAndCenter(displayId, scale, centerX, centerY, animate, mId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -890,12 +891,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
@Override
- public void setMagnificationCallbackEnabled(boolean enabled) {
- mInvocationHandler.setMagnificationCallbackEnabled(enabled);
+ public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
+ mInvocationHandler.setMagnificationCallbackEnabled(displayId, enabled);
}
- public boolean isMagnificationCallbackEnabled() {
- return mInvocationHandler.mIsMagnificationCallbackEnabled;
+ public boolean isMagnificationCallbackEnabled(int displayId) {
+ return mInvocationHandler.isMagnificationCallbackEnabled(displayId);
}
@Override
@@ -1106,10 +1107,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
}
- public void notifyMagnificationChangedLocked(@NonNull Region region,
+ public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
float scale, float centerX, float centerY) {
mInvocationHandler
- .notifyMagnificationChangedLocked(region, scale, centerX, centerY);
+ .notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
}
public void notifySoftKeyboardShowModeChangedLocked(int showState) {
@@ -1128,12 +1129,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
* Called by the invocation handler to notify the service that the
* state of magnification has changed.
*/
- private void notifyMagnificationChangedInternal(@NonNull Region region,
+ private void notifyMagnificationChangedInternal(int displayId, @NonNull Region region,
float scale, float centerX, float centerY) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
- listener.onMagnificationChanged(region, scale, centerX, centerY);
+ listener.onMagnificationChanged(displayId, region, scale, centerX, centerY);
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Error sending magnification changes to " + mService, re);
}
@@ -1251,7 +1252,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
private static final int MSG_ON_ACCESSIBILITY_BUTTON_CLICKED = 7;
private static final int MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED = 8;
- private boolean mIsMagnificationCallbackEnabled = false;
+ /** List of magnification callback states, mapping from displayId -> Boolean */
+ @GuardedBy("mlock")
+ private final SparseArray<Boolean> mMagnificationCallbackState = new SparseArray<>(0);
private boolean mIsSoftKeyboardCallbackEnabled = false;
public InvocationHandler(Looper looper) {
@@ -1277,7 +1280,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final float scale = (float) args.arg2;
final float centerX = (float) args.arg3;
final float centerY = (float) args.arg4;
- notifyMagnificationChangedInternal(region, scale, centerX, centerY);
+ final int displayId = args.argi1;
+ notifyMagnificationChangedInternal(displayId, region, scale, centerX, centerY);
args.recycle();
} break;
@@ -1301,11 +1305,12 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- public void notifyMagnificationChangedLocked(@NonNull Region region, float scale,
- float centerX, float centerY) {
- if (!mIsMagnificationCallbackEnabled) {
- // Callback is disabled, don't bother packing args.
- return;
+ public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
+ float scale, float centerX, float centerY) {
+ synchronized (mLock) {
+ if (mMagnificationCallbackState.get(displayId) == null) {
+ return;
+ }
}
final SomeArgs args = SomeArgs.obtain();
@@ -1313,13 +1318,26 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
args.arg2 = scale;
args.arg3 = centerX;
args.arg4 = centerY;
+ args.argi1 = displayId;
final Message msg = obtainMessage(MSG_ON_MAGNIFICATION_CHANGED, args);
msg.sendToTarget();
}
- public void setMagnificationCallbackEnabled(boolean enabled) {
- mIsMagnificationCallbackEnabled = enabled;
+ public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {
+ synchronized (mLock) {
+ if (enabled) {
+ mMagnificationCallbackState.put(displayId, true);
+ } else {
+ mMagnificationCallbackState.remove(displayId);
+ }
+ }
+ }
+
+ public boolean isMagnificationCallbackEnabled(int displayId) {
+ synchronized (mLock) {
+ return mMagnificationCallbackState.get(displayId) != null;
+ }
}
public void notifySoftKeyboardShowModeChangedLocked(int showState) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 763c16f771d0..dbe86c15c7a4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -208,6 +208,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final WindowManagerInternal mWindowManagerService;
+ private final DisplayManager mDisplayManager;
+
private AppWidgetManagerInternal mAppWidgetService;
private final SecurityPolicy mSecurityPolicy;
@@ -304,10 +306,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mMainHandler = new MainHandler(mContext.getMainLooper());
mGlobalActionPerformer = new GlobalActionPerformer(mContext, mWindowManagerService);
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
registerBroadcastReceivers();
new AccessibilityContentObserver(mMainHandler).register(
context.getContentResolver());
+ registerDisplayListener(mMainHandler);
}
@Override
@@ -523,6 +527,30 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}, UserHandle.ALL, intentFilter, null, null);
}
+ private void registerDisplayListener(Handler handler) {
+ mDisplayManager.registerDisplayListener(new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ synchronized (mLock) {
+ UserState userState = getCurrentUserStateLocked();
+ updateMagnificationLocked(userState);
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (mMagnificationController != null) {
+ mMagnificationController.onDisplayRemoved(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ // do nothing
+ }
+ }, handler);
+ }
+
@Override
public long addClient(IAccessibilityManagerClient callback, int userId) {
synchronized (mLock) {
@@ -968,17 +996,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* Called by the MagnificationController when the state of display
* magnification changes.
*
+ * @param displayId The logical display id.
* @param region the new magnified region, may be empty if
* magnification is not enabled (e.g. scale is 1)
* @param scale the new scale
* @param centerX the new screen-relative center X coordinate
* @param centerY the new screen-relative center Y coordinate
*/
- public void notifyMagnificationChanged(@NonNull Region region,
+ public void notifyMagnificationChanged(int displayId, @NonNull Region region,
float scale, float centerX, float centerY) {
synchronized (mLock) {
notifyClearAccessibilityCacheLocked();
- notifyMagnificationChangedLocked(region, scale, centerX, centerY);
+ notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
}
}
@@ -1203,12 +1232,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private void notifyMagnificationChangedLocked(@NonNull Region region,
+ private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
float scale, float centerX, float centerY) {
final UserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
- service.notifyMagnificationChangedLocked(region, scale, centerX, centerY);
+ service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
}
}
@@ -2191,15 +2220,44 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return;
}
- if (!mUiAutomationManager.suppressingAccessibilityServicesLocked()
- && (userState.mIsDisplayMagnificationEnabled
- || userState.mIsNavBarMagnificationEnabled
- || userHasListeningMagnificationServicesLocked(userState))) {
- // Initialize the magnification controller if necessary
- getMagnificationController();
- mMagnificationController.register();
- } else if (mMagnificationController != null) {
- mMagnificationController.unregister();
+ if (mUiAutomationManager.suppressingAccessibilityServicesLocked()
+ && mMagnificationController != null) {
+ mMagnificationController.unregisterAll();
+ return;
+ }
+
+ // register all display if global magnification is enabled.
+ final Display[] displays = mDisplayManager.getDisplays();
+ if (userState.mIsDisplayMagnificationEnabled
+ || userState.mIsNavBarMagnificationEnabled) {
+ for (int i = 0; i < displays.length; i++) {
+ final Display display = displays[i];
+ // Overlay display uses overlay window to simulate secondary displays in
+ // one display. It's not a real display and there's no input events for it.
+ // We should ignore it.
+ if (display.getType() == Display.TYPE_OVERLAY) {
+ continue;
+ }
+ getMagnificationController().register(display.getDisplayId());
+ }
+ return;
+ }
+
+ // register if display has listening magnification services.
+ for (int i = 0; i < displays.length; i++) {
+ final Display display = displays[i];
+ // Overlay display uses overlay window to simulate secondary displays in
+ // one display. It's not a real display and there's no input events for it.
+ // We should ignore it.
+ if (display.getType() == Display.TYPE_OVERLAY) {
+ continue;
+ }
+ final int displayId = display.getDisplayId();
+ if (userHasListeningMagnificationServicesLocked(userState, displayId)) {
+ getMagnificationController().register(displayId);
+ } else if (mMagnificationController != null) {
+ mMagnificationController.unregister(displayId);
+ }
}
}
@@ -2222,12 +2280,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
* Returns whether the specified user has any services that are capable of
* controlling magnification and are actively listening for magnification updates.
*/
- private boolean userHasListeningMagnificationServicesLocked(UserState userState) {
+ private boolean userHasListeningMagnificationServicesLocked(UserState userState,
+ int displayId) {
final List<AccessibilityServiceConnection> services = userState.mBoundServices;
for (int i = 0, count = services.size(); i < count; i++) {
final AccessibilityServiceConnection service = services.get(i);
if (mSecurityPolicy.canControlMagnification(service)
- && service.isMagnificationCallbackEnabled()) {
+ && service.isMagnificationCallbackEnabled(displayId)) {
return true;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 86132a8e6473..a19a84724d45 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -109,7 +109,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
UserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.removeServiceLocked(this);
- mSystemSupport.getMagnificationController().resetIfNeeded(mId);
+ mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
resetLocked();
}
@@ -256,7 +256,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
userState.serviceDisconnectedLocked(this);
}
resetLocked();
- mSystemSupport.getMagnificationController().resetIfNeeded(mId);
+ mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
mSystemSupport.onClientChangeLocked(false);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index 6a97fbbf30da..e784056d9e35 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -19,7 +19,6 @@ package com.android.server.accessibility;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -32,6 +31,7 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.util.MathUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.Display;
import android.view.MagnificationSpec;
import android.view.View;
@@ -39,6 +39,7 @@ import android.view.animation.DecelerateInterpolator;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.wm.WindowManagerInternal;
@@ -68,9 +69,7 @@ public class MagnificationController {
private final Object mLock;
- private final AccessibilityManagerService mAms;
-
- private final SettingsBridge mSettingsBridge;
+ private final ControllerContext mControllerCtx;
private final ScreenStateObserver mScreenStateObserver;
@@ -78,11 +77,9 @@ public class MagnificationController {
private final long mMainThreadId;
- private Handler mHandler;
-
- private final WindowManagerInternal mWindowManager;
-
- private final DisplayMagnification mDisplay;
+ /** List of display Magnification, mapping from displayId -> DisplayMagnification. */
+ @GuardedBy("mLock")
+ private final SparseArray<DisplayMagnification> mDisplays = new SparseArray<>(0);
/**
* This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds
@@ -107,46 +104,82 @@ public class MagnificationController {
// Flag indicating that we are registered with window manager.
private boolean mRegistered;
private boolean mUnregisterPending;
+ private boolean mDeleteAfterUnregister;
private final int mDisplayId;
private static final int INVALID_ID = -1;
private int mIdOfLastServiceToMagnify = INVALID_ID;
-
- DisplayMagnification(int displayId, SpecAnimationBridge specAnimation) {
+ DisplayMagnification(int displayId) {
mDisplayId = displayId;
- mSpecAnimationBridge = specAnimation;
+ mSpecAnimationBridge = new SpecAnimationBridge(mControllerCtx, mLock, mDisplayId);
}
- void register() {
- synchronized (mLock) {
- if (!mRegistered) {
- mWindowManager.setMagnificationCallbacks(this);
- mSpecAnimationBridge.setEnabled(true);
- // Obtain initial state.
- mWindowManager.getMagnificationRegion(mMagnificationRegion);
- mMagnificationRegion.getBounds(mMagnificationBounds);
- mRegistered = true;
- }
+ /**
+ * Registers magnification callback and get current magnification region from
+ * window manager.
+ *
+ * @return true if callback registers successful.
+ */
+ @GuardedBy("mLock")
+ boolean register() {
+ mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks(
+ mDisplayId, this);
+ if (!mRegistered) {
+ Slog.w(LOG_TAG, "set magnification callbacks fail, displayId:" + mDisplayId);
+ return false;
}
+ mSpecAnimationBridge.setEnabled(true);
+ // Obtain initial state.
+ mControllerCtx.getWindowManager().getMagnificationRegion(
+ mDisplayId, mMagnificationRegion);
+ mMagnificationRegion.getBounds(mMagnificationBounds);
+ return true;
}
- void unregister() {
- synchronized (mLock) {
- if (!isMagnifying()) {
- unregisterInternalLocked();
- } else {
- mUnregisterPending = true;
- reset(true);
- }
+ /**
+ * Unregisters magnification callback from window manager. Callbacks to
+ * {@link MagnificationController#unregisterCallbackLocked(int, boolean)} after
+ * unregistered.
+ *
+ * @param delete true if this instance should be removed from the SparseArray in
+ * MagnificationController after unregistered, for example, display removed.
+ */
+ @GuardedBy("mLock")
+ void unregister(boolean delete) {
+ if (mRegistered) {
+ mSpecAnimationBridge.setEnabled(false);
+ mControllerCtx.getWindowManager().setMagnificationCallbacks(
+ mDisplayId, null);
+ mMagnificationRegion.setEmpty();
+ mRegistered = false;
+ unregisterCallbackLocked(mDisplayId, delete);
}
+ mUnregisterPending = false;
}
- boolean isRegisteredLocked() {
+ /**
+ * Reset magnification status with animation enabled. {@link #unregister(boolean)} will be
+ * called after animation finished.
+ *
+ * @param delete true if this instance should be removed from the SparseArray in
+ * MagnificationController after unregistered, for example, display removed.
+ */
+ @GuardedBy("mLock")
+ void unregisterPending(boolean delete) {
+ mDeleteAfterUnregister = delete;
+ mUnregisterPending = true;
+ reset(true);
+ }
+
+ boolean isRegistered() {
return mRegistered;
}
+ boolean isMagnifying() {
+ return mCurrentMagnificationSpec.scale > 1.0f;
+ }
float getScale() {
return mCurrentMagnificationSpec.scale;
@@ -156,18 +189,20 @@ public class MagnificationController {
return mCurrentMagnificationSpec.offsetX;
}
+ float getOffsetY() {
+ return mCurrentMagnificationSpec.offsetY;
+ }
+
+ @GuardedBy("mLock")
float getCenterX() {
- synchronized (mLock) {
- return (mMagnificationBounds.width() / 2.0f
- + mMagnificationBounds.left - getOffsetX()) / getScale();
- }
+ return (mMagnificationBounds.width() / 2.0f
+ + mMagnificationBounds.left - getOffsetX()) / getScale();
}
+ @GuardedBy("mLock")
float getCenterY() {
- synchronized (mLock) {
- return (mMagnificationBounds.height() / 2.0f
- + mMagnificationBounds.top - getOffsetY()) / getScale();
- }
+ return (mMagnificationBounds.height() / 2.0f
+ + mMagnificationBounds.top - getOffsetY()) / getScale();
}
/**
@@ -203,64 +238,35 @@ public class MagnificationController {
return mSpecAnimationBridge.mSentMagnificationSpec.offsetY;
}
- boolean resetIfNeeded(boolean animate) {
- synchronized (mLock) {
- if (isMagnifying()) {
- reset(animate);
- return true;
- }
- return false;
- }
- }
-
- float getOffsetY() {
- return mCurrentMagnificationSpec.offsetY;
- }
-
- boolean isMagnifying() {
- return mCurrentMagnificationSpec.scale > 1.0f;
- }
-
- void unregisterInternalLocked() {
- if (mRegistered) {
- mSpecAnimationBridge.setEnabled(false);
- mWindowManager.setMagnificationCallbacks(null);
- mMagnificationRegion.setEmpty();
-
- mRegistered = false;
- }
- mUnregisterPending = false;
- }
-
-
@Override
public void onMagnificationRegionChanged(Region magnificationRegion) {
final Message m = PooledLambda.obtainMessage(
- DisplayMagnification.this::updateMagnificationRegion,
+ DisplayMagnification::updateMagnificationRegion, this,
Region.obtain(magnificationRegion));
- mHandler.sendMessage(m);
+ mControllerCtx.getHandler().sendMessage(m);
}
@Override
public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) {
final Message m = PooledLambda.obtainMessage(
- DisplayMagnification.this::requestRectangleOnScreen, left, top, right, bottom);
- mHandler.sendMessage(m);
+ DisplayMagnification::requestRectangleOnScreen, this,
+ left, top, right, bottom);
+ mControllerCtx.getHandler().sendMessage(m);
}
@Override
public void onRotationChanged(int rotation) {
// Treat as context change and reset
- final Message m = PooledLambda.obtainMessage(DisplayMagnification.this::resetIfNeeded,
- true);
- mHandler.sendMessage(m);
+ final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded,
+ MagnificationController.this, mDisplayId, true);
+ mControllerCtx.getHandler().sendMessage(m);
}
@Override
public void onUserContextChanged() {
- final Message m = PooledLambda.obtainMessage(DisplayMagnification.this::resetIfNeeded,
- true);
- mHandler.sendMessage(m);
+ final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded,
+ MagnificationController.this, mDisplayId, true);
+ mControllerCtx.getHandler().sendMessage(m);
}
/**
@@ -298,8 +304,9 @@ public class MagnificationController {
mSpecAnimationBridge.updateSentSpecMainThread(spec, animate);
} else {
final Message m = PooledLambda.obtainMessage(
- this.mSpecAnimationBridge::updateSentSpecMainThread, spec, animate);
- mHandler.sendMessage(m);
+ SpecAnimationBridge::updateSentSpecMainThread,
+ mSpecAnimationBridge, spec, animate);
+ mControllerCtx.getHandler().sendMessage(m);
}
}
@@ -313,30 +320,26 @@ public class MagnificationController {
}
void onMagnificationChangedLocked() {
- mAms.notifyMagnificationChanged(mMagnificationRegion,
+ mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId, mMagnificationRegion,
getScale(), getCenterX(), getCenterY());
if (mUnregisterPending && !isMagnifying()) {
- unregisterInternalLocked();
+ unregister(mDeleteAfterUnregister);
}
}
+ @GuardedBy("mLock")
boolean magnificationRegionContains(float x, float y) {
- synchronized (mLock) {
- return mMagnificationRegion.contains((int) x, (int) y);
-
- }
+ return mMagnificationRegion.contains((int) x, (int) y);
}
+ @GuardedBy("mLock")
void getMagnificationBounds(@NonNull Rect outBounds) {
- synchronized (mLock) {
- outBounds.set(mMagnificationBounds);
- }
+ outBounds.set(mMagnificationBounds);
}
+ @GuardedBy("mLock")
void getMagnificationRegion(@NonNull Region outRegion) {
- synchronized (mLock) {
- outRegion.set(mMagnificationRegion);
- }
+ outRegion.set(mMagnificationRegion);
}
void requestRectangleOnScreen(int left, int top, int right, int bottom) {
@@ -392,94 +395,76 @@ public class MagnificationController {
outFrame.scale(1.0f / scale);
}
- /**
- * Resets magnification if last magnifying service is disabled.
- *
- * @param connectionId the connection ID be disabled.
- * @return {@code true} on success, {@code false} on failure
- */
- boolean resetIfNeeded(int connectionId) {
- if (mIdOfLastServiceToMagnify == connectionId) {
- return resetIfNeeded(true /*animate*/);
- }
- return false;
- }
-
+ @GuardedBy("mLock")
void setForceShowMagnifiableBounds(boolean show) {
if (mRegistered) {
- mWindowManager.setForceShowMagnifiableBounds(show);
+ mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
+ mDisplayId, show);
}
}
+ @GuardedBy("mLock")
boolean reset(boolean animate) {
- synchronized (mLock) {
- if (!mRegistered) {
- return false;
- }
- final MagnificationSpec spec = mCurrentMagnificationSpec;
- final boolean changed = !spec.isNop();
- if (changed) {
- spec.clear();
- onMagnificationChangedLocked();
- }
- mIdOfLastServiceToMagnify = INVALID_ID;
- sendSpecToAnimation(spec, animate);
- return changed;
+ if (!mRegistered) {
+ return false;
}
+ final MagnificationSpec spec = mCurrentMagnificationSpec;
+ final boolean changed = !spec.isNop();
+ if (changed) {
+ spec.clear();
+ onMagnificationChangedLocked();
+ }
+ mIdOfLastServiceToMagnify = INVALID_ID;
+ sendSpecToAnimation(spec, animate);
+ return changed;
}
-
+ @GuardedBy("mLock")
boolean setScale(float scale, float pivotX, float pivotY,
boolean animate, int id) {
-
- synchronized (mLock) {
- if (!mRegistered) {
- return false;
- }
- // Constrain scale immediately for use in the pivot calculations.
- scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
-
- final Rect viewport = mTempRect;
- mMagnificationRegion.getBounds(viewport);
- final MagnificationSpec spec = mCurrentMagnificationSpec;
- final float oldScale = spec.scale;
- final float oldCenterX
- = (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale;
- final float oldCenterY
- = (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale;
- final float normPivotX = (pivotX - spec.offsetX) / oldScale;
- final float normPivotY = (pivotY - spec.offsetY) / oldScale;
- final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
- final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
- final float centerX = normPivotX + offsetX;
- final float centerY = normPivotY + offsetY;
- mIdOfLastServiceToMagnify = id;
-
- return setScaleAndCenter(scale, centerX, centerY, animate, id);
+ if (!mRegistered) {
+ return false;
}
+ // Constrain scale immediately for use in the pivot calculations.
+ scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+
+ final Rect viewport = mTempRect;
+ mMagnificationRegion.getBounds(viewport);
+ final MagnificationSpec spec = mCurrentMagnificationSpec;
+ final float oldScale = spec.scale;
+ final float oldCenterX =
+ (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale;
+ final float oldCenterY =
+ (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale;
+ final float normPivotX = (pivotX - spec.offsetX) / oldScale;
+ final float normPivotY = (pivotY - spec.offsetY) / oldScale;
+ final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
+ final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale);
+ final float centerX = normPivotX + offsetX;
+ final float centerY = normPivotY + offsetY;
+ mIdOfLastServiceToMagnify = id;
+ return setScaleAndCenter(scale, centerX, centerY, animate, id);
}
+ @GuardedBy("mLock")
boolean setScaleAndCenter(float scale, float centerX, float centerY,
boolean animate, int id) {
-
- synchronized (mLock) {
- if (!mRegistered) {
- return false;
- }
- if (DEBUG) {
- Slog.i(LOG_TAG,
- "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
- + ", centerY = " + centerY + ", animate = " + animate
- + ", id = " + id
- + ")");
- }
- final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
- sendSpecToAnimation(mCurrentMagnificationSpec, animate);
- if (isMagnifying() && (id != INVALID_ID)) {
- mIdOfLastServiceToMagnify = id;
- }
- return changed;
+ if (!mRegistered) {
+ return false;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG,
+ "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX
+ + ", centerY = " + centerY + ", animate = " + animate
+ + ", id = " + id
+ + ")");
}
+ final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY);
+ sendSpecToAnimation(mCurrentMagnificationSpec, animate);
+ if (isMagnifying() && (id != INVALID_ID)) {
+ mIdOfLastServiceToMagnify = id;
+ }
+ return changed;
}
/**
@@ -527,22 +512,21 @@ public class MagnificationController {
return changed;
}
+ @GuardedBy("mLock")
void offsetMagnifiedRegion(float offsetX, float offsetY, int id) {
- synchronized (mLock) {
- if (!mRegistered) {
- return;
- }
+ if (!mRegistered) {
+ return;
+ }
- final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
- final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
- if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
- onMagnificationChangedLocked();
- }
- if (id != INVALID_ID) {
- mIdOfLastServiceToMagnify = id;
- }
- sendSpecToAnimation(mCurrentMagnificationSpec, false);
+ final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX;
+ final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY;
+ if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) {
+ onMagnificationChangedLocked();
}
+ if (id != INVALID_ID) {
+ mIdOfLastServiceToMagnify = id;
+ }
+ sendSpecToAnimation(mCurrentMagnificationSpec, false);
}
boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) {
@@ -593,44 +577,38 @@ public class MagnificationController {
@Override
public String toString() {
- return "DisplayMagnification{" +
- "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec +
- ", mMagnificationRegion=" + mMagnificationRegion +
- ", mMagnificationBounds=" + mMagnificationBounds +
- ", mDisplayId=" + mDisplayId +
- ", mUserId=" + mUserId +
- ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify +
- ", mRegistered=" + mRegistered +
- ", mUnregisterPending=" + mUnregisterPending +
- '}';
+ return "DisplayMagnification["
+ + "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec
+ + ", mMagnificationRegion=" + mMagnificationRegion
+ + ", mMagnificationBounds=" + mMagnificationBounds
+ + ", mDisplayId=" + mDisplayId
+ + ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify
+ + ", mRegistered=" + mRegistered
+ + ", mUnregisterPending=" + mUnregisterPending
+ + ']';
}
-
}
- public MagnificationController(Context context, AccessibilityManagerService ams, Object lock) {
- this(context, ams, lock, null, LocalServices.getService(WindowManagerInternal.class),
- new ValueAnimator(), new SettingsBridge(context.getContentResolver()));
- mHandler = new Handler(context.getMainLooper());
+ /**
+ * MagnificationController Constructor
+ */
+ public MagnificationController(@NonNull Context context,
+ @NonNull AccessibilityManagerService ams, @NonNull Object lock) {
+ this(new ControllerContext(context, ams,
+ LocalServices.getService(WindowManagerInternal.class),
+ new Handler(context.getMainLooper()),
+ context.getResources().getInteger(R.integer.config_longAnimTime)), lock);
}
- public MagnificationController(
- Context context,
- AccessibilityManagerService ams,
- Object lock,
- Handler handler,
- WindowManagerInternal windowManagerInternal,
- ValueAnimator valueAnimator,
- SettingsBridge settingsBridge) {
- mHandler = handler;
- mWindowManager = windowManagerInternal;
- mMainThreadId = context.getMainLooper().getThread().getId();
- mAms = ams;
- mScreenStateObserver = new ScreenStateObserver(context, this);
+ /**
+ * Constructor for tests
+ */
+ @VisibleForTesting
+ public MagnificationController(@NonNull ControllerContext ctx, @NonNull Object lock) {
+ mControllerCtx = ctx;
mLock = lock;
- mSettingsBridge = settingsBridge;
- //TODO (multidisplay): Magnification is supported only for the default display.
- mDisplay = new DisplayMagnification(Display.DEFAULT_DISPLAY,
- new SpecAnimationBridge(context, mLock, mWindowManager, valueAnimator));
+ mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId();
+ mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this);
}
/**
@@ -639,54 +617,114 @@ public class MagnificationController {
*
* This tracking imposes a cost on the system, so we avoid tracking this data unless it's
* required.
+ *
+ * @param displayId The logical display id.
*/
- public void register() {
+ public void register(int displayId) {
synchronized (mLock) {
- mScreenStateObserver.register();
+ DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ display = new DisplayMagnification(displayId);
+ }
+ if (display.isRegistered()) {
+ return;
+ }
+ if (display.register()) {
+ mDisplays.put(displayId, display);
+ mScreenStateObserver.registerIfNecessary();
+ }
}
- mDisplay.register();
}
/**
* Stop requiring tracking the magnification region. We may remain registered while we
* reset magnification.
+ *
+ * @param displayId The logical display id.
*/
- public void unregister() {
+ public void unregister(int displayId) {
synchronized (mLock) {
- mScreenStateObserver.unregister();
+ unregisterLocked(displayId, false);
}
- mDisplay.unregister();
}
-
+
/**
- * Check if we are registered. Note that we may be planning to unregister at any moment.
+ * Stop tracking all displays' magnification region.
+ */
+ public void unregisterAll() {
+ synchronized (mLock) {
+ // display will be removed from array after unregister, we need to clone it to
+ // prevent error.
+ final SparseArray<DisplayMagnification> displays = mDisplays.clone();
+ for (int i = 0; i < displays.size(); i++) {
+ unregisterLocked(displays.keyAt(i), false);
+ }
+ }
+ }
+
+ /**
+ * Remove the display magnification with given id.
+ *
+ * @param displayId The logical display id.
+ */
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ unregisterLocked(displayId, true);
+ }
+ }
+
+ /**
+ * Check if we are registered on specified display. Note that we may be planning to unregister
+ * at any moment.
+ *
+ * @return {@code true} if the controller is registered on specified display.
+ * {@code false} otherwise.
*
- * @return {@code true} if the controller is registered. {@code false} otherwise.
+ * @param displayId The logical display id.
*/
- public boolean isRegisteredLocked() {
- return mDisplay.isRegisteredLocked();
+ public boolean isRegistered(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isRegistered();
+ }
}
/**
+ * @param displayId The logical display id.
* @return {@code true} if magnification is active, e.g. the scale
* is > 1, {@code false} otherwise
*/
- public boolean isMagnifying() {
- return mDisplay.isMagnifying();
+ public boolean isMagnifying(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isMagnifying();
+ }
}
/**
* Returns whether the magnification region contains the specified
* screen-relative coordinates.
*
+ * @param displayId The logical display id.
* @param x the screen-relative X coordinate to check
* @param y the screen-relative Y coordinate to check
* @return {@code true} if the coordinate is contained within the
* magnified region, or {@code false} otherwise
*/
- public boolean magnificationRegionContains(float x, float y) {
- return mDisplay.magnificationRegionContains(x, y);
-
+ public boolean magnificationRegionContains(int displayId, float x, float y) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.magnificationRegionContains(x, y);
+ }
}
/**
@@ -694,11 +732,18 @@ public class MagnificationController {
* magnification region. If magnification is not enabled, the returned
* bounds will be empty.
*
+ * @param displayId The logical display id.
* @param outBounds rect to populate with the bounds of the magnified
* region
*/
- public void getMagnificationBounds(@NonNull Rect outBounds) {
- mDisplay.getMagnificationBounds(outBounds);
+ public void getMagnificationBounds(int displayId, @NonNull Rect outBounds) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return;
+ }
+ display.getMagnificationBounds(outBounds);
+ }
}
/**
@@ -706,76 +751,122 @@ public class MagnificationController {
* region. If magnification is not enabled, then the returned region
* will be empty.
*
+ * @param displayId The logical display id.
* @param outRegion the region to populate
*/
- public void getMagnificationRegion(@NonNull Region outRegion) {
- mDisplay.getMagnificationRegion(outRegion);
+ public void getMagnificationRegion(int displayId, @NonNull Region outRegion) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return;
+ }
+ display.getMagnificationRegion(outRegion);
+ }
}
/**
* Returns the magnification scale. If an animation is in progress,
* this reflects the end state of the animation.
*
+ * @param displayId The logical display id.
* @return the scale
*/
- public float getScale() {
- return mDisplay.getScale();
+ public float getScale(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return 1.0f;
+ }
+ return display.getScale();
+ }
}
/**
* Returns the X offset of the magnification viewport. If an animation
* is in progress, this reflects the end state of the animation.
*
+ * @param displayId The logical display id.
* @return the X offset
*/
- public float getOffsetX() {
- return mDisplay.getOffsetX();
+ public float getOffsetX(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return 0.0f;
+ }
+ return display.getOffsetX();
+ }
}
-
/**
* Returns the screen-relative X coordinate of the center of the
* magnification viewport.
*
+ * @param displayId The logical display id.
* @return the X coordinate
*/
- public float getCenterX() {
- return mDisplay.getCenterX();
+ public float getCenterX(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return 0.0f;
+ }
+ return display.getCenterX();
+ }
}
/**
* Returns the Y offset of the magnification viewport. If an animation
* is in progress, this reflects the end state of the animation.
*
+ * @param displayId The logical display id.
* @return the Y offset
*/
- public float getOffsetY() {
- return mDisplay.getOffsetY();
+ public float getOffsetY(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return 0.0f;
+ }
+ return display.getOffsetY();
+ }
}
/**
* Returns the screen-relative Y coordinate of the center of the
* magnification viewport.
*
+ * @param displayId The logical display id.
* @return the Y coordinate
*/
- public float getCenterY() {
- return mDisplay.getCenterY();
+ public float getCenterY(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return 0.0f;
+ }
+ return display.getCenterY();
+ }
}
/**
* Resets the magnification scale and center, optionally animating the
* transition.
*
+ * @param displayId The logical display id.
* @param animate {@code true} to animate the transition, {@code false}
* to transition immediately
* @return {@code true} if the magnification spec changed, {@code false} if
* the spec did not change
*/
- public boolean reset(boolean animate) {
-
- return mDisplay.reset(animate);
-
+ public boolean reset(int displayId, boolean animate) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.reset(animate);
+ }
}
/**
@@ -783,6 +874,7 @@ public class MagnificationController {
* optionally animating the transition. If animation is disabled, the
* transition is immediate.
*
+ * @param displayId The logical display id.
* @param scale the target scale, must be >= 1
* @param pivotX the screen-relative X coordinate around which to scale
* @param pivotY the screen-relative Y coordinate around which to scale
@@ -792,15 +884,22 @@ public class MagnificationController {
* @return {@code true} if the magnification spec changed, {@code false} if
* the spec did not change
*/
- public boolean setScale(float scale, float pivotX, float pivotY, boolean animate, int id) {
- return mDisplay.
- setScale(scale, pivotX, pivotY, animate, id);
+ public boolean setScale(int displayId, float scale, float pivotX, float pivotY,
+ boolean animate, int id) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.setScale(scale, pivotX, pivotY, animate, id);
+ }
}
/**
* Sets the center of the magnified region, optionally animating the
* transition. If animation is disabled, the transition is immediate.
*
+ * @param displayId The logical display id.
* @param centerX the screen-relative X coordinate around which to
* center
* @param centerY the screen-relative Y coordinate around which to
@@ -811,9 +910,14 @@ public class MagnificationController {
* @return {@code true} if the magnification spec changed, {@code false} if
* the spec did not change
*/
- public boolean setCenter(float centerX, float centerY, boolean animate, int id) {
- return mDisplay.
- setScaleAndCenter(Float.NaN, centerX, centerY, animate, id);
+ public boolean setCenter(int displayId, float centerX, float centerY, boolean animate, int id) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.setScaleAndCenter(Float.NaN, centerX, centerY, animate, id);
+ }
}
/**
@@ -821,6 +925,7 @@ public class MagnificationController {
* animating the transition. If animation is disabled, the transition
* is immediate.
*
+ * @param displayId The logical display id.
* @param scale the target scale, or {@link Float#NaN} to leave unchanged
* @param centerX the screen-relative X coordinate around which to
* center and scale, or {@link Float#NaN} to leave unchanged
@@ -832,53 +937,66 @@ public class MagnificationController {
* @return {@code true} if the magnification spec changed, {@code false} if
* the spec did not change
*/
- public boolean setScaleAndCenter(
- float scale, float centerX, float centerY, boolean animate, int id) {
- return mDisplay.
- setScaleAndCenter(scale, centerX, centerY, animate, id);
+ public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY,
+ boolean animate, int id) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.setScaleAndCenter(scale, centerX, centerY, animate, id);
+ }
}
/**
* Offsets the magnified region. Note that the offsetX and offsetY values actually move in the
* opposite direction as the offsets passed in here.
*
+ * @param displayId The logical display id.
* @param offsetX the amount in pixels to offset the region in the X direction, in current
* screen pixels.
* @param offsetY the amount in pixels to offset the region in the Y direction, in current
* screen pixels.
* @param id the ID of the service requesting the change
*/
- public void offsetMagnifiedRegion(float offsetX, float offsetY, int id) {
- mDisplay.offsetMagnifiedRegion(offsetX, offsetY,
- id);
+ public void offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return;
+ }
+ display.offsetMagnifiedRegion(offsetX, offsetY, id);
+ }
}
/**
* Get the ID of the last service that changed the magnification spec.
*
+ * @param displayId The logical display id.
* @return The id
*/
- public int getIdOfLastServiceToMagnify() {
- return mDisplay.getIdOfLastServiceToMagnify();
+ public int getIdOfLastServiceToMagnify(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return -1;
+ }
+ return display.getIdOfLastServiceToMagnify();
+ }
}
/**
- * Persists the current magnification scale to the current user's settings.
+ * Persists the default display magnification scale to the current user's settings.
*/
public void persistScale() {
- persistScale(Display.DEFAULT_DISPLAY);
- }
- /**
- * Persists the current magnification scale to the current user's settings.
- */
- public void persistScale(int displayId) {
- final float scale = mDisplay.getScale();
+ // TODO: b/123047354, Need support multi-display?
+ final float scale = getScale(Display.DEFAULT_DISPLAY);
final int userId = mUserId;
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
- mSettingsBridge.putMagnificationScale(scale, displayId, userId);
+ mControllerCtx.putMagnificationScale(scale, userId);
return null;
}
}.execute();
@@ -892,7 +1010,7 @@ public class MagnificationController {
* scale if none is available
*/
public float getPersistedScale() {
- return mSettingsBridge.getMagnificationScale(Display.DEFAULT_DISPLAY, mUserId);
+ return mControllerCtx.getMagnificationScale(mUserId);
}
/**
@@ -901,50 +1019,136 @@ public class MagnificationController {
* @param userId the currently active user ID
*/
public void setUserId(int userId) {
- if (mUserId != userId) {
- mUserId = userId;
+ if (mUserId == userId) {
+ return;
+ }
+ mUserId = userId;
+ resetAllIfNeeded(false);
+ }
- synchronized (mLock) {
- if (isMagnifying()) {
- reset(false);
- }
+ /**
+ * Resets all displays' magnification if last magnifying service is disabled.
+ *
+ * @param connectionId
+ */
+ public void resetAllIfNeeded(int connectionId) {
+ synchronized (mLock) {
+ for (int i = 0; i < mDisplays.size(); i++) {
+ resetIfNeeded(mDisplays.keyAt(i), connectionId);
}
}
}
- /**
+ /**
* Resets magnification if magnification and auto-update are both enabled.
*
+ * @param displayId The logical display id.
* @param animate whether the animate the transition
- * @return whether was {@link #isMagnifying magnifying}
+ * @return whether was {@link #isMagnifying(int) magnifying}
*/
- public boolean resetIfNeeded(boolean animate) {
- return mDisplay.resetIfNeeded(animate);
+ boolean resetIfNeeded(int displayId, boolean animate) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null || !display.isMagnifying()) {
+ return false;
+ }
+ display.reset(animate);
+ return true;
+ }
}
/**
* Resets magnification if last magnifying service is disabled.
*
+ * @param displayId The logical display id.
* @param connectionId the connection ID be disabled.
* @return {@code true} on success, {@code false} on failure
*/
- public boolean resetIfNeeded(int connectionId) {
- return mDisplay.resetIfNeeded(connectionId);
+ boolean resetIfNeeded(int displayId, int connectionId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null || !display.isMagnifying()
+ || connectionId != display.getIdOfLastServiceToMagnify()) {
+ return false;
+ }
+ display.reset(true);
+ return true;
+ }
}
- void setForceShowMagnifiableBounds(boolean show) {
- mDisplay.setForceShowMagnifiableBounds(show);
+ void setForceShowMagnifiableBounds(int displayId, boolean show) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return;
+ }
+ display.setForceShowMagnifiableBounds(show);
+ }
}
private void onScreenTurnedOff() {
final Message m = PooledLambda.obtainMessage(
- mDisplay::resetIfNeeded, false);
- mHandler.sendMessage(m);
+ MagnificationController::resetAllIfNeeded, this, false);
+ mControllerCtx.getHandler().sendMessage(m);
+ }
+
+ private void resetAllIfNeeded(boolean animate) {
+ synchronized (mLock) {
+ for (int i = 0; i < mDisplays.size(); i++) {
+ resetIfNeeded(mDisplays.keyAt(i), animate);
+ }
+ }
+ }
+
+ private void unregisterLocked(int displayId, boolean delete) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return;
+ }
+ if (!display.isRegistered()) {
+ if (delete) {
+ mDisplays.remove(displayId);
+ }
+ return;
+ }
+ if (!display.isMagnifying()) {
+ display.unregister(delete);
+ } else {
+ display.unregisterPending(delete);
+ }
+ }
+
+ /**
+ * Callbacks from DisplayMagnification after display magnification unregistered. It will remove
+ * DisplayMagnification instance if delete is true, and unregister screen state if
+ * there is no registered display magnification.
+ */
+ private void unregisterCallbackLocked(int displayId, boolean delete) {
+ if (delete) {
+ mDisplays.remove(displayId);
+ }
+ // unregister screen state if necessary
+ boolean hasRegister = false;
+ for (int i = 0; i < mDisplays.size(); i++) {
+ final DisplayMagnification display = mDisplays.valueAt(i);
+ hasRegister = display.isRegistered();
+ if (hasRegister) {
+ break;
+ }
+ }
+ if (!hasRegister) {
+ mScreenStateObserver.unregister();
+ }
}
@Override
public String toString() {
- return mDisplay.toString();
+ StringBuilder builder = new StringBuilder();
+ builder.append("MagnificationController[");
+ builder.append("mUserId=").append(mUserId);
+ builder.append(", mDisplays=").append(mDisplays);
+ builder.append("]");
+ return builder.toString();
}
/**
@@ -952,7 +1156,7 @@ public class MagnificationController {
* updates to the window manager.
*/
private static class SpecAnimationBridge implements ValueAnimator.AnimatorUpdateListener {
- private final WindowManagerInternal mWindowManager;
+ private final ControllerContext mControllerCtx;
/**
* The magnification spec that was sent to the window manager. This should
@@ -973,16 +1177,17 @@ public class MagnificationController {
private final Object mLock;
+ private final int mDisplayId;
+
@GuardedBy("mLock")
private boolean mEnabled = false;
- private SpecAnimationBridge(Context context, Object lock, WindowManagerInternal wm,
- ValueAnimator animator) {
+ private SpecAnimationBridge(ControllerContext ctx, Object lock, int displayId) {
+ mControllerCtx = ctx;
mLock = lock;
- mWindowManager = wm;
- final long animationDuration = context.getResources().getInteger(
- R.integer.config_longAnimTime);
- mValueAnimator = animator;
+ mDisplayId = displayId;
+ final long animationDuration = mControllerCtx.getAnimationDuration();
+ mValueAnimator = mControllerCtx.newValueAnimator();
mValueAnimator.setDuration(animationDuration);
mValueAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
mValueAnimator.setFloatValues(0.0f, 1.0f);
@@ -999,7 +1204,8 @@ public class MagnificationController {
mEnabled = enabled;
if (!mEnabled) {
mSentMagnificationSpec.clear();
- mWindowManager.setMagnificationSpec(mSentMagnificationSpec);
+ mControllerCtx.getWindowManager().setMagnificationSpec(
+ mDisplayId, mSentMagnificationSpec);
}
}
}
@@ -1031,7 +1237,8 @@ public class MagnificationController {
}
mSentMagnificationSpec.setTo(spec);
- mWindowManager.setMagnificationSpec(spec);
+ mControllerCtx.getWindowManager().setMagnificationSpec(
+ mDisplayId, mSentMagnificationSpec);
}
}
@@ -1054,9 +1261,7 @@ public class MagnificationController {
mTmpMagnificationSpec.offsetY = mStartMagnificationSpec.offsetY +
(mEndMagnificationSpec.offsetY - mStartMagnificationSpec.offsetY)
* fract;
- synchronized (mLock) {
- setMagnificationSpecLocked(mTmpMagnificationSpec);
- }
+ setMagnificationSpecLocked(mTmpMagnificationSpec);
}
}
}
@@ -1072,7 +1277,7 @@ public class MagnificationController {
mController = controller;
}
- public void register() {
+ public void registerIfNecessary() {
if (!mRegistered) {
mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
mRegistered = true;
@@ -1092,26 +1297,97 @@ public class MagnificationController {
}
}
- // Extra class to get settings so tests can mock it
- public static class SettingsBridge {
- private final ContentResolver mContentResolver;
+ /**
+ * This class holds resources used between the classes in MagnificationController, and
+ * functions for tests to mock it.
+ */
+ @VisibleForTesting
+ public static class ControllerContext {
+ private final Context mContext;
+ private final AccessibilityManagerService mAms;
+ private final WindowManagerInternal mWindowManager;
+ private final Handler mHandler;
+ private final Long mAnimationDuration;
+
+ /**
+ * Constructor for ControllerContext.
+ */
+ public ControllerContext(@NonNull Context context,
+ @NonNull AccessibilityManagerService ams,
+ @NonNull WindowManagerInternal windowManager,
+ @NonNull Handler handler,
+ long animationDuration) {
+ mContext = context;
+ mAms = ams;
+ mWindowManager = windowManager;
+ mHandler = handler;
+ mAnimationDuration = animationDuration;
+ }
+
+ /**
+ * @return A context.
+ */
+ @NonNull
+ public Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * @return AccessibilityManagerService
+ */
+ @NonNull
+ public AccessibilityManagerService getAms() {
+ return mAms;
+ }
+
+ /**
+ * @return WindowManagerInternal
+ */
+ @NonNull
+ public WindowManagerInternal getWindowManager() {
+ return mWindowManager;
+ }
- public SettingsBridge(ContentResolver contentResolver) {
- mContentResolver = contentResolver;
+ /**
+ * @return Handler for main looper
+ */
+ @NonNull
+ public Handler getHandler() {
+ return mHandler;
}
- public void putMagnificationScale(float value, int displayId, int userId) {
- Settings.Secure.putFloatForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE + (
- Display.DEFAULT_DISPLAY == displayId ? "" : displayId),
- value, userId);
+ /**
+ * Create a new ValueAnimator.
+ *
+ * @return ValueAnimator
+ */
+ @NonNull
+ public ValueAnimator newValueAnimator() {
+ return new ValueAnimator();
}
- public float getMagnificationScale(int displayId, int userId) {
- return Settings.Secure.getFloatForUser(mContentResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE
- + (Display.DEFAULT_DISPLAY == displayId ? "" : displayId),
+ /**
+ * Write Settings of magnification scale.
+ */
+ public void putMagnificationScale(float value, int userId) {
+ Settings.Secure.putFloatForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, value, userId);
+ }
+
+ /**
+ * Get Settings of magnification scale.
+ */
+ public float getMagnificationScale(int userId) {
+ return Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
DEFAULT_MAGNIFICATION_SCALE, userId);
}
+
+ /**
+ * @return Configuration of animation duration.
+ */
+ public long getAnimationDuration() {
+ return mAnimationDuration;
+ }
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 80049e80e1a9..49db488bc740 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -43,6 +43,7 @@ import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
import android.util.TypedValue;
+import android.view.Display;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
@@ -251,14 +252,16 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
mScreenStateReceiver.unregister();
}
// Check if need to reset when MagnificationGestureHandler is the last magnifying service.
- mMagnificationController.resetIfNeeded(
+ mMagnificationController.resetAllIfNeeded(
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
clearAndTransitionToStateDetecting();
}
void notifyShortcutTriggered() {
if (mDetectShortcutTrigger) {
- boolean wasMagnifying = mMagnificationController.resetIfNeeded(/* animate */ true);
+ // TODO: multi-display support for magnification gesture handler
+ boolean wasMagnifying = mMagnificationController.resetIfNeeded(Display.DEFAULT_DISPLAY,
+ /* animate */ true);
if (wasMagnifying) {
clearAndTransitionToStateDetecting();
} else {
@@ -419,8 +422,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX
+ " scrollY: " + distanceY);
}
- mMagnificationController.offsetMagnifiedRegion(distanceX, distanceY,
- AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ // TODO: multi-display support for magnification gesture handler
+ mMagnificationController.offsetMagnifiedRegion(Display.DEFAULT_DISPLAY, distanceX,
+ distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
return /* event consumed: */ true;
}
@@ -436,7 +440,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
return mScaling;
}
- final float initialScale = mMagnificationController.getScale();
+ // TODO: multi-display support for magnification gesture handler
+ final float initialScale = mMagnificationController.getScale(Display.DEFAULT_DISPLAY);
final float targetScale = initialScale * detector.getScaleFactor();
// Don't allow a gesture to move the user further outside the
@@ -458,7 +463,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
final float pivotX = detector.getFocusX();
final float pivotY = detector.getFocusY();
if (DEBUG_PANNING_SCALING) Slog.i(LOG_TAG, "Scaled content to: " + scale + "x");
- mMagnificationController.setScale(scale, pivotX, pivotY, false,
+ // TODO: multi-display support for magnification gesture handler
+ mMagnificationController.setScale(Display.DEFAULT_DISPLAY, scale, pivotX, pivotY, false,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
return /* handled: */ true;
}
@@ -518,8 +524,10 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
}
final float eventX = event.getX();
final float eventY = event.getY();
- if (mMagnificationController.magnificationRegionContains(eventX, eventY)) {
- mMagnificationController.setCenter(eventX, eventY,
+ // TODO: multi-display support for magnification gesture handler
+ if (mMagnificationController.magnificationRegionContains(
+ Display.DEFAULT_DISPLAY, eventX, eventY)) {
+ mMagnificationController.setCenter(Display.DEFAULT_DISPLAY, eventX, eventY,
/* animate */ mLastMoveOutsideMagnifiedRegion,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
mLastMoveOutsideMagnifiedRegion = false;
@@ -657,8 +665,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ // TODO: multi-display support for magnification gesture handler
if (!mMagnificationController.magnificationRegionContains(
- event.getX(), event.getY())) {
+ Display.DEFAULT_DISPLAY, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
@@ -667,11 +676,16 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
// 3tap and hold
afterLongTapTimeoutTransitionToDraggingState(event);
+ } else if (isTapOutOfDistanceSlop()) {
+
+ transitionToDelegatingStateAndClear();
+
} else if (mDetectTripleTap
// If magnified, delay an ACTION_DOWN for mMultiTapMaxDelay
// to ensure reachability of
// STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
- || mMagnificationController.isMagnifying()) {
+ // TODO: multi-display support for magnification gesture handler
+ || mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) {
afterMultiTapTimeoutTransitionToDelegatingState();
@@ -683,7 +697,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
}
break;
case ACTION_POINTER_DOWN: {
- if (mMagnificationController.isMagnifying()) {
+ // TODO: multi-display support for magnification gesture handler
+ if (mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) {
transitionTo(mPanningScalingState);
clear();
} else {
@@ -712,8 +727,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
+ // TODO: multi-display support for magnification gesture handler
if (!mMagnificationController.magnificationRegionContains(
- event.getX(), event.getY())) {
+ Display.DEFAULT_DISPLAY, event.getX(), event.getY())) {
transitionToDelegatingStateAndClear();
@@ -864,7 +880,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
clear();
// Toggle zoom
- if (mMagnificationController.isMagnifying()) {
+ // TODO: multi-display support for magnification gesture handler
+ if (mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY)) {
zoomOff();
} else {
zoomOn(up.getX(), up.getY());
@@ -876,8 +893,9 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()");
clear();
+ // TODO: multi-display support for magnification gesture handler
mViewportDraggingState.mZoomedInBeforeDrag =
- mMagnificationController.isMagnifying();
+ mMagnificationController.isMagnifying(Display.DEFAULT_DISPLAY);
zoomOn(down.getX(), down.getY());
@@ -904,7 +922,33 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "setShortcutTriggered(" + state + ")");
mShortcutTriggered = state;
- mMagnificationController.setForceShowMagnifiableBounds(state);
+ // TODO: multi-display support for magnification gesture handler
+ mMagnificationController.setForceShowMagnifiableBounds(Display.DEFAULT_DISPLAY, state);
+ }
+
+ /**
+ * Detects if last action down is out of distance slop between with previous
+ * one, when triple tap is enabled.
+ *
+ * @return true if tap is out of distance slop
+ */
+ boolean isTapOutOfDistanceSlop() {
+ if (!mDetectTripleTap) return false;
+ if (mPreLastDown == null || mLastDown == null) {
+ return false;
+ }
+ final boolean outOfDistanceSlop =
+ GestureUtils.distance(mPreLastDown, mLastDown) > mMultiTapMaxDistance;
+ if (tapCount() > 0) {
+ return outOfDistanceSlop;
+ }
+ // There's no tap in the queue here. We still need to check if this is the case that
+ // user tap screen quickly and out of distance slop.
+ if (outOfDistanceSlop
+ && !GestureUtils.isTimedOut(mPreLastDown, mLastDown, mMultiTapMaxDelay)) {
+ return true;
+ }
+ return false;
}
}
@@ -914,7 +958,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
final float scale = MathUtils.constrain(
mMagnificationController.getPersistedScale(),
MIN_SCALE, MAX_SCALE);
- mMagnificationController.setScaleAndCenter(
+ // TODO: multi-display support for magnification gesture handler
+ mMagnificationController.setScaleAndCenter(Display.DEFAULT_DISPLAY,
scale, centerX, centerY,
/* animate */ true,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
@@ -922,8 +967,8 @@ class MagnificationGestureHandler extends BaseEventStreamTransformation {
private void zoomOff() {
if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOff()");
-
- mMagnificationController.reset(/* animate */ true);
+ // TODO: multi-display support for magnification gesture handler
+ mMagnificationController.reset(Display.DEFAULT_DISPLAY, /* animate */ true);
}
private static MotionEvent recycleAndNullify(@Nullable MotionEvent event) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 7dfd8fef13c2..9f7d33f6796a 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -261,6 +261,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
private Runnable mAugmentedAutofillDestroyer;
/**
+ * List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics.
+ */
+ @GuardedBy("mLock")
+ private ArrayList<LogMaker> mAugmentedRequestsLogs;
+
+ /**
* Receiver of assist data from the app's {@link Activity}.
*/
private final IAssistDataReceiver mAssistReceiver = new IAssistDataReceiver.Stub() {
@@ -2541,8 +2547,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
mService.resetLastResponse();
- // The default autofill service cannot fullfill the request, let's check if the intelligence
- // service can.
+ // The default autofill service cannot fullfill the request, let's check if the augmented
+ // autofill service can.
mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked();
if (mAugmentedAutofillDestroyer == null) {
if (sVerbose) {
@@ -2605,8 +2611,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
if (sVerbose) {
- Slog.v(TAG, "calling IntelligenseService on view " + mCurrentViewId
- + " using suggestion mode " + smartSuggestionFlagsToString(mode)
+ Slog.v(TAG, "calling Augmented Autofill Service ("
+ + remoteService.getComponentName().toShortString() + ") on view "
+ + mCurrentViewId + " using suggestion mode "
+ + smartSuggestionFlagsToString(mode)
+ " when server returned null for session " + this.id);
}
@@ -2620,8 +2628,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
final AutofillValue currentValue = mViewStates.get(mCurrentViewId).getCurrentValue();
// TODO(b/111330312): we might need to add a new state in the AutofillManager to optimize
- // furgher AFM -> AFMS calls.
- // TODO(b/119638958): add CTS tests
+ // further AFM -> AFMS calls.
+
+ if (mAugmentedRequestsLogs == null) {
+ mAugmentedRequestsLogs = new ArrayList<>();
+ }
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_AUGMENTED_REQUEST,
+ remoteService.getComponentName().getPackageName());
+ mAugmentedRequestsLogs.add(log);
+
remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, mCurrentViewId,
currentValue);
@@ -2912,6 +2927,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mAugmentedAutofillDestroyer != null) {
pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer");
}
+ if (mAugmentedRequestsLogs != null) {
+ pw.print(prefix); pw.print("number augmented requests: ");
+ pw.println(mAugmentedRequestsLogs.size());
+ }
+
mRemoteFillService.dump(prefix, pw);
}
@@ -3053,8 +3073,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
mMetricsLogger.write(log);
}
}
- mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests));
+
+ final int totalAugmentedRequests = mAugmentedRequestsLogs == null ? 0
+ : mAugmentedRequestsLogs.size();
+ if (totalAugmentedRequests > 0) {
+ if (sVerbose) {
+ Slog.v(TAG, "destroyLocked(): logging " + totalRequests + " augmented requests");
+ }
+ for (int i = 0; i < totalAugmentedRequests; i++) {
+ final LogMaker log = mAugmentedRequestsLogs.get(i);
+ mMetricsLogger.write(log);
+ }
+ }
+
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests);
+ if (totalAugmentedRequests > 0) {
+ log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS,
+ totalAugmentedRequests);
+ }
+ mMetricsLogger.write(log);
return mRemoteFillService;
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 0fa996ed7657..fcd136c65169 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -64,6 +64,7 @@ import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.ThreadLocalWorkSource;
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -1131,23 +1132,23 @@ class AlarmManagerService extends SystemService {
final IBinder mListener;
final WorkSource mWorkSource;
final int mUid;
+ final int mCreatorUid;
final String mTag;
final BroadcastStats mBroadcastStats;
final FilterStats mFilterStats;
final int mAlarmType;
- InFlight(AlarmManagerService service, PendingIntent pendingIntent, IAlarmListener listener,
- WorkSource workSource, int uid, String alarmPkg, int alarmType, String tag,
- long nowELAPSED) {
- mPendingIntent = pendingIntent;
+ InFlight(AlarmManagerService service, Alarm alarm, long nowELAPSED) {
+ mPendingIntent = alarm.operation;
mWhenElapsed = nowELAPSED;
- mListener = listener != null ? listener.asBinder() : null;
- mWorkSource = workSource;
- mUid = uid;
- mTag = tag;
- mBroadcastStats = (pendingIntent != null)
- ? service.getStatsLocked(pendingIntent)
- : service.getStatsLocked(uid, alarmPkg);
+ mListener = alarm.listener != null ? alarm.listener.asBinder() : null;
+ mWorkSource = alarm.workSource;
+ mUid = alarm.uid;
+ mCreatorUid = alarm.creatorUid;
+ mTag = alarm.statsTag;
+ mBroadcastStats = (alarm.operation != null)
+ ? service.getStatsLocked(alarm.operation)
+ : service.getStatsLocked(alarm.uid, alarm.packageName);
FilterStats fs = mBroadcastStats.filterStats.get(mTag);
if (fs == null) {
fs = new FilterStats(mBroadcastStats, mTag);
@@ -1155,7 +1156,7 @@ class AlarmManagerService extends SystemService {
}
fs.lastTime = nowELAPSED;
mFilterStats = fs;
- mAlarmType = alarmType;
+ mAlarmType = alarm.type;
}
@Override
@@ -1165,6 +1166,7 @@ class AlarmManagerService extends SystemService {
+ ", when=" + mWhenElapsed
+ ", workSource=" + mWorkSource
+ ", uid=" + mUid
+ + ", creatorUid=" + mCreatorUid
+ ", tag=" + mTag
+ ", broadcastStats=" + mBroadcastStats
+ ", filterStats=" + mFilterStats
@@ -3811,12 +3813,10 @@ class AlarmManagerService extends SystemService {
/**
* Attribute blame for a WakeLock.
- * @param pi PendingIntent to attribute blame to if ws is null.
* @param ws WorkSource to attribute blame.
- * @param knownUid attribution uid; < 0 if we need to derive it from the PendingIntent sender
+ * @param knownUid attribution uid; < 0 values are ignored.
*/
- void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, String tag,
- int knownUid, boolean first) {
+ void setWakelockWorkSource(WorkSource ws, int knownUid, String tag, boolean first) {
try {
mWakeLock.setHistoryTag(first ? tag : null);
@@ -3825,11 +3825,8 @@ class AlarmManagerService extends SystemService {
return;
}
- final int uid = (knownUid >= 0)
- ? knownUid
- : ActivityManager.getService().getUidForIntentSender(pi.getTarget());
- if (uid >= 0) {
- mWakeLock.setWorkSource(new WorkSource(uid));
+ if (knownUid >= 0) {
+ mWakeLock.setWorkSource(new WorkSource(knownUid));
return;
}
} catch (Exception e) {
@@ -3839,6 +3836,14 @@ class AlarmManagerService extends SystemService {
mWakeLock.setWorkSource(null);
}
+ private static int getAlarmAttributionUid(Alarm alarm) {
+ if (alarm.workSource != null && !alarm.workSource.isEmpty()) {
+ return alarm.workSource.getAttributionUid();
+ }
+
+ return alarm.creatorUid;
+ }
+
@VisibleForTesting
class AlarmHandler extends Handler {
public static final int ALARM_EVENT = 1;
@@ -4285,8 +4290,8 @@ class AlarmManagerService extends SystemService {
// the next of our alarms is now in flight. reattribute the wakelock.
if (mInFlight.size() > 0) {
InFlight inFlight = mInFlight.get(0);
- setWakelockWorkSource(inFlight.mPendingIntent, inFlight.mWorkSource,
- inFlight.mAlarmType, inFlight.mTag, -1, false);
+ setWakelockWorkSource(inFlight.mWorkSource, inFlight.mCreatorUid, inFlight.mTag,
+ false);
} else {
// should never happen
mLog.w("Alarm wakelock still held but sent queue empty");
@@ -4369,64 +4374,70 @@ class AlarmManagerService extends SystemService {
*/
@GuardedBy("mLock")
public void deliverLocked(Alarm alarm, long nowELAPSED, boolean allowWhileIdle) {
- if (alarm.operation != null) {
- // PendingIntent alarm
- mSendCount++;
-
- try {
- alarm.operation.send(getContext(), 0,
- mBackgroundIntent.putExtra(
- Intent.EXTRA_ALARM_COUNT, alarm.count),
- mDeliveryTracker, mHandler, null,
- allowWhileIdle ? mIdleOptions : null);
- } catch (PendingIntent.CanceledException e) {
- if (alarm.repeatInterval > 0) {
- // This IntentSender is no longer valid, but this
- // is a repeating alarm, so toss it
- removeImpl(alarm.operation, null);
+ final long workSourceToken = ThreadLocalWorkSource.setUid(
+ getAlarmAttributionUid(alarm));
+ try {
+ if (alarm.operation != null) {
+ // PendingIntent alarm
+ mSendCount++;
+
+ try {
+ alarm.operation.send(getContext(), 0,
+ mBackgroundIntent.putExtra(
+ Intent.EXTRA_ALARM_COUNT, alarm.count),
+ mDeliveryTracker, mHandler, null,
+ allowWhileIdle ? mIdleOptions : null);
+ } catch (PendingIntent.CanceledException e) {
+ if (alarm.repeatInterval > 0) {
+ // This IntentSender is no longer valid, but this
+ // is a repeating alarm, so toss it
+ removeImpl(alarm.operation, null);
+ }
+ // No actual delivery was possible, so the delivery tracker's
+ // 'finished' callback won't be invoked. We also don't need
+ // to do any wakelock or stats tracking, so we have nothing
+ // left to do here but go on to the next thing.
+ mSendFinishCount++;
+ return;
}
- // No actual delivery was possible, so the delivery tracker's
- // 'finished' callback won't be invoked. We also don't need
- // to do any wakelock or stats tracking, so we have nothing
- // left to do here but go on to the next thing.
- mSendFinishCount++;
- return;
- }
- } else {
- // Direct listener callback alarm
- mListenerCount++;
-
- if (RECORD_ALARMS_IN_HISTORY) {
- if (alarm.listener == mTimeTickTrigger) {
- mTickHistory[mNextTickHistory++] = nowELAPSED;
- if (mNextTickHistory >= TICK_HISTORY_DEPTH) {
- mNextTickHistory = 0;
+ } else {
+ // Direct listener callback alarm
+ mListenerCount++;
+
+ if (RECORD_ALARMS_IN_HISTORY) {
+ if (alarm.listener == mTimeTickTrigger) {
+ mTickHistory[mNextTickHistory++] = nowELAPSED;
+ if (mNextTickHistory >= TICK_HISTORY_DEPTH) {
+ mNextTickHistory = 0;
+ }
}
}
- }
- try {
- if (DEBUG_LISTENER_CALLBACK) {
- Slog.v(TAG, "Alarm to uid=" + alarm.uid
- + " listener=" + alarm.listener.asBinder());
- }
- alarm.listener.doAlarm(this);
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(AlarmHandler.LISTENER_TIMEOUT,
- alarm.listener.asBinder()),
- mConstants.LISTENER_TIMEOUT);
- } catch (Exception e) {
- if (DEBUG_LISTENER_CALLBACK) {
- Slog.i(TAG, "Alarm undeliverable to listener "
- + alarm.listener.asBinder(), e);
+ try {
+ if (DEBUG_LISTENER_CALLBACK) {
+ Slog.v(TAG, "Alarm to uid=" + alarm.uid
+ + " listener=" + alarm.listener.asBinder());
+ }
+ alarm.listener.doAlarm(this);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(AlarmHandler.LISTENER_TIMEOUT,
+ alarm.listener.asBinder()),
+ mConstants.LISTENER_TIMEOUT);
+ } catch (Exception e) {
+ if (DEBUG_LISTENER_CALLBACK) {
+ Slog.i(TAG, "Alarm undeliverable to listener "
+ + alarm.listener.asBinder(), e);
+ }
+ // As in the PendingIntent.CanceledException case, delivery of the
+ // alarm was not possible, so we have no wakelock or timeout or
+ // stats management to do. It threw before we posted the delayed
+ // timeout message, so we're done here.
+ mListenerFinishCount++;
+ return;
}
- // As in the PendingIntent.CanceledException case, delivery of the
- // alarm was not possible, so we have no wakelock or timeout or
- // stats management to do. It threw before we posted the delayed
- // timeout message, so we're done here.
- mListenerFinishCount++;
- return;
}
+ } finally {
+ ThreadLocalWorkSource.restore(workSourceToken);
}
// The alarm is now in flight; now arrange wakelock and stats tracking
@@ -4434,15 +4445,11 @@ class AlarmManagerService extends SystemService {
Slog.d(TAG, "mBroadcastRefCount -> " + (mBroadcastRefCount + 1));
}
if (mBroadcastRefCount == 0) {
- setWakelockWorkSource(alarm.operation, alarm.workSource,
- alarm.type, alarm.statsTag, (alarm.operation == null) ? alarm.uid : -1,
- true);
+ setWakelockWorkSource(alarm.workSource, alarm.creatorUid, alarm.statsTag, true);
mWakeLock.acquire();
mHandler.obtainMessage(AlarmHandler.REPORT_ALARMS_ACTIVE, 1).sendToTarget();
}
- final InFlight inflight = new InFlight(AlarmManagerService.this,
- alarm.operation, alarm.listener, alarm.workSource, alarm.uid,
- alarm.packageName, alarm.type, alarm.statsTag, nowELAPSED);
+ final InFlight inflight = new InFlight(AlarmManagerService.this, alarm, nowELAPSED);
mInFlight.add(inflight);
mBroadcastRefCount++;
if (allowWhileIdle) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 00550d9c660e..fae7a8d7bfab 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3183,6 +3183,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
return mMultinetworkPolicyTracker.getAvoidBadWifi();
}
+ @Override
+ public boolean getAvoidBadWifi() {
+ if (!checkNetworkStackPermission()) {
+ throw new SecurityException("avoidBadWifi requires NETWORK_STACK permission");
+ }
+ return avoidBadWifi();
+ }
+
+
private void rematchForAvoidBadWifiUpdate() {
rematchAllNetworksAndRequests(null, 0);
for (NetworkAgentInfo nai: mNetworkAgentInfos.values()) {
@@ -6346,6 +6355,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ @GuardedBy("mVpns")
+ private Vpn getVpnIfOwner() {
+ final int uid = Binder.getCallingUid();
+ final int user = UserHandle.getUserId(uid);
+
+ final Vpn vpn = mVpns.get(user);
+ if (vpn == null) {
+ return null;
+ } else {
+ final VpnInfo info = vpn.getVpnInfo();
+ return (info == null || info.ownerUid != uid) ? null : vpn;
+ }
+ }
+
/**
* Caller either needs to be an active VPN, or hold the NETWORK_STACK permission
* for testing.
@@ -6354,14 +6377,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (checkNetworkStackPermission()) {
return null;
}
- final int uid = Binder.getCallingUid();
- final int user = UserHandle.getUserId(uid);
synchronized (mVpns) {
- Vpn vpn = mVpns.get(user);
- try {
- if (vpn.getVpnInfo().ownerUid == uid) return vpn;
- } catch (NullPointerException e) {
- /* vpn is null, or VPN is not connected and getVpnInfo() is null. */
+ Vpn vpn = getVpnIfOwner();
+ if (vpn != null) {
+ return vpn;
}
}
throw new SecurityException("App must either be an active VPN or have the NETWORK_STACK "
@@ -6390,4 +6409,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
return uid;
}
+
+ @Override
+ public boolean isCallerCurrentAlwaysOnVpnApp() {
+ synchronized (mVpns) {
+ Vpn vpn = getVpnIfOwner();
+ return vpn != null && vpn.getAlwaysOn();
+ }
+ }
+
+ @Override
+ public boolean isCallerCurrentAlwaysOnVpnLockdownApp() {
+ synchronized (mVpns) {
+ Vpn vpn = getVpnIfOwner();
+ return vpn != null && vpn.getLockdown();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 121a830f05f5..39030aaf3eb4 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -341,6 +341,29 @@ public class DeviceIdleController extends SystemService
@VisibleForTesting
static final int STATE_QUICK_DOZE_DELAY = 7;
+ private static final int ACTIVE_REASON_UNKNOWN = 0;
+ private static final int ACTIVE_REASON_MOTION = 1;
+ private static final int ACTIVE_REASON_SCREEN = 2;
+ private static final int ACTIVE_REASON_CHARGING = 3;
+ private static final int ACTIVE_REASON_UNLOCKED = 4;
+ private static final int ACTIVE_REASON_FROM_BINDER_CALL = 5;
+ private static final int ACTIVE_REASON_FORCED = 6;
+ private static final int ACTIVE_REASON_ALARM = 7;
+ @VisibleForTesting
+ static final int SET_IDLE_FACTOR_RESULT_UNINIT = -1;
+ @VisibleForTesting
+ static final int SET_IDLE_FACTOR_RESULT_IGNORED = 0;
+ @VisibleForTesting
+ static final int SET_IDLE_FACTOR_RESULT_OK = 1;
+ @VisibleForTesting
+ static final int SET_IDLE_FACTOR_RESULT_NOT_SUPPORT = 2;
+ @VisibleForTesting
+ static final int SET_IDLE_FACTOR_RESULT_INVALID = 3;
+ @VisibleForTesting
+ static final long MIN_STATE_STEP_ALARM_CHANGE = 60 * 1000;
+ @VisibleForTesting
+ static final float MIN_PRE_IDLE_FACTOR_CHANGE = 0.05f;
+
@VisibleForTesting
static String stateToString(int state) {
switch (state) {
@@ -405,6 +428,7 @@ public class DeviceIdleController extends SystemService
private long mNextSensingTimeoutAlarmTime;
private long mCurIdleBudget;
private long mMaintenanceStartTime;
+ private long mIdleStartTime;
private int mActiveIdleOpCount;
private PowerManager.WakeLock mActiveIdleWakeLock; // held when there are operations in progress
@@ -415,6 +439,17 @@ public class DeviceIdleController extends SystemService
private boolean mAlarmsActive;
private boolean mReportedMaintenanceActivity;
+ /* Factor to apply to INACTIVE_TIMEOUT and IDLE_AFTER_INACTIVE_TIMEOUT in order to enter
+ * STATE_IDLE faster or slower. Don't apply this to SENSING_TIMEOUT or LOCATING_TIMEOUT because:
+ * - Both of them are shorter
+ * - Device sensor might take time be to become be stabilized
+ * Also don't apply the factor if the device is in motion because device motion provides a
+ * stronger signal than a prediction algorithm.
+ */
+ private float mPreIdleFactor;
+ private float mLastPreIdleFactor;
+ private int mActiveReason;
+
public final AtomicFile mConfigFile;
private final RemoteCallbackList<IMaintenanceActivityListener> mMaintenanceActivityListeners =
@@ -760,6 +795,10 @@ public class DeviceIdleController extends SystemService
* exit doze. Default = true
*/
private static final String KEY_WAIT_FOR_UNLOCK = "wait_for_unlock";
+ private static final String KEY_PRE_IDLE_FACTOR_LONG =
+ "pre_idle_factor_long";
+ private static final String KEY_PRE_IDLE_FACTOR_SHORT =
+ "pre_idle_factor_short";
/**
* This is the time, after becoming inactive, that we go in to the first
@@ -987,6 +1026,16 @@ public class DeviceIdleController extends SystemService
*/
public long NOTIFICATION_WHITELIST_DURATION;
+ /**
+ * Pre idle time factor use to make idle delay longer
+ */
+ public float PRE_IDLE_FACTOR_LONG;
+
+ /**
+ * Pre idle time factor use to make idle delay shorter
+ */
+ public float PRE_IDLE_FACTOR_SHORT;
+
public boolean WAIT_FOR_UNLOCK;
private final ContentResolver mResolver;
@@ -1082,6 +1131,8 @@ public class DeviceIdleController extends SystemService
NOTIFICATION_WHITELIST_DURATION = mParser.getDurationMillis(
KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);
WAIT_FOR_UNLOCK = mParser.getBoolean(KEY_WAIT_FOR_UNLOCK, false);
+ PRE_IDLE_FACTOR_LONG = mParser.getFloat(KEY_PRE_IDLE_FACTOR_LONG, 1.67f);
+ PRE_IDLE_FACTOR_SHORT = mParser.getFloat(KEY_PRE_IDLE_FACTOR_SHORT, 0.33f);
}
}
@@ -1196,6 +1247,12 @@ public class DeviceIdleController extends SystemService
pw.print(" "); pw.print(KEY_WAIT_FOR_UNLOCK); pw.print("=");
pw.println(WAIT_FOR_UNLOCK);
+
+ pw.print(" "); pw.print(KEY_PRE_IDLE_FACTOR_LONG); pw.print("=");
+ pw.println(PRE_IDLE_FACTOR_LONG);
+
+ pw.print(" "); pw.print(KEY_PRE_IDLE_FACTOR_SHORT); pw.print("=");
+ pw.println(PRE_IDLE_FACTOR_SHORT);
}
}
@@ -1244,6 +1301,8 @@ public class DeviceIdleController extends SystemService
private static final int MSG_FINISH_IDLE_OP = 8;
private static final int MSG_REPORT_TEMP_APP_WHITELIST_CHANGED = 9;
private static final int MSG_SEND_CONSTRAINT_MONITORING = 10;
+ private static final int MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR = 11;
+ private static final int MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR = 12;
final class MyHandler extends Handler {
MyHandler(Looper looper) {
@@ -1373,6 +1432,13 @@ public class DeviceIdleController extends SystemService
constraint.stopMonitoring();
}
} break;
+ case MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR: {
+ updatePreIdleFactor();
+ } break;
+ case MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR: {
+ updatePreIdleFactor();
+ maybeDoImmediateMaintenance();
+ } break;
}
}
}
@@ -1526,6 +1592,28 @@ public class DeviceIdleController extends SystemService
DeviceIdleController.this.unregisterMaintenanceActivityListener(listener);
}
+ @Override public int setPreIdleTimeoutMode(int mode) {
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
+ null);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return DeviceIdleController.this.setPreIdleTimeoutMode(mode);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override public void resetPreIdleTimeoutMode() {
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.DEVICE_POWER,
+ null);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ DeviceIdleController.this.resetPreIdleTimeoutMode();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
@Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
DeviceIdleController.this.dump(fd, pw, args);
}
@@ -1768,9 +1856,12 @@ public class DeviceIdleController extends SystemService
// Start out assuming we are charging. If we aren't, we will at least get
// a battery update the next time the level drops.
mCharging = true;
+ mActiveReason = ACTIVE_REASON_UNKNOWN;
mState = STATE_ACTIVE;
mLightState = LIGHT_STATE_ACTIVE;
mInactiveTimeout = mConstants.INACTIVE_TIMEOUT;
+ mPreIdleFactor = 1.0f;
+ mLastPreIdleFactor = 1.0f;
}
mBinderService = new BinderService();
@@ -2394,6 +2485,7 @@ public class DeviceIdleController extends SystemService
public void exitIdleInternal(String reason) {
synchronized (this) {
+ mActiveReason = ACTIVE_REASON_FROM_BINDER_CALL;
becomeActiveLocked(reason, Binder.getCallingUid());
}
}
@@ -2463,6 +2555,7 @@ public class DeviceIdleController extends SystemService
} else if (screenOn) {
mScreenOn = true;
if (!mForceIdle && (!mScreenLocked || !mConstants.WAIT_FOR_UNLOCK)) {
+ mActiveReason = ACTIVE_REASON_SCREEN;
becomeActiveLocked("screen", Process.myUid());
}
}
@@ -2485,6 +2578,7 @@ public class DeviceIdleController extends SystemService
} else if (charging) {
mCharging = charging;
if (!mForceIdle) {
+ mActiveReason = ACTIVE_REASON_CHARGING;
becomeActiveLocked("charging", Process.myUid());
}
}
@@ -2516,6 +2610,7 @@ public class DeviceIdleController extends SystemService
if (mScreenLocked != showing) {
mScreenLocked = showing;
if (mScreenOn && !mForceIdle && !mScreenLocked) {
+ mActiveReason = ACTIVE_REASON_UNLOCKED;
becomeActiveLocked("unlocked", Process.myUid());
}
}
@@ -2587,7 +2682,11 @@ public class DeviceIdleController extends SystemService
mState = STATE_INACTIVE;
if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE");
resetIdleManagementLocked();
- scheduleAlarmLocked(mInactiveTimeout, false);
+ long delay = mInactiveTimeout;
+ if (shouldUseIdleTimeoutFactorLocked()) {
+ delay = (long) (mPreIdleFactor * delay);
+ }
+ scheduleAlarmLocked(delay, false);
EventLogTags.writeDeviceIdle(mState, "no activity");
}
}
@@ -2605,6 +2704,7 @@ public class DeviceIdleController extends SystemService
mNextIdlePendingDelay = 0;
mNextIdleDelay = 0;
mNextLightIdleDelay = 0;
+ mIdleStartTime = 0;
cancelAlarmLocked();
cancelSensingTimeoutAlarmLocked();
cancelLocatingLocked();
@@ -2621,6 +2721,7 @@ public class DeviceIdleController extends SystemService
if (mForceIdle) {
mForceIdle = false;
if (mScreenOn || mCharging) {
+ mActiveReason = ACTIVE_REASON_FORCED;
becomeActiveLocked("exit-force", Process.myUid());
}
}
@@ -2740,6 +2841,7 @@ public class DeviceIdleController extends SystemService
if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) {
// Whoops, there is an upcoming alarm. We don't actually want to go idle.
if (mState != STATE_ACTIVE) {
+ mActiveReason = ACTIVE_REASON_ALARM;
becomeActiveLocked("alarm", Process.myUid());
becomeInactiveIfAppropriateLocked();
}
@@ -2763,7 +2865,11 @@ public class DeviceIdleController extends SystemService
// We have now been inactive long enough, it is time to start looking
// for motion and sleep some more while doing so.
startMonitoringMotionLocked();
- scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
+ long delay = mConstants.IDLE_AFTER_INACTIVE_TIMEOUT;
+ if (shouldUseIdleTimeoutFactorLocked()) {
+ delay = (long) (mPreIdleFactor * delay);
+ }
+ scheduleAlarmLocked(delay, false);
moveToStateLocked(STATE_IDLE_PENDING, reason);
break;
case STATE_IDLE_PENDING:
@@ -2834,6 +2940,7 @@ public class DeviceIdleController extends SystemService
" ms.");
mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);
if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);
+ mIdleStartTime = SystemClock.elapsedRealtime();
mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);
if (mNextIdleDelay < mConstants.IDLE_TIMEOUT) {
mNextIdleDelay = mConstants.IDLE_TIMEOUT;
@@ -2934,6 +3041,127 @@ public class DeviceIdleController extends SystemService
}
}
+ @VisibleForTesting
+ int setPreIdleTimeoutMode(int mode) {
+ return setPreIdleTimeoutFactor(getPreIdleTimeoutByMode(mode));
+ }
+
+ @VisibleForTesting
+ float getPreIdleTimeoutByMode(int mode) {
+ switch (mode) {
+ case PowerManager.PRE_IDLE_TIMEOUT_MODE_LONG: {
+ return mConstants.PRE_IDLE_FACTOR_LONG;
+ }
+ case PowerManager.PRE_IDLE_TIMEOUT_MODE_SHORT: {
+ return mConstants.PRE_IDLE_FACTOR_SHORT;
+ }
+ case PowerManager.PRE_IDLE_TIMEOUT_MODE_NORMAL: {
+ return 1.0f;
+ }
+ default: {
+ Slog.w(TAG, "Invalid time out factor mode: " + mode);
+ return 1.0f;
+ }
+ }
+ }
+
+ @VisibleForTesting
+ float getPreIdleTimeoutFactor() {
+ return mPreIdleFactor;
+ }
+
+ @VisibleForTesting
+ int setPreIdleTimeoutFactor(float ratio) {
+ if (!mDeepEnabled) {
+ if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Deep Idle disable");
+ return SET_IDLE_FACTOR_RESULT_NOT_SUPPORT;
+ } else if (ratio <= MIN_PRE_IDLE_FACTOR_CHANGE) {
+ if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Invalid input");
+ return SET_IDLE_FACTOR_RESULT_INVALID;
+ } else if (Math.abs(ratio - mPreIdleFactor) < MIN_PRE_IDLE_FACTOR_CHANGE) {
+ if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: New factor same as previous factor");
+ return SET_IDLE_FACTOR_RESULT_IGNORED;
+ }
+ synchronized (this) {
+ mLastPreIdleFactor = mPreIdleFactor;
+ mPreIdleFactor = ratio;
+ }
+ if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: " + ratio);
+ postUpdatePreIdleFactor();
+ return SET_IDLE_FACTOR_RESULT_OK;
+ }
+
+ @VisibleForTesting
+ void resetPreIdleTimeoutMode() {
+ synchronized (this) {
+ mLastPreIdleFactor = mPreIdleFactor;
+ mPreIdleFactor = 1.0f;
+ }
+ if (DEBUG) Slog.d(TAG, "resetPreIdleTimeoutMode to 1.0");
+ postResetPreIdleTimeoutFactor();
+ }
+
+ private void postUpdatePreIdleFactor() {
+ mHandler.sendEmptyMessage(MSG_UPDATE_PRE_IDLE_TIMEOUT_FACTOR);
+ }
+
+ private void postResetPreIdleTimeoutFactor() {
+ mHandler.sendEmptyMessage(MSG_RESET_PRE_IDLE_TIMEOUT_FACTOR);
+ }
+
+ @VisibleForTesting
+ void updatePreIdleFactor() {
+ synchronized (this) {
+ if (!shouldUseIdleTimeoutFactorLocked()) {
+ return;
+ }
+ if (mState == STATE_INACTIVE || mState == STATE_IDLE_PENDING) {
+ if (mNextAlarmTime == 0) {
+ return;
+ }
+ long delay = mNextAlarmTime - SystemClock.elapsedRealtime();
+ if (delay < MIN_STATE_STEP_ALARM_CHANGE) {
+ return;
+ }
+ long newDelay = (long) (delay / mLastPreIdleFactor * mPreIdleFactor);
+ if (Math.abs(delay - newDelay) < MIN_STATE_STEP_ALARM_CHANGE) {
+ return;
+ }
+ scheduleAlarmLocked(newDelay, false);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ void maybeDoImmediateMaintenance() {
+ synchronized (this) {
+ if (mState == STATE_IDLE) {
+ long duration = SystemClock.elapsedRealtime() - mIdleStartTime;
+ /* Let's trgger a immediate maintenance,
+ * if it has been idle for a long time */
+ if (duration > mConstants.IDLE_TIMEOUT) {
+ scheduleAlarmLocked(0, false);
+ }
+ }
+ }
+ }
+
+ private boolean shouldUseIdleTimeoutFactorLocked() {
+ // exclude ACTIVE_REASON_MOTION, for exclude device in pocket case
+ if (mActiveReason == ACTIVE_REASON_MOTION) {
+ return false;
+ }
+ return true;
+ }
+
+ /** Must only be used in tests. */
+ @VisibleForTesting
+ void setIdleStartTimeForTest(long idleStartTime) {
+ synchronized (this) {
+ mIdleStartTime = idleStartTime;
+ }
+ }
+
void reportMaintenanceActivityIfNeededLocked() {
boolean active = mJobsActive;
if (active == mReportedMaintenanceActivity) {
@@ -2945,6 +3173,11 @@ public class DeviceIdleController extends SystemService
mHandler.sendMessage(msg);
}
+ @VisibleForTesting
+ long getNextAlarmTime() {
+ return mNextAlarmTime;
+ }
+
boolean isOpsInactiveLocked() {
return mActiveIdleOpCount <= 0 && !mJobsActive && !mAlarmsActive;
}
@@ -2994,6 +3227,7 @@ public class DeviceIdleController extends SystemService
scheduleReportActiveLocked(type, Process.myUid());
addEvent(EVENT_NORMAL, type);
}
+ mActiveReason = ACTIVE_REASON_MOTION;
mState = STATE_ACTIVE;
mInactiveTimeout = timeout;
mCurIdleBudget = 0;
@@ -3401,6 +3635,11 @@ public class DeviceIdleController extends SystemService
+ "and any [-d] is ignored");
pw.println(" motion");
pw.println(" Simulate a motion event to bring the device out of deep doze");
+ pw.println(" pre-idle-factor [0|1|2]");
+ pw.println(" Set a new factor to idle time before step to idle"
+ + "(inactive_to and idle_after_inactive_to)");
+ pw.println(" reset-pre-idle-factor");
+ pw.println(" Reset factor to idle time to default");
}
class Shell extends ShellCommand {
@@ -3571,6 +3810,7 @@ public class DeviceIdleController extends SystemService
}
}
if (becomeActive) {
+ mActiveReason = ACTIVE_REASON_FORCED;
becomeActiveLocked((arg == null ? "all" : arg) + "-disabled",
Process.myUid());
}
@@ -3820,6 +4060,52 @@ public class DeviceIdleController extends SystemService
Binder.restoreCallingIdentity(token);
}
}
+ } else if ("pre-idle-factor".equals(cmd)) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ synchronized (this) {
+ long token = Binder.clearCallingIdentity();
+ int ret = SET_IDLE_FACTOR_RESULT_UNINIT;
+ try {
+ String arg = shell.getNextArg();
+ boolean valid = false;
+ int mode = 0;
+ if (arg != null) {
+ mode = Integer.parseInt(arg);
+ ret = setPreIdleTimeoutMode(mode);
+ if (ret == SET_IDLE_FACTOR_RESULT_OK) {
+ pw.println("pre-idle-factor: " + mode);
+ valid = true;
+ } else if (ret == SET_IDLE_FACTOR_RESULT_NOT_SUPPORT) {
+ valid = true;
+ pw.println("Deep idle not supported");
+ } else if (ret == SET_IDLE_FACTOR_RESULT_IGNORED) {
+ valid = true;
+ pw.println("Idle timeout factor not changed");
+ }
+ }
+ if (!valid) {
+ pw.println("Unknown idle timeout factor: " + arg
+ + ",(error code: " + ret + ")");
+ }
+ } catch (NumberFormatException e) {
+ pw.println("Unknown idle timeout factor"
+ + ",(error code: " + ret + ")");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ } else if ("reset-pre-idle-factor".equals(cmd)) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ synchronized (this) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ resetPreIdleTimeoutMode();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
} else {
return shell.handleDefaultCommands(cmd);
}
@@ -4053,6 +4339,9 @@ public class DeviceIdleController extends SystemService
if (mAlarmsActive) {
pw.print(" mAlarmsActive="); pw.println(mAlarmsActive);
}
+ if (Math.abs(mPreIdleFactor - 1.0f) > MIN_PRE_IDLE_FACTOR_CHANGE) {
+ pw.print(" mPreIdleFactor="); pw.println(mPreIdleFactor);
+ }
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index add5e5feaa8a..b3a0f643ba4b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -46,6 +46,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.hardware.location.ActivityRecognitionHardware;
import android.location.Address;
import android.location.Criteria;
import android.location.GeocoderParams;
@@ -66,7 +67,6 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -87,11 +87,11 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.location.AbstractLocationProvider;
+import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
import com.android.server.location.GeofenceProxy;
@@ -246,7 +246,7 @@ public class LocationManagerService extends ILocationManager.Stub {
public LocationManagerService(Context context) {
super();
mContext = context;
- mHandler = BackgroundThread.getHandler();
+ mHandler = FgThread.getHandler();
// Let the package manager query which are the default location
// providers as they get certain permissions granted by default.
@@ -736,6 +736,25 @@ public class LocationManagerService extends ILocationManager.Stub {
Slog.d(TAG, "Unable to bind FLP Geofence proxy.");
}
+ // bind to hardware activity recognition
+ boolean activityRecognitionHardwareIsSupported = ActivityRecognitionHardware.isSupported();
+ ActivityRecognitionHardware activityRecognitionHardware = null;
+ if (activityRecognitionHardwareIsSupported) {
+ activityRecognitionHardware = ActivityRecognitionHardware.getInstance(mContext);
+ } else {
+ Slog.d(TAG, "Hardware Activity-Recognition not supported.");
+ }
+ ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind(
+ mContext,
+ activityRecognitionHardwareIsSupported,
+ activityRecognitionHardware,
+ com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
+ com.android.internal.R.string.config_activityRecognitionHardwarePackageName,
+ com.android.internal.R.array.config_locationProviderPackageNames);
+ if (proxy == null) {
+ Slog.d(TAG, "Unable to bind ActivityRecognitionProxy.");
+ }
+
String[] testProviderStrings = resources.getStringArray(
com.android.internal.R.array.config_testLocationProviders);
for (String testProviderString : testProviderStrings) {
@@ -954,7 +973,7 @@ public class LocationManagerService extends ILocationManager.Stub {
public void onReportLocation(Location location) {
// no security check necessary because this is coming from an internal-only interface
// move calls coming from below LMS onto a different thread to avoid deadlock
- runInternal(() -> {
+ mHandler.post(() -> {
synchronized (mLock) {
handleLocationChangedLocked(location, this);
}
@@ -965,7 +984,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void onReportLocation(List<Location> locations) {
// move calls coming from below LMS onto a different thread to avoid deadlock
- runInternal(() -> {
+ mHandler.post(() -> {
synchronized (mLock) {
LocationProvider gpsProvider = getLocationProviderLocked(GPS_PROVIDER);
if (gpsProvider == null || !gpsProvider.isUseableLocked()) {
@@ -991,7 +1010,7 @@ public class LocationManagerService extends ILocationManager.Stub {
@Override
public void onSetEnabled(boolean enabled) {
// move calls coming from below LMS onto a different thread to avoid deadlock
- runInternal(() -> {
+ mHandler.post(() -> {
synchronized (mLock) {
if (enabled == mEnabled) {
return;
@@ -1113,16 +1132,6 @@ public class LocationManagerService extends ILocationManager.Stub {
mUseable = false;
updateProviderUseableLocked(this);
}
-
- // binder transactions coming from below LMS (ie location providers) need to be moved onto
- // a different thread to avoid potential deadlock as code reenters the location providers
- private void runInternal(Runnable runnable) {
- if (Looper.myLooper() == mHandler.getLooper()) {
- runnable.run();
- } else {
- mHandler.post(runnable);
- }
- }
}
private class MockLocationProvider extends LocationProvider {
@@ -1278,7 +1287,11 @@ public class LocationManagerService extends ILocationManager.Stub {
// are high power (has a high power provider with an interval under a threshold).
for (UpdateRecord updateRecord : mUpdateRecords.values()) {
LocationProvider provider = getLocationProviderLocked(updateRecord.mProvider);
- if (provider == null || !provider.isUseableLocked()) {
+ if (provider == null) {
+ continue;
+ }
+ if (!provider.isUseableLocked()
+ && !updateRecord.mRealRequest.isLocationSettingsIgnored()) {
continue;
}
@@ -1960,14 +1973,22 @@ public class LocationManagerService extends ILocationManager.Stub {
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider.getName());
if (records != null) {
for (UpdateRecord record : records) {
- if (isCurrentProfileLocked(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
- // Sends a notification message to the receiver
- if (!record.mReceiver.callProviderEnabledLocked(provider.getName(), useable)) {
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<>();
- }
- deadReceivers.add(record.mReceiver);
+ if (!isCurrentProfileLocked(
+ UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
+ continue;
+ }
+
+ // requests that ignore location settings will never provider notifications
+ if (record.mRealRequest.isLocationSettingsIgnored()) {
+ continue;
+ }
+
+ // Sends a notification message to the receiver
+ if (!record.mReceiver.callProviderEnabledLocked(provider.getName(), useable)) {
+ if (deadReceivers == null) {
+ deadReceivers = new ArrayList<>();
}
+ deadReceivers.add(record.mReceiver);
}
}
}
@@ -2007,40 +2028,48 @@ public class LocationManagerService extends ILocationManager.Stub {
Binder.restoreCallingIdentity(identity);
}
- if (provider.isUseableLocked() && records != null && !records.isEmpty()) {
+ if (records != null && !records.isEmpty()) {
// initialize the low power mode to true and set to false if any of the records requires
providerRequest.lowPowerMode = true;
for (UpdateRecord record : records) {
- if (isCurrentProfileLocked(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
- if (checkLocationAccess(
- record.mReceiver.mIdentity.mPid,
- record.mReceiver.mIdentity.mUid,
- record.mReceiver.mIdentity.mPackageName,
- record.mReceiver.mAllowedResolutionLevel)) {
- LocationRequest locationRequest = record.mRealRequest;
- long interval = locationRequest.getInterval();
-
- if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
- if (!record.mIsForegroundUid) {
- interval = Math.max(interval, backgroundThrottleInterval);
- }
- if (interval != locationRequest.getInterval()) {
- locationRequest = new LocationRequest(locationRequest);
- locationRequest.setInterval(interval);
- }
- }
+ if (!isCurrentProfileLocked(
+ UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
+ continue;
+ }
+ if (!checkLocationAccess(
+ record.mReceiver.mIdentity.mPid,
+ record.mReceiver.mIdentity.mUid,
+ record.mReceiver.mIdentity.mPackageName,
+ record.mReceiver.mAllowedResolutionLevel)) {
+ continue;
+ }
+ if (!provider.isUseableLocked()
+ && !record.mRealRequest.isLocationSettingsIgnored()) {
+ continue;
+ }
- record.mRequest = locationRequest;
- providerRequest.locationRequests.add(locationRequest);
- if (!locationRequest.isLowPowerMode()) {
- providerRequest.lowPowerMode = false;
- }
- if (interval < providerRequest.interval) {
- providerRequest.reportLocation = true;
- providerRequest.interval = interval;
- }
+ LocationRequest locationRequest = record.mRealRequest;
+ long interval = locationRequest.getInterval();
+
+ if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+ if (!record.mIsForegroundUid) {
+ interval = Math.max(interval, backgroundThrottleInterval);
+ }
+ if (interval != locationRequest.getInterval()) {
+ locationRequest = new LocationRequest(locationRequest);
+ locationRequest.setInterval(interval);
}
}
+
+ record.mRequest = locationRequest;
+ providerRequest.locationRequests.add(locationRequest);
+ if (!locationRequest.isLowPowerMode()) {
+ providerRequest.lowPowerMode = false;
+ }
+ if (interval < providerRequest.interval) {
+ providerRequest.reportLocation = true;
+ providerRequest.interval = interval;
+ }
}
if (providerRequest.reportLocation) {
@@ -2307,6 +2336,10 @@ public class LocationManagerService extends ILocationManager.Stub {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, null);
}
+ if (request.isLocationSettingsIgnored()) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.WRITE_SECURE_SETTINGS, null);
+ }
boolean callerHasLocationHardwarePermission =
mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
== PERMISSION_GRANTED;
@@ -2376,12 +2409,14 @@ public class LocationManagerService extends ILocationManager.Stub {
oldRecord.disposeLocked(false);
}
- if (provider.isUseableLocked()) {
- applyRequirementsLocked(name);
- } else {
- // Notify the listener that updates are currently disabled
+ if (!provider.isUseableLocked() && !request.isLocationSettingsIgnored()) {
+ // Notify the listener that updates are currently disabled - but only if the request
+ // does not ignore location settings
receiver.callProviderEnabledLocked(name, false);
}
+
+ applyRequirementsLocked(name);
+
// Update the monitoring here just in case multiple location requests were added to the
// same receiver (this request may be high power and the initial might not have been).
receiver.updateMonitoring(true);
@@ -2981,26 +3016,29 @@ public class LocationManagerService extends ILocationManager.Stub {
return;
}
- if (!provider.isPassiveLocked()) {
- // notify passive provider of the new location
- mPassiveProvider.updateLocation(location);
+ // only notify passive provider and update last location for locations that come from
+ // useable providers
+ if (provider.isUseableLocked()) {
+ if (!provider.isPassiveLocked()) {
+ mPassiveProvider.updateLocation(location);
+ }
}
if (D) Log.d(TAG, "incoming location: " + location);
long now = SystemClock.elapsedRealtime();
- updateLastLocationLocked(location, provider.getName());
- // mLastLocation should have been updated from the updateLastLocationLocked call above.
- Location lastLocation = mLastLocation.get(provider.getName());
- if (lastLocation == null) {
- Log.e(TAG, "handleLocationChangedLocked() updateLastLocation failed");
- return;
+ if (provider.isUseableLocked()) {
+ updateLastLocationLocked(location, provider.getName());
}
// Update last known coarse interval location if enough time has passed.
- Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider.getName());
+ Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(
+ provider.getName());
if (lastLocationCoarseInterval == null) {
lastLocationCoarseInterval = new Location(location);
- mLastLocationCoarseInterval.put(provider.getName(), lastLocationCoarseInterval);
+
+ if (provider.isUseableLocked()) {
+ mLastLocationCoarseInterval.put(provider.getName(), lastLocationCoarseInterval);
+ }
}
long timeDiffNanos = location.getElapsedRealtimeNanos()
- lastLocationCoarseInterval.getElapsedRealtimeNanos();
@@ -3031,6 +3069,10 @@ public class LocationManagerService extends ILocationManager.Stub {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
+ if (!provider.isUseableLocked() && !r.mRealRequest.isLocationSettingsIgnored()) {
+ continue;
+ }
+
int receiverUserId = UserHandle.getUserId(receiver.mIdentity.mUid);
if (!isCurrentProfileLocked(receiverUserId)
&& !isLocationProviderLocked(receiver.mIdentity.mUid)) {
@@ -3066,7 +3108,7 @@ public class LocationManagerService extends ILocationManager.Stub {
if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
notifyLocation = coarseLocation; // use coarse location
} else {
- notifyLocation = lastLocation; // use fine location
+ notifyLocation = location; // use fine location
}
if (notifyLocation != null) {
Location lastLoc = r.mLastFixBroadcast;
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index cee98c10c7f7..4a8706e7090f 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -122,6 +122,10 @@ public class LooperStatsService extends Binder {
"exception_count"));
pw.println(header);
for (LooperStats.ExportedEntry entry : entries) {
+ if (entry.messageName.startsWith(LooperStats.DEBUG_ENTRY_PREFIX)) {
+ // Do not dump debug entries.
+ continue;
+ }
pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
packageMap.mapUid(entry.workSourceUid),
entry.threadName,
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index c84389bcee31..2ce2f8d1a152 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -46,7 +46,9 @@ import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.InetAddresses;
import android.net.INetd;
+import android.net.INetdUnsolicitedEventListener;
import android.net.INetworkManagementEventObserver;
import android.net.ITetheringStatsProvider;
import android.net.InterfaceConfiguration;
@@ -205,6 +207,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private INetd mNetdService;
+ private final NetdUnsolicitedEventListener mNetdUnsolicitedEventListener;
+
private IBatteryStats mBatteryStats;
private final Thread mThread;
@@ -322,6 +326,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub
mDaemonHandler = new Handler(FgThread.get().getLooper());
+ mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();
+
// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
@@ -340,6 +346,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
mFgHandler = null;
mThread = null;
mServices = null;
+ mNetdUnsolicitedEventListener = null;
}
static NetworkManagementService create(Context context, String socket, SystemServices services)
@@ -446,7 +453,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub
// our sanity-checking state.
mActiveAlerts.remove(iface);
mActiveQuotas.remove(iface);
-
invokeForAllObservers(o -> o.interfaceRemoved(iface));
}
@@ -552,7 +558,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
return;
}
// No current code examines the interface parameter in a global alert. Just pass null.
- notifyLimitReached(LIMIT_GLOBAL_ALERT, null);
+ mDaemonHandler.post(() -> notifyLimitReached(LIMIT_GLOBAL_ALERT, null));
}
}
@@ -583,6 +589,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private void connectNativeNetdService() {
mNetdService = mServices.getNetd();
+ try {
+ mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);
+ if (DBG) Slog.d(TAG, "Register unsolicited event listener");
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "Failed to set Netd unsolicited event listener " + e);
+ }
}
/**
@@ -709,14 +721,96 @@ public class NetworkManagementService extends INetworkManagementService.Stub
/**
* Notify our observers of a route change.
*/
- private void notifyRouteChange(String action, RouteInfo route) {
- if (action.equals("updated")) {
+ private void notifyRouteChange(boolean updated, RouteInfo route) {
+ if (updated) {
invokeForAllObservers(o -> o.routeUpdated(route));
} else {
invokeForAllObservers(o -> o.routeRemoved(route));
}
}
+ private class NetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {
+ @Override
+ public void onInterfaceClassActivityChanged(boolean isActive,
+ int label, long timestamp, int uid) throws RemoteException {
+ final long timestampNanos;
+ if (timestamp <= 0) {
+ timestampNanos = SystemClock.elapsedRealtimeNanos();
+ } else {
+ timestampNanos = timestamp;
+ }
+ mDaemonHandler.post(() -> notifyInterfaceClassActivity(label,
+ isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
+ : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ timestampNanos, uid, false));
+ }
+
+ @Override
+ public void onQuotaLimitReached(String alertName, String ifName)
+ throws RemoteException {
+ mDaemonHandler.post(() -> notifyLimitReached(alertName, ifName));
+ }
+
+ @Override
+ public void onInterfaceDnsServerInfo(String ifName,
+ long lifetime, String[] servers) throws RemoteException {
+ mDaemonHandler.post(() -> notifyInterfaceDnsServerInfo(ifName, lifetime, servers));
+ }
+
+ @Override
+ public void onInterfaceAddressUpdated(String addr,
+ String ifName, int flags, int scope) throws RemoteException {
+ final LinkAddress address = new LinkAddress(addr, flags, scope);
+ mDaemonHandler.post(() -> notifyAddressUpdated(ifName, address));
+ }
+
+ @Override
+ public void onInterfaceAddressRemoved(String addr,
+ String ifName, int flags, int scope) throws RemoteException {
+ final LinkAddress address = new LinkAddress(addr, flags, scope);
+ mDaemonHandler.post(() -> notifyAddressRemoved(ifName, address));
+ }
+
+ @Override
+ public void onInterfaceAdded(String ifName) throws RemoteException {
+ mDaemonHandler.post(() -> notifyInterfaceAdded(ifName));
+ }
+
+ @Override
+ public void onInterfaceRemoved(String ifName) throws RemoteException {
+ mDaemonHandler.post(() -> notifyInterfaceRemoved(ifName));
+ }
+
+ @Override
+ public void onInterfaceChanged(String ifName, boolean up)
+ throws RemoteException {
+ mDaemonHandler.post(() -> notifyInterfaceStatusChanged(ifName, up));
+ }
+
+ @Override
+ public void onInterfaceLinkStateChanged(String ifName, boolean up)
+ throws RemoteException {
+ mDaemonHandler.post(() -> notifyInterfaceLinkStateChanged(ifName, up));
+ }
+
+ @Override
+ public void onRouteChanged(boolean updated,
+ String route, String gateway, String ifName) throws RemoteException {
+ final RouteInfo processRoute = new RouteInfo(new IpPrefix(route),
+ ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway),
+ ifName);
+ mDaemonHandler.post(() -> notifyRouteChange(updated, processRoute));
+ }
+
+ @Override
+ public void onStrictCleartextDetected(int uid, String hex) throws RemoteException {
+ // Don't need to post to mDaemonHandler because the only thing
+ // that notifyCleartextNetwork does is post to a handler
+ ActivityManager.getService().notifyCleartextNetwork(uid,
+ HexDump.hexStringToByteArray(hex));
+ }
+ }
+
//
// Netd Callback handling
//
@@ -904,7 +998,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
InetAddress gateway = null;
if (via != null) gateway = InetAddress.parseNumericAddress(via);
RouteInfo route = new RouteInfo(new IpPrefix(cooked[3]), gateway, dev);
- notifyRouteChange(cooked[2], route);
+ notifyRouteChange(cooked[2].equals("updated"), route);
return true;
} catch (IllegalArgumentException e) {}
}
@@ -1367,13 +1461,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub
if (ConnectivityManager.isNetworkTypeMobile(type)) {
mNetworkActive = false;
}
- mDaemonHandler.post(new Runnable() {
- @Override public void run() {
- notifyInterfaceClassActivity(type,
- DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
- SystemClock.elapsedRealtimeNanos(), -1, false);
- }
- });
+ mDaemonHandler.post(() -> notifyInterfaceClassActivity(type,
+ DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ SystemClock.elapsedRealtimeNanos(), -1, false));
}
}
@@ -1396,13 +1486,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub
throw new IllegalStateException(e);
}
mActiveIdleTimers.remove(iface);
- mDaemonHandler.post(new Runnable() {
- @Override public void run() {
- notifyInterfaceClassActivity(params.type,
- DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
- SystemClock.elapsedRealtimeNanos(), -1, false);
- }
- });
+ mDaemonHandler.post(() -> notifyInterfaceClassActivity(params.type,
+ DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
+ SystemClock.elapsedRealtimeNanos(), -1, false));
}
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
index 1e9a00743a8b..190fff1f669c 100644
--- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
+++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
@@ -31,6 +31,19 @@ public interface PersistentDataBlockManagerInternal {
*/
byte[] getFrpCredentialHandle();
+ /** Stores the data used to enable the Test Harness Mode after factory-resetting. */
+ void setTestHarnessModeData(byte[] data);
+
+ /**
+ * Retrieves the data used to place the device into Test Harness Mode.
+ *
+ * @throws IllegalStateException if the underlying storage is corrupt or inaccessible.
+ */
+ byte[] getTestHarnessModeData();
+
+ /** Clear out the Test Harness Mode data. */
+ void clearTestHarnessModeData();
+
/** Update the OEM unlock enabled bit, bypassing user restriction checks. */
void forceOemUnlockEnabled(boolean enabled);
}
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 21093b9f6f88..bd5ad960a886 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static com.android.internal.util.Preconditions.checkArgument;
+
import android.Manifest;
import android.app.ActivityManager;
import android.content.Context;
@@ -28,12 +30,10 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.service.persistentdata.IPersistentDataBlockService;
import android.service.persistentdata.PersistentDataBlockManager;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
import libcore.io.IoUtils;
@@ -65,6 +65,40 @@ import java.util.concurrent.TimeUnit;
*
* Clients can read any number of bytes from the currently written block up to its total size by
* invoking {@link IPersistentDataBlockService#read}
+ *
+ * The persistent data block is currently laid out as follows:
+ * | ---------BEGINNING OF PARTITION-------------|
+ * | Partition digest (32 bytes) |
+ * | --------------------------------------------|
+ * | PARTITION_TYPE_MARKER (4 bytes) |
+ * | --------------------------------------------|
+ * | FRP data block length (4 bytes) |
+ * | --------------------------------------------|
+ * | FRP data (variable length) |
+ * | --------------------------------------------|
+ * | ... |
+ * | --------------------------------------------|
+ * | Test mode data block (10000 bytes) |
+ * | --------------------------------------------|
+ * | | Test mode data length (4 bytes) |
+ * | --------------------------------------------|
+ * | | Test mode data (variable length) |
+ * | | ... |
+ * | --------------------------------------------|
+ * | FRP credential handle block (1000 bytes) |
+ * | --------------------------------------------|
+ * | | FRP credential handle length (4 bytes)|
+ * | --------------------------------------------|
+ * | | FRP credential handle (variable len) |
+ * | | ... |
+ * | --------------------------------------------|
+ * | OEM Unlock bit (1 byte) |
+ * | ---------END OF PARTITION-------------------|
+ *
+ * TODO: now that the persistent partition contains several blocks, next time someone wants a new
+ * block, we should look at adding more generic block definitions and get rid of the various raw
+ * XXX_RESERVED_SIZE and XXX_DATA_SIZE constants. That will ensure the code is easier to maintain
+ * and less likely to introduce out-of-bounds read/write.
*/
public class PersistentDataBlockService extends SystemService {
private static final String TAG = PersistentDataBlockService.class.getSimpleName();
@@ -73,10 +107,16 @@ public class PersistentDataBlockService extends SystemService {
private static final int HEADER_SIZE = 8;
// Magic number to mark block device as adhering to the format consumed by this service
private static final int PARTITION_TYPE_MARKER = 0x19901873;
- /** Size of the block reserved for FPR credential, including 4 bytes for the size header. */
+ /** Size of the block reserved for FRP credential, including 4 bytes for the size header. */
private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
/** Maximum size of the FRP credential handle that can be stored. */
private static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
+ /**
+ * Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header.
+ */
+ private static final int TEST_MODE_RESERVED_SIZE = 10000;
+ /** Maximum size of the Test Harness Mode data that can be stored. */
+ private static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
// Limit to 100k as blocks larger than this might cause strain on Binder.
private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
@@ -221,6 +261,14 @@ public class PersistentDataBlockService extends SystemService {
return mBlockDeviceSize;
}
+ private long getFrpCredentialDataOffset() {
+ return getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE;
+ }
+
+ private long getTestHarnessModeDataOffset() {
+ return getFrpCredentialDataOffset() - TEST_MODE_RESERVED_SIZE;
+ }
+
private boolean enforceChecksumValidity() {
byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
@@ -383,7 +431,7 @@ public class PersistentDataBlockService extends SystemService {
private long doGetMaximumDataBlockSize() {
long actualSize = getBlockDeviceSize() - HEADER_SIZE - DIGEST_SIZE_BYTES
- - FRP_CREDENTIAL_RESERVED_SIZE - 1;
+ - TEST_MODE_RESERVED_SIZE - FRP_CREDENTIAL_RESERVED_SIZE - 1;
return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
}
@@ -391,6 +439,13 @@ public class PersistentDataBlockService extends SystemService {
private native int nativeWipe(String path);
private final IBinder mService = new IPersistentDataBlockService.Stub() {
+
+ /**
+ * Write the data to the persistent data block.
+ *
+ * @return a positive integer of the number of bytes that were written if successful,
+ * otherwise a negative integer indicating there was a problem
+ */
@Override
public int write(byte[] data) throws RemoteException {
enforceUid(Binder.getCallingUid());
@@ -597,12 +652,51 @@ public class PersistentDataBlockService extends SystemService {
@Override
public void setFrpCredentialHandle(byte[] handle) {
- Preconditions.checkArgument(handle == null || handle.length > 0,
- "handle must be null or non-empty");
- Preconditions.checkArgument(handle == null
- || handle.length <= MAX_FRP_CREDENTIAL_HANDLE_SIZE,
- "handle must not be longer than " + MAX_FRP_CREDENTIAL_HANDLE_SIZE);
+ writeInternal(handle, getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE);
+ }
+ @Override
+ public byte[] getFrpCredentialHandle() {
+ return readInternal(getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE);
+ }
+
+ @Override
+ public void setTestHarnessModeData(byte[] data) {
+ writeInternal(data, getTestHarnessModeDataOffset(), MAX_TEST_MODE_DATA_SIZE);
+ }
+
+ @Override
+ public byte[] getTestHarnessModeData() {
+ byte[] data = readInternal(getTestHarnessModeDataOffset(), MAX_TEST_MODE_DATA_SIZE);
+ if (data == null) {
+ return new byte[0];
+ }
+ return data;
+ }
+
+ @Override
+ public void clearTestHarnessModeData() {
+ int size = Math.min(MAX_TEST_MODE_DATA_SIZE, getTestHarnessModeData().length) + 4;
+ writeDataBuffer(getTestHarnessModeDataOffset(), ByteBuffer.allocate(size));
+ }
+
+ private void writeInternal(byte[] data, long offset, int dataLength) {
+ checkArgument(data == null || data.length > 0, "data must be null or non-empty");
+ checkArgument(
+ data == null || data.length <= dataLength,
+ "data must not be longer than " + dataLength);
+
+ ByteBuffer dataBuffer = ByteBuffer.allocate(dataLength + 4);
+ dataBuffer.putInt(data == null ? 0 : data.length);
+ if (data != null) {
+ dataBuffer.put(data);
+ }
+ dataBuffer.flip();
+
+ writeDataBuffer(offset, dataBuffer);
+ }
+
+ private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
FileOutputStream outputStream;
try {
outputStream = new FileOutputStream(new File(mDataBlockFile));
@@ -610,25 +704,15 @@ public class PersistentDataBlockService extends SystemService {
Slog.e(TAG, "partition not available", e);
return;
}
-
- ByteBuffer data = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
- data.putInt(handle == null ? 0 : handle.length);
- if (handle != null) {
- data.put(handle);
- }
- data.flip();
-
synchronized (mLock) {
if (!mIsWritable) {
IoUtils.closeQuietly(outputStream);
return;
}
-
try {
FileChannel channel = outputStream.getChannel();
-
- channel.position(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
- channel.write(data);
+ channel.position(offset);
+ channel.write(dataBuffer);
outputStream.flush();
} catch (IOException e) {
Slog.e(TAG, "unable to access persistent partition", e);
@@ -641,8 +725,7 @@ public class PersistentDataBlockService extends SystemService {
}
}
- @Override
- public byte[] getFrpCredentialHandle() {
+ private byte[] readInternal(long offset, int maxLength) {
if (!enforceChecksumValidity()) {
throw new IllegalStateException("invalid checksum");
}
@@ -652,14 +735,14 @@ public class PersistentDataBlockService extends SystemService {
inputStream = new DataInputStream(
new FileInputStream(new File(mDataBlockFile)));
} catch (FileNotFoundException e) {
- throw new IllegalStateException("frp partition not available");
+ throw new IllegalStateException("persistent partition not available");
}
try {
synchronized (mLock) {
- inputStream.skip(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
+ inputStream.skip(offset);
int length = inputStream.readInt();
- if (length <= 0 || length > MAX_FRP_CREDENTIAL_HANDLE_SIZE) {
+ if (length <= 0 || length > maxLength) {
return null;
}
byte[] bytes = new byte[length];
@@ -667,7 +750,7 @@ public class PersistentDataBlockService extends SystemService {
return bytes;
}
} catch (IOException e) {
- throw new IllegalStateException("frp handle not readable", e);
+ throw new IllegalStateException("persistent partition not readable", e);
} finally {
IoUtils.closeQuietly(inputStream);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7731c04a94b8..9d810cd8f3ca 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1706,8 +1706,8 @@ class StorageManagerService extends IStorageManager.Stub
int uid, String packageName, int[] ops) {
long maxTime = 0;
final List<AppOpsManager.PackageOps> pkgs = manager.getOpsForPackage(uid, packageName, ops);
- for (AppOpsManager.PackageOps pkg : CollectionUtils.defeatNullable(pkgs)) {
- for (AppOpsManager.OpEntry op : CollectionUtils.defeatNullable(pkg.getOps())) {
+ for (AppOpsManager.PackageOps pkg : CollectionUtils.emptyIfNull(pkgs)) {
+ for (AppOpsManager.OpEntry op : CollectionUtils.emptyIfNull(pkg.getOps())) {
maxTime = Math.max(maxTime, op.getLastAccessTime());
}
}
diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java
index fe27c495ef55..fd402cc08c0c 100644
--- a/services/core/java/com/android/server/am/AppCompactor.java
+++ b/services/core/java/com/android/server/am/AppCompactor.java
@@ -64,7 +64,7 @@ public final class AppCompactor {
final private String COMPACT_ACTION_FILE = "file";
final private String COMPACT_ACTION_ANON = "anon";
- final private String COMPACT_ACTION_FULL = "full";
+ final private String COMPACT_ACTION_FULL = "all";
final private String compactActionSome;
final private String compactActionFull;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 65aacdcb73e0..353749f211c1 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -45,6 +45,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.util.EventLog;
import android.util.Slog;
+import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -157,6 +158,9 @@ public final class BroadcastQueue {
static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
+ // log latency metrics for ordered broadcasts during BOOT_COMPLETED processing
+ boolean mLogLatencyMetrics = true;
+
final BroadcastHandler mHandler;
private final class BroadcastHandler extends Handler {
@@ -941,6 +945,12 @@ public final class BroadcastQueue {
// adjustments.
mService.updateOomAdjLocked();
}
+
+ // when we have no more ordered broadcast on this queue, stop logging
+ if (mService.mUserController.mBootCompleted && mLogLatencyMetrics) {
+ mLogLatencyMetrics = false;
+ }
+
return;
}
r = mOrderedBroadcasts.get(0);
@@ -1036,6 +1046,13 @@ public final class BroadcastQueue {
if (recIdx == 0) {
r.dispatchTime = r.receiverTime;
r.dispatchClockTime = System.currentTimeMillis();
+
+ if (mLogLatencyMetrics) {
+ StatsLog.write(
+ StatsLog.BROADCAST_DISPATCH_LATENCY_REPORTED,
+ r.dispatchClockTime - r.enqueueClockTime);
+ }
+
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index bcce05289206..c981e6885a9e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -240,6 +240,8 @@ class UserController implements Handler.Callback {
private final LockPatternUtils mLockPatternUtils;
+ volatile boolean mBootCompleted;
+
UserController(ActivityManagerService service) {
this(new Injector(service));
}
@@ -567,6 +569,7 @@ class UserController implements Handler.Callback {
Bundle extras, boolean ordered, boolean sticky, int sendingUser)
throws RemoteException {
Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId);
+ mBootCompleted = true;
}
}, 0, null, null,
new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index f60d6b06f451..27edbbf4f2d5 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -47,6 +47,7 @@ import android.service.attention.IAttentionService;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.StatsLog;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -73,7 +74,6 @@ public class AttentionManagerService extends SystemService {
private final Context mContext;
private final PowerManager mPowerManager;
- private final ActivityManager mActivityManager;
private final Object mLock;
@GuardedBy("mLock")
private final SparseArray<UserState> mUserStates = new SparseArray<>();
@@ -85,7 +85,6 @@ public class AttentionManagerService extends SystemService {
super(context);
mContext = Preconditions.checkNotNull(context);
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mLock = new Object();
mAttentionHandler = new AttentionHandler();
}
@@ -96,7 +95,7 @@ public class AttentionManagerService extends SystemService {
}
@Override
- public void onStopUser(int userId) {
+ public void onSwitchUser(int userId) {
cancelAndUnbindLocked(peekUserStateLocked(userId),
AttentionService.ATTENTION_FAILURE_UNKNOWN);
}
@@ -178,11 +177,16 @@ public class AttentionManagerService extends SystemService {
userState.mAttentionCheckCache = new AttentionCheckCache(
SystemClock.uptimeMillis(), result,
timestamp);
+
+ StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
+ result);
}
@Override
public void onFailure(int requestCode, int error) {
callback.onFailure(requestCode, error);
+ StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
+ error);
}
@Override
@@ -201,11 +205,20 @@ public class AttentionManagerService extends SystemService {
/** Cancels the specified attention check. */
public void cancelAttentionCheck(int requestCode) {
- final UserState userState = getOrCreateCurrentUserStateLocked();
- try {
- userState.mService.cancelAttentionCheck(requestCode);
- } catch (RemoteException e) {
- Slog.e(LOG_TAG, "Cannot call into the AttentionService");
+ synchronized (mLock) {
+ final UserState userState = getOrCreateCurrentUserStateLocked();
+ if (userState.mService == null) {
+ if (userState.mPendingAttentionCheck != null
+ && userState.mPendingAttentionCheck.mRequestCode == requestCode) {
+ userState.mPendingAttentionCheck = null;
+ }
+ return;
+ }
+ try {
+ userState.mService.cancelAttentionCheck(requestCode);
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Cannot call into the AttentionService");
+ }
}
}
@@ -224,7 +237,7 @@ public class AttentionManagerService extends SystemService {
@GuardedBy("mLock")
private UserState getOrCreateCurrentUserStateLocked() {
- return getOrCreateUserStateLocked(mActivityManager.getCurrentUser());
+ return getOrCreateUserStateLocked(ActivityManager.getCurrentUser());
}
@GuardedBy("mLock")
@@ -239,7 +252,7 @@ public class AttentionManagerService extends SystemService {
@GuardedBy("mLock")
UserState peekCurrentUserStateLocked() {
- return peekUserStateLocked(mActivityManager.getCurrentUser());
+ return peekUserStateLocked(ActivityManager.getCurrentUser());
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 11299b690bb1..de389bc3aa01 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1223,11 +1223,13 @@ public class AudioService extends IAudioService.Stub
private void checkMuteAffectedStreams() {
// any stream with a min level > 0 is not muteable by definition
- // STREAM_VOICE_CALL can be muted by applications that has the the MODIFY_PHONE_STATE permission.
+ // STREAM_VOICE_CALL and STREAM_BLUETOOTH_SCO can be muted by applications
+ // that has the the MODIFY_PHONE_STATE permission.
for (int i = 0; i < mStreamStates.length; i++) {
final VolumeStreamState vss = mStreamStates[i];
if (vss.mIndexMin > 0 &&
- vss.mStreamType != AudioSystem.STREAM_VOICE_CALL) {
+ (vss.mStreamType != AudioSystem.STREAM_VOICE_CALL &&
+ vss.mStreamType != AudioSystem.STREAM_BLUETOOTH_SCO)) {
mMuteAffectedStreams &= ~(1 << vss.mStreamType);
}
}
@@ -1711,10 +1713,11 @@ public class AudioService extends IAudioService.Stub
return;
}
- // If adjust is mute and the stream is STREAM_VOICE_CALL, make sure
+ // If adjust is mute and the stream is STREAM_VOICE_CALL or STREAM_BLUETOOTH_SCO, make sure
// that the calling app have the MODIFY_PHONE_STATE permission.
if (isMuteAdjust &&
- streamType == AudioSystem.STREAM_VOICE_CALL &&
+ (streamType == AudioSystem.STREAM_VOICE_CALL ||
+ streamType == AudioSystem.STREAM_BLUETOOTH_SCO) &&
mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
@@ -2038,12 +2041,14 @@ public class AudioService extends IAudioService.Stub
+ " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage);
return;
}
- if ((streamType == AudioManager.STREAM_VOICE_CALL) &&
+ if ((streamType == AudioManager.STREAM_VOICE_CALL ||
+ streamType == AudioManager.STREAM_BLUETOOTH_SCO) &&
(index == 0) &&
(mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED)) {
- Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without"
+ Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL or"
+ + " STREAM_BLUETOOTH_SCO and index 0 without"
+ " MODIFY_PHONE_STATE callingPackage=" + callingPackage);
return;
}
@@ -4746,12 +4751,18 @@ public class AudioService extends IAudioService.Stub
private int getA2dpCodec(BluetoothDevice device) {
synchronized (mA2dpAvrcpLock) {
- if (mA2dp != null) {
- BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device);
- BluetoothCodecConfig btCodecConfig = btCodecStatus.getCodecConfig();
- return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
+ if (mA2dp == null) {
+ return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ }
+ BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device);
+ if (btCodecStatus == null) {
+ return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ }
+ BluetoothCodecConfig btCodecConfig = btCodecStatus.getCodecConfig();
+ if (btCodecConfig == null) {
+ return AudioSystem.AUDIO_FORMAT_DEFAULT;
}
- return AudioSystem.AUDIO_FORMAT_DEFAULT;
+ return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 602aedbc2d00..c72c9ddf3f7a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -60,7 +60,6 @@ import android.net.NetworkMisc;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.UidRange;
-import android.net.Uri;
import android.net.VpnService;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
@@ -71,7 +70,6 @@ import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
-import android.os.PatternMatcher;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -100,6 +98,8 @@ import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -121,8 +121,6 @@ import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
-import libcore.io.IoUtils;
-
/**
* @hide
*/
@@ -346,11 +344,18 @@ public class Vpn {
*
* @return {@code true} if VPN lockdown is enabled.
*/
- public boolean getLockdown() {
+ public synchronized boolean getLockdown() {
return mLockdown;
}
/**
+ * Returns whether VPN is configured as always-on.
+ */
+ public synchronized boolean getAlwaysOn() {
+ return mAlwaysOn;
+ }
+
+ /**
* Checks if a VPN app supports always-on mode.
*
* In order to support the always-on feature, an app has to
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 9223739406ee..3a58160cae8d 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -28,6 +28,7 @@ import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.UserIdInt;
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -51,6 +52,7 @@ import android.provider.Settings.Secure;
import android.provider.Settings.System;
import android.util.MathUtils;
import android.util.Slog;
+import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
@@ -866,6 +868,12 @@ public final class ColorDisplayService extends SystemService {
if (mDisplayWhiteBalanceListener != null && oldActivated != activated) {
mDisplayWhiteBalanceListener.onDisplayWhiteBalanceStatusChanged(activated);
}
+
+ // If disabled, clear the tint. If enabled, do nothing more here and let the next
+ // temperature update set the correct tint.
+ if (!activated) {
+ applyTint(mDisplayWhiteBalanceTintController, false);
+ }
}
private boolean isDisplayWhiteBalanceSettingEnabled() {
@@ -878,6 +886,21 @@ public final class ColorDisplayService extends SystemService {
return dtm.isDeviceColorManaged();
}
+ private int getTransformCapabilitiesInternal() {
+ int availabilityFlags = ColorDisplayManager.CAPABILITY_NONE;
+ if (SurfaceControl.getProtectedContentSupport()) {
+ availabilityFlags |= ColorDisplayManager.CAPABILITY_PROTECTED_CONTENT;
+ }
+ final Resources res = getContext().getResources();
+ if (res.getBoolean(R.bool.config_setColorTransformAccelerated)) {
+ availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_GLOBAL;
+ }
+ if (res.getBoolean(R.bool.config_setColorTransformAcceleratedPerLayer)) {
+ availabilityFlags |= ColorDisplayManager.CAPABILITY_HARDWARE_ACCELERATION_PER_APP;
+ }
+ return availabilityFlags;
+ }
+
/**
* Returns the last time the night display transform activation state was changed, or {@link
* LocalDateTime#MIN} if night display has never been activated.
@@ -1226,10 +1249,10 @@ public final class ColorDisplayService extends SystemService {
* Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and
* invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed.
*/
- public boolean attachColorTransformController(String packageName, int uid,
+ public boolean attachColorTransformController(String packageName, @UserIdInt int userId,
WeakReference<ColorTransformController> controller) {
return mAppSaturationController
- .addColorTransformController(packageName, uid, controller);
+ .addColorTransformController(packageName, userId, controller);
}
}
@@ -1318,6 +1341,18 @@ public final class ColorDisplayService extends SystemService {
}
}
+ public int getTransformCapabilities() {
+ getContext().enforceCallingPermission(
+ Manifest.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS,
+ "Permission required to query transform capabilities");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getTransformCapabilitiesInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 63214ed651b9..cac1a95454fa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -268,7 +268,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
mTvSystemAudioModeSupport = false;
// Record the last state of System Audio Control before going to standby
synchronized (mLock) {
- mService.writeStringSetting(
+ mService.writeStringSystemProperty(
Constants.PROPERTY_LAST_SYSTEM_AUDIO_CONTROL,
mSystemAudioActivated ? "true" : "false");
}
@@ -330,7 +330,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
@ServiceThreadOnly
protected void setPreferredAddress(int addr) {
assertRunOnServiceThread();
- mService.writeStringSetting(
+ mService.writeStringSystemProperty(
Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, String.valueOf(addr));
}
@@ -469,7 +469,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
assertRunOnServiceThread();
removeAction(ArcInitiationActionFromAvr.class);
- if (!mService.readBooleanSetting(Constants.PROPERTY_ARC_SUPPORT, true)) {
+ if (!mService.readBooleanSystemProperty(Constants.PROPERTY_ARC_SUPPORT, true)) {
mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
} else if (!isDirectConnectToTv()) {
HdmiLogger.debug("AVR device is not directly connected with TV");
@@ -829,7 +829,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
boolean currentMuteStatus =
mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
if (currentMuteStatus == newSystemAudioMode) {
- if (mService.readBooleanSetting(
+ if (mService.readBooleanSystemProperty(
Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true)
|| newSystemAudioMode) {
mService.getAudioManager()
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 7a0c27906122..ef7d24159009 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -100,7 +100,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
@ServiceThreadOnly
protected void setPreferredAddress(int addr) {
assertRunOnServiceThread();
- mService.writeStringSetting(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
+ mService.writeStringSystemProperty(Constants.PROPERTY_PREFERRED_ADDRESS_PLAYBACK,
String.valueOf(addr));
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 46219d5cbe8a..f3a1e46bc1e4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -657,9 +657,13 @@ public class HdmiControlService extends SystemService {
Global.putInt(cr, key, toInt(value));
}
- void writeStringSetting(String key, String value) {
- ContentResolver cr = getContext().getContentResolver();
- Global.putString(cr, key, value);
+ void writeStringSystemProperty(String key, String value) {
+ SystemProperties.set(key, value);
+ }
+
+ @VisibleForTesting
+ boolean readBooleanSystemProperty(String key, boolean defVal) {
+ return SystemProperties.getBoolean(key, defVal);
}
private void initializeCec(int initiatedBy) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index d4b8eb2db007..944a95dda99b 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -18,7 +18,6 @@ package com.android.server.inputmethod;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
-import android.content.ComponentName;
import android.view.inputmethod.InputMethodInfo;
import com.android.server.LocalServices;
@@ -42,11 +41,6 @@ public abstract class InputMethodManagerInternal {
public abstract void hideCurrentInputMethod();
/**
- * Switches to VR InputMethod defined in the packageName of {@param componentName}.
- */
- public abstract void startVrInputMethodNoCheck(ComponentName componentName);
-
- /**
* Returns the list of installed input methods for the specified user.
*
* @param userId The user ID to be queried.
@@ -76,10 +70,6 @@ public abstract class InputMethodManagerInternal {
}
@Override
- public void startVrInputMethodNoCheck(ComponentName componentName) {
- }
-
- @Override
public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
return Collections.emptyList();
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 2d197bb4da96..eca371a78750 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -91,8 +91,6 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
-import android.service.vr.IVrManager;
-import android.service.vr.IVrStateCallbacks;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
@@ -203,7 +201,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
static final int MSG_CREATE_SESSION = 1050;
static final int MSG_START_INPUT = 2000;
- static final int MSG_START_VR_INPUT = 2010;
static final int MSG_UNBIND_CLIENT = 3000;
static final int MSG_BIND_CLIENT = 3010;
@@ -381,28 +378,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- /**
- * VR state callback.
- * Listens for when VR mode finishes.
- */
- private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
- @Override
- public void onVrStateChanged(boolean enabled) {
- if (!enabled) {
- restoreNonVrImeFromSettingsNoCheck();
- }
- }
- };
-
- private void restoreNonVrImeFromSettingsNoCheck() {
- // switch back to non-VR InputMethod from settings.
- synchronized (mMethodMap) {
- if (!mIsVrImeStarted) return;
- mIsVrImeStarted = false;
- updateFromSettingsLocked(false);
- }
- }
-
private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
private final InputMethodManagerService mImms;
private final IInputMethodClient mClient;
@@ -597,9 +572,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final ImeDisplayValidator mImeDisplayValidator;
- /** True if VR IME started by {@link #startVrInputMethodNoCheck}. */
- boolean mIsVrImeStarted;
-
/**
* If non-null, this is the input method service we are currently connected
* to.
@@ -977,31 +949,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
/**
- * Start a VR InputMethod that matches IME with package name of {@param component}.
- * Note: This method is called from {@link android.app.VrManager}.
- */
- private void startVrInputMethodNoCheck(@Nullable ComponentName component) {
- if (component == null) {
- // clear the current VR-only IME (if any) and restore normal IME.
- restoreNonVrImeFromSettingsNoCheck();
- return;
- }
-
- synchronized (mMethodMap) {
- String packageName = component.getPackageName();
- for (InputMethodInfo info : mMethodList) {
- if (TextUtils.equals(info.getPackageName(), packageName) && info.isVrOnly()) {
- // set this is as current inputMethod without updating settings.
- setInputMethodEnabledLocked(info.getId(), true);
- setInputMethodLocked(info.getId(), NOT_A_SUBTYPE_ID);
- mIsVrImeStarted = true;
- break;
- }
- }
- }
- }
-
- /**
* Handles {@link Intent#ACTION_LOCALE_CHANGED}.
*
* <p>Note: For historical reasons, {@link Intent#ACTION_LOCALE_CHANGED} has been sent to all
@@ -1452,15 +1399,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
mSettings, context);
- // Register VR-state listener.
- IVrManager vrManager = (IVrManager) ServiceManager.getService(Context.VR_SERVICE);
- if (vrManager != null) {
- try {
- vrManager.registerListener(mVrStateCallbacks);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register VR mode state listener.");
- }
- }
}
private void resetDefaultImeLocked(Context context) {
@@ -1607,7 +1545,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// 1) it comes from the system process
// 2) the calling process' user id is identical to the current user id IMMS thinks.
@GuardedBy("mMethodMap")
- private boolean calledFromValidUserLocked(boolean allowCrossProfileAccess) {
+ private boolean calledFromValidUserLocked() {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserId(uid);
if (DEBUG) {
@@ -1623,7 +1561,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (userId == mSettings.getCurrentUserId()) {
return true;
}
- if (allowCrossProfileAccess && mSettings.isCurrentProfile(userId)) {
+ if (!PER_PROFILE_IME_ENABLED && mSettings.isCurrentProfile(userId)) {
return true;
}
@@ -1688,25 +1626,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
final long ident = Binder.clearCallingIdentity();
try {
- return getInputMethodListLocked(false /* isVrOnly */, resolvedUserIds[0]);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- @Override
- public List<InputMethodInfo> getVrInputMethodList() {
- final int callingUserId = UserHandle.getCallingUserId();
- synchronized (mMethodMap) {
- final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
- mSettings.getCurrentUserId(), null);
- if (resolvedUserIds.length != 1) {
- return Collections.emptyList();
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- return getInputMethodListLocked(true /* isVrOnly */, resolvedUserIds[0]);
+ return getInputMethodListLocked(resolvedUserIds[0]);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1732,8 +1652,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("mMethodMap")
- private List<InputMethodInfo> getInputMethodListLocked(boolean isVrOnly,
- @UserIdInt int userId) {
+ private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId) {
final ArrayList<InputMethodInfo> methodList;
if (userId == mSettings.getCurrentUserId()) {
// Create a copy.
@@ -1747,7 +1666,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
methodList);
}
- methodList.removeIf(imi -> imi.isVrOnly() != isVrOnly);
return methodList;
}
@@ -2021,8 +1939,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
// Compute the final shown display ID with validated cs.selfReportedDisplayId for this
// session & other conditions.
- final int displayIdToShowIme = computeImeDisplayIdForTarget(
- cs.selfReportedDisplayId, mIsVrImeStarted, mImeDisplayValidator);
+ final int displayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
+ mImeDisplayValidator);
if (mCurClient != cs) {
// Was the keyguard locked when switching over to the new client?
@@ -2131,17 +2049,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
* Find the display where the IME should be shown.
*
* @param displayId the ID of the display where the IME client target is.
- * @param isVrImeStarted {@code true} if VR IME started, {@code false} otherwise.
* @param checker instance of {@link ImeDisplayValidator} which is used for
* checking display config to adjust the final target display.
* @return The ID of the display where the IME should be shown.
*/
- static int computeImeDisplayIdForTarget(int displayId, boolean isVrImeStarted,
- @NonNull ImeDisplayValidator checker) {
- // For VR IME, we always show in default display.
- if (isVrImeStarted) {
- return DEFAULT_DISPLAY;
- }
+ static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
// We always assume that the default display id suitable to show the IME window.
return DEFAULT_DISPLAY;
@@ -2651,7 +2563,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
ResultReceiver resultReceiver) {
int uid = Binder.getCallingUid();
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
+ if (!calledFromValidUserLocked()) {
return false;
}
final long ident = Binder.clearCallingIdentity();
@@ -2736,7 +2648,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
ResultReceiver resultReceiver) {
int uid = Binder.getCallingUid();
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
+ if (!calledFromValidUserLocked()) {
return false;
}
final long ident = Binder.clearCallingIdentity();
@@ -3087,7 +2999,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public void showInputMethodPickerFromClient(
IInputMethodClient client, int auxiliarySubtypeMode) {
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
+ if (!calledFromValidUserLocked()) {
return;
}
if(!canShowInputMethodPickerLocked(client)) {
@@ -3159,7 +3071,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
IInputMethodClient client, String inputMethodId) {
synchronized (mMethodMap) {
// TODO(yukawa): Should we verify the display ID?
- if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
+ if (!calledFromValidUserLocked()) {
return;
}
executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
@@ -3274,7 +3186,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public InputMethodSubtype getLastInputMethodSubtype() {
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
+ if (!calledFromValidUserLocked()) {
return null;
}
final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked();
@@ -3312,7 +3224,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
synchronized (mMethodMap) {
- if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
+ if (!calledFromValidUserLocked()) {
return;
}
if (!mSystemReady) {
@@ -3627,9 +3539,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
case MSG_SET_INTERACTIVE:
handleSetInteractive(msg.arg1 != 0);
return true;
- case MSG_START_VR_INPUT:
- startVrInputMethodNoCheck((ComponentName) msg.obj);
- return true;
case MSG_REPORT_FULLSCREEN_MODE: {
final boolean fullscreen = msg.arg1 != 0;
final ClientState clientState = (ClientState)msg.obj;
@@ -3716,6 +3625,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
try {
final InputMethodInfo imi = new InputMethodInfo(context, ri,
additionalSubtypeMap.get(imeId));
+ if (imi.isVrOnly()) {
+ continue; // Skip VR-only IME, which isn't supported for now.
+ }
methodList.add(imi);
methodMap.put(imi.getId(), imi);
if (DEBUG) {
@@ -4099,17 +4011,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
boolean setSubtypeOnly) {
- // Updates to InputMethod are transient in VR mode. Its not included in history.
- final boolean isVrInput = imi != null && imi.isVrOnly();
- if (!isVrInput) {
- // Update the history of InputMethod and Subtype
- mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
- }
-
- if (isVrInput) {
- // Updates to InputMethod are transient in VR mode. Any changes to Settings are skipped.
- return;
- }
+ mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
// Set Subtype here
if (imi == null || subtypeId < 0) {
@@ -4158,7 +4060,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public InputMethodSubtype getCurrentInputMethodSubtype() {
synchronized (mMethodMap) {
// TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
+ if (!calledFromValidUserLocked()) {
return null;
}
return getCurrentInputMethodSubtypeLocked();
@@ -4208,7 +4110,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
synchronized (mMethodMap) {
// TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked(!PER_PROFILE_IME_ENABLED)) {
+ if (!calledFromValidUserLocked()) {
return false;
}
if (subtype != null && mCurMethodId != null) {
@@ -4225,7 +4127,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
synchronized (mMethodMap) {
- return getInputMethodListLocked(false, userId);
+ return getInputMethodListLocked(userId);
}
}
@@ -4257,11 +4159,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) {
- mService.mHandler.obtainMessage(MSG_START_VR_INPUT, componentName).sendToTarget();
- }
-
- @Override
public List<InputMethodInfo> getInputMethodListAsUser(int userId) {
return mService.getInputMethodListAsUser(userId);
}
@@ -4648,7 +4545,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
for (int userId : userIds) {
final List<InputMethodInfo> methods = all
- ? getInputMethodListLocked(false, userId)
+ ? getInputMethodListLocked(userId)
: getEnabledInputMethodListLocked(userId);
if (userIds.length > 1) {
pr.print("User #");
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 6f0c5e83b4fe..a31b3b4b3b5a 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -159,11 +159,6 @@ public final class MultiClientInputMethodManagerService {
}
@Override
- public void startVrInputMethodNoCheck(ComponentName componentName) {
- reportNotSupported();
- }
-
- @Override
public List<InputMethodInfo> getInputMethodListAsUser(
@UserIdInt int userId) {
return userIdToInputMethodInfoMapper.getAsList(userId);
@@ -1244,13 +1239,6 @@ public final class MultiClientInputMethodManagerService {
@BinderThread
@Override
- public List<InputMethodInfo> getVrInputMethodList() {
- reportNotSupported();
- return Collections.emptyList();
- }
-
- @BinderThread
- @Override
public List<InputMethodInfo> getEnabledInputMethodList() {
return mInputMethodInfoMap.getAsList(UserHandle.getUserId(Binder.getCallingUid()));
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index cc0ac9a7e0a9..bd12075fdad3 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -443,6 +443,16 @@ public class JobSchedulerService extends com.android.server.SystemService
"qc_window_size_rare_ms";
private static final String KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
"qc_max_execution_time_ms";
+ private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
+ "qc_max_job_count_active";
+ private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
+ "qc_max_job_count_working";
+ private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
+ "qc_max_job_count_frequent";
+ private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
+ "qc_max_job_count_rare";
+ private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME =
+ "qc_max_count_per_allowed_time";
private static final int DEFAULT_MIN_IDLE_COUNT = 1;
private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
@@ -479,6 +489,15 @@ public class JobSchedulerService extends com.android.server.SystemService
24 * 60 * 60 * 1000L; // 24 hours
private static final long DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
4 * 60 * 60 * 1000L; // 4 hours
+ private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
+ 200; // 1200/hr
+ private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
+ 1200; // 600/hr
+ private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
+ 1800; // 225/hr
+ private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
+ 2400; // 100/hr
+ private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
/**
* Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
@@ -682,6 +701,41 @@ public class JobSchedulerService extends com.android.server.SystemService
public long QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS;
+ /**
+ * The maximum number of jobs an app can run within this particular standby bucket's
+ * window size.
+ */
+ public int QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
+ DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE;
+
+ /**
+ * The maximum number of jobs an app can run within this particular standby bucket's
+ * window size.
+ */
+ public int QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
+ DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING;
+
+ /**
+ * The maximum number of jobs an app can run within this particular standby bucket's
+ * window size.
+ */
+ public int QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
+ DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT;
+
+ /**
+ * The maximum number of jobs an app can run within this particular standby bucket's
+ * window size.
+ */
+ public int QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
+ DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE;
+
+ /**
+ * The maximum number of jobs that can run within the past
+ * {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
+ */
+ public int QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME =
+ DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
+
private final KeyValueListParser mParser = new KeyValueListParser(',');
void updateConstantsLocked(String value) {
@@ -767,6 +821,21 @@ public class JobSchedulerService extends com.android.server.SystemService
QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = mParser.getInt(
+ KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE,
+ DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE);
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = mParser.getInt(
+ KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING,
+ DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING);
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = mParser.getInt(
+ KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT,
+ DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT);
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = mParser.getInt(
+ KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE,
+ DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE);
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt(
+ KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME,
+ DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
}
void dump(IndentingPrintWriter pw) {
@@ -823,6 +892,16 @@ public class JobSchedulerService extends com.android.server.SystemService
QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println();
pw.printPair(KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE,
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING,
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT,
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE,
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE).println();
+ pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME,
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME).println();
pw.decreaseIndent();
}
@@ -872,6 +951,16 @@ public class JobSchedulerService extends com.android.server.SystemService
QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS,
QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
+ proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE,
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE);
+ proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_WORKING,
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING);
+ proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT,
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT);
+ proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE,
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE);
+ proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
+ QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
proto.end(qcToken);
proto.end(token);
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index c16d1b4ecec5..5a0b991bc7de 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -230,10 +230,10 @@ public final class QuotaController extends StateController {
@VisibleForTesting
static class ExecutionStats {
/**
- * The time at which this record should be considered invalid, in the elapsed realtime
- * timebase.
+ * The time after which this record should be considered invalid (out of date), in the
+ * elapsed realtime timebase.
*/
- public long invalidTimeElapsed;
+ public long expirationTimeElapsed;
public long windowSizeMs;
@@ -241,29 +241,45 @@ public final class QuotaController extends StateController {
public long executionTimeInWindowMs;
public int bgJobCountInWindow;
- /** The total amount of time the app ran in the last {@link MAX_PERIOD_MS}. */
+ /** The total amount of time the app ran in the last {@link #MAX_PERIOD_MS}. */
public long executionTimeInMaxPeriodMs;
public int bgJobCountInMaxPeriod;
/**
- * The time after which the sum of all the app's sessions plus {@link mQuotaBufferMs} equals
- * the quota. This is only valid if
- * executionTimeInWindowMs >= {@link mAllowedTimePerPeriodMs} or
- * executionTimeInMaxPeriodMs >= {@link mMaxExecutionTimeMs}.
+ * The time after which the sum of all the app's sessions plus {@link #mQuotaBufferMs}
+ * equals the quota. This is only valid if
+ * executionTimeInWindowMs >= {@link #mAllowedTimePerPeriodMs} or
+ * executionTimeInMaxPeriodMs >= {@link #mMaxExecutionTimeMs}.
*/
public long quotaCutoffTimeElapsed;
+ /**
+ * The time after which {@link #jobCountInAllowedTime} should be considered invalid, in the
+ * elapsed realtime timebase.
+ */
+ public long jobCountExpirationTimeElapsed;
+
+ /**
+ * The number of jobs that ran in at least the last {@link #mAllowedTimePerPeriodMs}.
+ * It may contain a few stale entries since cleanup won't happen exactly every
+ * {@link #mAllowedTimePerPeriodMs}.
+ */
+ public int jobCountInAllowedTime;
+
@Override
public String toString() {
return new StringBuilder()
- .append("invalidTime=").append(invalidTimeElapsed).append(", ")
+ .append("expirationTime=").append(expirationTimeElapsed).append(", ")
.append("windowSize=").append(windowSizeMs).append(", ")
.append("executionTimeInWindow=").append(executionTimeInWindowMs).append(", ")
.append("bgJobCountInWindow=").append(bgJobCountInWindow).append(", ")
.append("executionTimeInMaxPeriod=").append(executionTimeInMaxPeriodMs)
.append(", ")
.append("bgJobCountInMaxPeriod=").append(bgJobCountInMaxPeriod).append(", ")
- .append("quotaCutoffTime=").append(quotaCutoffTimeElapsed)
+ .append("quotaCutoffTime=").append(quotaCutoffTimeElapsed).append(", ")
+ .append("jobCountExpirationTime").append(jobCountExpirationTimeElapsed)
+ .append(", ")
+ .append("jobCountInAllowedTime").append(jobCountInAllowedTime)
.toString();
}
@@ -271,13 +287,15 @@ public final class QuotaController extends StateController {
public boolean equals(Object obj) {
if (obj instanceof ExecutionStats) {
ExecutionStats other = (ExecutionStats) obj;
- return this.invalidTimeElapsed == other.invalidTimeElapsed
+ return this.expirationTimeElapsed == other.expirationTimeElapsed
&& this.windowSizeMs == other.windowSizeMs
&& this.executionTimeInWindowMs == other.executionTimeInWindowMs
&& this.bgJobCountInWindow == other.bgJobCountInWindow
&& this.executionTimeInMaxPeriodMs == other.executionTimeInMaxPeriodMs
&& this.bgJobCountInMaxPeriod == other.bgJobCountInMaxPeriod
- && this.quotaCutoffTimeElapsed == other.quotaCutoffTimeElapsed;
+ && this.quotaCutoffTimeElapsed == other.quotaCutoffTimeElapsed
+ && this.jobCountExpirationTimeElapsed == other.jobCountExpirationTimeElapsed
+ && this.jobCountInAllowedTime == other.jobCountInAllowedTime;
} else {
return false;
}
@@ -286,13 +304,15 @@ public final class QuotaController extends StateController {
@Override
public int hashCode() {
int result = 0;
- result = 31 * result + hashLong(invalidTimeElapsed);
+ result = 31 * result + hashLong(expirationTimeElapsed);
result = 31 * result + hashLong(windowSizeMs);
result = 31 * result + hashLong(executionTimeInWindowMs);
result = 31 * result + bgJobCountInWindow;
result = 31 * result + hashLong(executionTimeInMaxPeriodMs);
result = 31 * result + bgJobCountInMaxPeriod;
result = 31 * result + hashLong(quotaCutoffTimeElapsed);
+ result = 31 * result + hashLong(jobCountExpirationTimeElapsed);
+ result = 31 * result + jobCountInAllowedTime;
return result;
}
}
@@ -320,7 +340,7 @@ public final class QuotaController extends StateController {
/**
* List of jobs that started while the UID was in the TOP state. There will be no more than
- * 16 ({@link JobSchedulerService.MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
+ * 16 ({@link JobSchedulerService#MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
* fine.
*/
private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
@@ -343,7 +363,7 @@ public final class QuotaController extends StateController {
private long mAllowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
/**
- * The maximum amount of time an app can have its jobs running within a {@link MAX_PERIOD_MS}
+ * The maximum amount of time an app can have its jobs running within a {@link #MAX_PERIOD_MS}
* window.
*/
private long mMaxExecutionTimeMs = 4 * 60 * MINUTE_IN_MILLIS;
@@ -355,17 +375,20 @@ public final class QuotaController extends StateController {
private long mQuotaBufferMs = 30 * 1000L; // 30 seconds
/**
- * {@link mAllowedTimePerPeriodMs} - {@link mQuotaBufferMs}. This can be used to determine when
- * an app will have enough quota to transition from out-of-quota to in-quota.
+ * {@link #mAllowedTimePerPeriodMs} - {@link #mQuotaBufferMs}. This can be used to determine
+ * when an app will have enough quota to transition from out-of-quota to in-quota.
*/
private long mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
/**
- * {@link mMaxExecutionTimeMs} - {@link mQuotaBufferMs}. This can be used to determine when an
+ * {@link #mMaxExecutionTimeMs} - {@link #mQuotaBufferMs}. This can be used to determine when an
* app will have enough quota to transition from out-of-quota to in-quota.
*/
private long mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
+ /** The maximum number of jobs that can run within the past {@link #mAllowedTimePerPeriodMs}. */
+ private int mMaxJobCountPerAllowedTime = 20;
+
private long mNextCleanupTimeElapsed = 0;
private final AlarmManager.OnAlarmListener mSessionCleanupAlarmListener =
new AlarmManager.OnAlarmListener() {
@@ -412,6 +435,23 @@ public final class QuotaController extends StateController {
/** The maximum period any bucket can have. */
private static final long MAX_PERIOD_MS = 24 * 60 * MINUTE_IN_MILLIS;
+ /**
+ * The maximum number of jobs based on its standby bucket. For each max value count in the
+ * array, the app will not be allowed to run more than that many number of jobs within the
+ * latest time interval of its rolling window size.
+ *
+ * @see #mBucketPeriodsMs
+ */
+ private final int[] mMaxBucketJobCounts = new int[] {
+ 200, // ACTIVE -- 1200/hr
+ 1200, // WORKING -- 600/hr
+ 1800, // FREQUENT -- 225/hr
+ 2400 // RARE -- 100/hr
+ };
+
+ /** The minimum number of jobs that any bucket will be allowed to run. */
+ private static final int MIN_BUCKET_JOB_COUNT = 100;
+
/** An app has reached its quota. The message should contain a {@link Package} object. */
private static final int MSG_REACHED_QUOTA = 0;
/** Drop any old timing sessions. */
@@ -463,17 +503,21 @@ public final class QuotaController extends StateController {
@Override
public void prepareForExecutionLocked(JobStatus jobStatus) {
if (DEBUG) Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
+
+ final int uid = jobStatus.getSourceUid();
+ if (mActivityManagerInternal.getUidProcessState(uid) <= ActivityManager.PROCESS_STATE_TOP) {
+ mTopStartedJobs.add(jobStatus);
+ // Top jobs won't count towards quota so there's no need to involve the Timer.
+ return;
+ }
+
final int userId = jobStatus.getSourceUserId();
final String packageName = jobStatus.getSourcePackageName();
- final int uid = jobStatus.getSourceUid();
Timer timer = mPkgTimers.get(userId, packageName);
if (timer == null) {
timer = new Timer(uid, userId, packageName);
mPkgTimers.add(userId, packageName, timer);
}
- if (mActivityManagerInternal.getUidProcessState(uid) == ActivityManager.PROCESS_STATE_TOP) {
- mTopStartedJobs.add(jobStatus);
- }
timer.startTrackingJob(jobStatus);
}
@@ -548,6 +592,36 @@ public final class QuotaController extends StateController {
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
changed = true;
}
+ int newMaxCountPerAllowedPeriod = Math.max(10,
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
+ if (mMaxJobCountPerAllowedTime != newMaxCountPerAllowedPeriod) {
+ mMaxJobCountPerAllowedTime = newMaxCountPerAllowedPeriod;
+ changed = true;
+ }
+ int newActiveMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+ Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE));
+ if (mMaxBucketJobCounts[ACTIVE_INDEX] != newActiveMaxJobCount) {
+ mMaxBucketJobCounts[ACTIVE_INDEX] = newActiveMaxJobCount;
+ changed = true;
+ }
+ int newWorkingMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+ Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING));
+ if (mMaxBucketJobCounts[WORKING_INDEX] != newWorkingMaxJobCount) {
+ mMaxBucketJobCounts[WORKING_INDEX] = newWorkingMaxJobCount;
+ changed = true;
+ }
+ int newFrequentMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+ Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT));
+ if (mMaxBucketJobCounts[FREQUENT_INDEX] != newFrequentMaxJobCount) {
+ mMaxBucketJobCounts[FREQUENT_INDEX] = newFrequentMaxJobCount;
+ changed = true;
+ }
+ int newRareMaxJobCount = Math.max(mMaxJobCountPerAllowedTime,
+ Math.max(MIN_BUCKET_JOB_COUNT, mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE));
+ if (mMaxBucketJobCounts[RARE_INDEX] != newRareMaxJobCount) {
+ mMaxBucketJobCounts[RARE_INDEX] = newRareMaxJobCount;
+ changed = true;
+ }
if (changed) {
// Update job bookkeeping out of band.
@@ -631,18 +705,39 @@ public final class QuotaController extends StateController {
return isTopStartedJob(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid())
|| isWithinQuotaLocked(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
}
- private boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
+ @VisibleForTesting
+ boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
final int standbyBucket) {
if (standbyBucket == NEVER_INDEX) return false;
// This check is needed in case the flag is toggled after a job has been registered.
if (!mShouldThrottle) return true;
// Quota constraint is not enforced while charging or when parole is on.
- return mChargeTracker.isCharging() || mInParole
- || getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) > 0;
+ if (mChargeTracker.isCharging() || mInParole) {
+ return true;
+ }
+
+ return getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) > 0
+ && isUnderJobCountQuotaLocked(userId, packageName, standbyBucket);
+ }
+
+ private boolean isUnderJobCountQuotaLocked(final int userId, @NonNull final String packageName,
+ final int standbyBucket) {
+ ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket, false);
+ return isUnderJobCountQuotaLocked(stats, standbyBucket);
+ }
+
+ private boolean isUnderJobCountQuotaLocked(@NonNull ExecutionStats stats,
+ final int standbyBucket) {
+ final long now = sElapsedRealtimeClock.millis();
+ final boolean isUnderAllowedTimeQuota =
+ (stats.jobCountExpirationTimeElapsed <= now
+ || stats.jobCountInAllowedTime < mMaxJobCountPerAllowedTime);
+ return isUnderAllowedTimeQuota
+ && (stats.bgJobCountInWindow < mMaxBucketJobCounts[standbyBucket]);
}
@VisibleForTesting
@@ -679,6 +774,13 @@ public final class QuotaController extends StateController {
@NonNull
ExecutionStats getExecutionStatsLocked(final int userId, @NonNull final String packageName,
final int standbyBucket) {
+ return getExecutionStatsLocked(userId, packageName, standbyBucket, true);
+ }
+
+ @NonNull
+ private ExecutionStats getExecutionStatsLocked(final int userId,
+ @NonNull final String packageName, final int standbyBucket,
+ final boolean refreshStatsIfOld) {
if (standbyBucket == NEVER_INDEX) {
Slog.wtf(TAG, "getExecutionStatsLocked called for a NEVER app.");
return new ExecutionStats();
@@ -693,14 +795,16 @@ public final class QuotaController extends StateController {
stats = new ExecutionStats();
appStats[standbyBucket] = stats;
}
- final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
- Timer timer = mPkgTimers.get(userId, packageName);
- if ((timer != null && timer.isActive())
- || stats.invalidTimeElapsed <= sElapsedRealtimeClock.millis()
- || stats.windowSizeMs != bucketWindowSizeMs) {
- // The stats are no longer valid.
- stats.windowSizeMs = bucketWindowSizeMs;
- updateExecutionStatsLocked(userId, packageName, stats);
+ if (refreshStatsIfOld) {
+ final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
+ Timer timer = mPkgTimers.get(userId, packageName);
+ if ((timer != null && timer.isActive())
+ || stats.expirationTimeElapsed <= sElapsedRealtimeClock.millis()
+ || stats.windowSizeMs != bucketWindowSizeMs) {
+ // The stats are no longer valid.
+ stats.windowSizeMs = bucketWindowSizeMs;
+ updateExecutionStatsLocked(userId, packageName, stats);
+ }
}
return stats;
@@ -717,14 +821,14 @@ public final class QuotaController extends StateController {
Timer timer = mPkgTimers.get(userId, packageName);
final long nowElapsed = sElapsedRealtimeClock.millis();
- stats.invalidTimeElapsed = nowElapsed + MAX_PERIOD_MS;
+ stats.expirationTimeElapsed = nowElapsed + MAX_PERIOD_MS;
if (timer != null && timer.isActive()) {
stats.executionTimeInWindowMs =
stats.executionTimeInMaxPeriodMs = timer.getCurrentDuration(nowElapsed);
stats.bgJobCountInWindow = stats.bgJobCountInMaxPeriod = timer.getBgJobCount();
// If the timer is active, the value will be stale at the next method call, so
// invalidate now.
- stats.invalidTimeElapsed = nowElapsed;
+ stats.expirationTimeElapsed = nowElapsed;
if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
stats.quotaCutoffTimeElapsed = Math.max(stats.quotaCutoffTimeElapsed,
nowElapsed - mAllowedTimeIntoQuotaMs);
@@ -800,7 +904,7 @@ public final class QuotaController extends StateController {
break;
}
}
- stats.invalidTimeElapsed = nowElapsed + emptyTimeMs;
+ stats.expirationTimeElapsed = nowElapsed + emptyTimeMs;
}
private void invalidateAllExecutionStatsLocked(final int userId,
@@ -811,13 +915,35 @@ public final class QuotaController extends StateController {
for (int i = 0; i < appStats.length; ++i) {
ExecutionStats stats = appStats[i];
if (stats != null) {
- stats.invalidTimeElapsed = nowElapsed;
+ stats.expirationTimeElapsed = nowElapsed;
}
}
}
}
@VisibleForTesting
+ void incrementJobCount(final int userId, @NonNull final String packageName, int count) {
+ final long now = sElapsedRealtimeClock.millis();
+ ExecutionStats[] appStats = mExecutionStatsCache.get(userId, packageName);
+ if (appStats == null) {
+ appStats = new ExecutionStats[mBucketPeriodsMs.length];
+ mExecutionStatsCache.add(userId, packageName, appStats);
+ }
+ for (int i = 0; i < appStats.length; ++i) {
+ ExecutionStats stats = appStats[i];
+ if (stats == null) {
+ stats = new ExecutionStats();
+ appStats[i] = stats;
+ }
+ if (stats.jobCountExpirationTimeElapsed <= now) {
+ stats.jobCountExpirationTimeElapsed = now + mAllowedTimePerPeriodMs;
+ stats.jobCountInAllowedTime = 0;
+ }
+ stats.jobCountInAllowedTime += count;
+ }
+ }
+
+ @VisibleForTesting
void saveTimingSession(final int userId, @NonNull final String packageName,
@NonNull final TimingSession session) {
synchronized (mLock) {
@@ -1023,9 +1149,12 @@ public final class QuotaController extends StateController {
final String pkgString = string(userId, packageName);
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
+ final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket);
+
QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
if (stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs
- && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs) {
+ && stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
+ && isUnderJobCountQuota) {
// Already in quota. Why was this method called?
if (DEBUG) {
Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString
@@ -1042,18 +1171,22 @@ public final class QuotaController extends StateController {
mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget();
return;
}
+
if (alarmListener == null) {
alarmListener = new QcAlarmListener(userId, packageName);
mInQuotaAlarmListeners.add(userId, packageName, alarmListener);
}
// The time this app will have quota again.
- long inQuotaTimeElapsed =
- stats.quotaCutoffTimeElapsed + stats.windowSizeMs;
+ long inQuotaTimeElapsed = stats.quotaCutoffTimeElapsed + stats.windowSizeMs;
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeMs) {
inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
stats.quotaCutoffTimeElapsed + MAX_PERIOD_MS);
}
+ if (!isUnderJobCountQuota) {
+ inQuotaTimeElapsed = Math.max(inQuotaTimeElapsed,
+ stats.jobCountExpirationTimeElapsed + mAllowedTimePerPeriodMs);
+ }
// Only schedule the alarm if:
// 1. There isn't one currently scheduled
// 2. The new alarm is significantly earlier than the previous alarm (which could be the
@@ -1228,6 +1361,7 @@ public final class QuotaController extends StateController {
mRunningBgJobs.add(jobStatus);
if (shouldTrackLocked()) {
mBgJobCount++;
+ incrementJobCount(mPkg.userId, mPkg.packageName, 1);
if (mRunningBgJobs.size() == 1) {
// Started tracking the first job.
mStartTimeElapsed = sElapsedRealtimeClock.millis();
@@ -1324,6 +1458,7 @@ public final class QuotaController extends StateController {
// repeatedly plugged in and unplugged, or an app changes foreground state
// very frequently, the job count for a package may be artificially high.
mBgJobCount = mRunningBgJobs.size();
+ incrementJobCount(mPkg.userId, mPkg.packageName, mBgJobCount);
// Starting the timer means that all cached execution stats are now
// incorrect.
invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
@@ -1604,6 +1739,12 @@ public final class QuotaController extends StateController {
@VisibleForTesting
@NonNull
+ int[] getBucketMaxJobCounts() {
+ return mMaxBucketJobCounts;
+ }
+
+ @VisibleForTesting
+ @NonNull
long[] getBucketWindowSizes() {
return mBucketPeriodsMs;
}
@@ -1631,6 +1772,11 @@ public final class QuotaController extends StateController {
}
@VisibleForTesting
+ int getMaxJobCountPerAllowedTime() {
+ return mMaxJobCountPerAllowedTime;
+ }
+
+ @VisibleForTesting
@Nullable
List<TimingSession> getTimingSessions(int userId, String packageName) {
return mTimingSessions.get(userId, packageName);
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
new file mode 100644
index 000000000000..22fabb2cdd3e
--- /dev/null
+++ b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
@@ -0,0 +1,118 @@
+/*
+ * 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
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.hardware.location.ActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
+import android.hardware.location.IActivityRecognitionHardwareWatcher;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.server.ServiceWatcher;
+
+/**
+ * Proxy class to bind GmsCore to the ActivityRecognitionHardware.
+ *
+ * @hide
+ */
+public class ActivityRecognitionProxy {
+
+ private static final String TAG = "ActivityRecognitionProxy";
+
+ /**
+ * Creates an instance of the proxy and binds it to the appropriate FusedProvider.
+ *
+ * @return An instance of the proxy if it could be bound, null otherwise.
+ */
+ public static ActivityRecognitionProxy createAndBind(
+ Context context,
+ boolean activityRecognitionHardwareIsSupported,
+ ActivityRecognitionHardware activityRecognitionHardware,
+ int overlaySwitchResId,
+ int defaultServicePackageNameResId,
+ int initialPackageNameResId) {
+ ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy(
+ context,
+ activityRecognitionHardwareIsSupported,
+ activityRecognitionHardware,
+ overlaySwitchResId,
+ defaultServicePackageNameResId,
+ initialPackageNameResId);
+
+ if (activityRecognitionProxy.mServiceWatcher.start()) {
+ return activityRecognitionProxy;
+ } else {
+ return null;
+ }
+ }
+
+ private final ServiceWatcher mServiceWatcher;
+ private final boolean mIsSupported;
+ private final ActivityRecognitionHardware mInstance;
+
+ private ActivityRecognitionProxy(
+ Context context,
+ boolean activityRecognitionHardwareIsSupported,
+ ActivityRecognitionHardware activityRecognitionHardware,
+ int overlaySwitchResId,
+ int defaultServicePackageNameResId,
+ int initialPackageNameResId) {
+ mIsSupported = activityRecognitionHardwareIsSupported;
+ mInstance = activityRecognitionHardware;
+
+ mServiceWatcher = new ServiceWatcher(
+ context,
+ TAG,
+ "com.android.location.service.ActivityRecognitionProvider",
+ overlaySwitchResId,
+ defaultServicePackageNameResId,
+ initialPackageNameResId,
+ BackgroundThread.getHandler()) {
+ @Override
+ protected void onBind() {
+ runOnBinder(ActivityRecognitionProxy.this::initializeService);
+ }
+ };
+ }
+
+ private void initializeService(IBinder binder) {
+ try {
+ String descriptor = binder.getInterfaceDescriptor();
+
+ if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(
+ descriptor)) {
+ IActivityRecognitionHardwareWatcher watcher =
+ IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
+ if (mInstance != null) {
+ watcher.onInstanceChanged(mInstance);
+ }
+ } else if (IActivityRecognitionHardwareClient.class.getCanonicalName()
+ .equals(descriptor)) {
+ IActivityRecognitionHardwareClient client =
+ IActivityRecognitionHardwareClient.Stub.asInterface(binder);
+ client.onAvailabilityChanged(mIsSupported, mInstance);
+ } else {
+ Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 8734ceb614a9..a9ae74f67de7 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -947,6 +947,10 @@ public class LockSettingsService extends ILockSettings.Stub {
public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
String managedUserPassword) {
checkWritePermission(userId);
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ throw new UnsupportedOperationException(
+ "This operation requires secure lock screen feature.");
+ }
synchronized (mSeparateChallengeLock) {
setSeparateProfileChallengeEnabledLocked(userId, enabled, managedUserPassword);
}
@@ -1305,6 +1309,10 @@ public class LockSettingsService extends ILockSettings.Stub {
public void setLockCredential(String credential, int type, String savedCredential,
int requestedQuality, int userId)
throws RemoteException {
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ throw new UnsupportedOperationException(
+ "This operation requires secure lock screen feature");
+ }
checkWritePermission(userId);
synchronized (mSeparateChallengeLock) {
setLockCredentialInternal(credential, type, savedCredential, requestedQuality, userId);
@@ -2906,6 +2914,10 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
byte[] token, int requestedQuality, int userId) {
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ throw new UnsupportedOperationException(
+ "This operation requires secure lock screen feature.");
+ }
try {
return LockSettingsService.this.setLockCredentialWithToken(credential, type,
tokenHandle, token, requestedQuality, userId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 07f23ce2231a..6163077e1acf 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -18,6 +18,7 @@ package com.android.server.locksettings;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+
import static com.android.internal.widget.LockPatternUtils.stringToPattern;
import android.app.ActivityManager;
@@ -58,6 +59,18 @@ class LockSettingsShellCommand extends ShellCommand {
mCurrentUserId = ActivityManager.getService().getCurrentUser().id;
parseArgs();
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ switch (cmd) {
+ case COMMAND_HELP:
+ case COMMAND_GET_DISABLED:
+ case COMMAND_SET_DISABLED:
+ break;
+ default:
+ getErrPrintWriter().println(
+ "The device does not support lock screen - ignoring the command.");
+ return -1;
+ }
+ }
if (!checkCredential()) {
return -1;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index d8c2432db856..af790f25564c 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -78,6 +78,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private final String mPackageName;
private final String mTag;
private final ControllerLink mController;
+ private final MediaSession.Token mSessionToken;
private final SessionLink mSession;
private final SessionCb mSessionCb;
private final MediaSessionService.ServiceImpl mService;
@@ -128,6 +129,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mPackageName = ownerPackageName;
mTag = tag;
mController = new ControllerLink(new ControllerStub());
+ mSessionToken = new MediaSession.Token(mController);
mSession = new SessionLink(new SessionStub());
mSessionCb = new SessionCb(cb);
mService = service;
@@ -157,6 +159,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
/**
+ * Get the session token for creating {@link MediaController}.
+ *
+ * @return The session token.
+ */
+ public MediaSession.Token getSessionToken() {
+ return mSessionToken;
+ }
+
+ /**
* Get the info for this session.
*
* @return Info that identifies this session.
diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
index e3ae8a7ac21f..1541b1d520cd 100644
--- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java
@@ -51,7 +51,6 @@ import android.media.session.ICallback;
import android.media.session.IOnMediaKeyListener;
import android.media.session.IOnVolumeKeyLongPressListener;
import android.media.session.ISession2TokensListener;
-import android.media.session.ISessionController;
import android.media.session.ISessionManager;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
@@ -290,9 +289,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl {
return;
}
try {
- mRvc.remoteVolumeChanged(
- ISessionController.Stub.asInterface(session.getControllerLink().getBinder()),
- flags);
+ mRvc.remoteVolumeChanged(session.getSessionToken(), flags);
} catch (Exception e) {
Log.wtf(TAG, "Error sending volume change to system UI.", e);
}
@@ -618,7 +615,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl {
int size = records.size();
ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
for (int i = 0; i < size; i++) {
- tokens.add(new MediaSession.Token(records.get(i).getControllerLink()));
+ tokens.add(records.get(i).getSessionToken());
}
pushRemoteVolumeUpdateLocked(userId);
for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
@@ -645,9 +642,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl {
return;
}
MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId);
- mRvc.updateRemoteController(record == null ? null
- : ISessionController.Stub.asInterface(
- record.getControllerLink().getBinder()));
+ mRvc.updateRemoteController(record == null ? null : record.getSessionToken());
} catch (RemoteException e) {
Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
}
@@ -864,7 +859,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl {
MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked();
if (mediaButtonSession != null) {
mCallback.onAddressedPlayerChangedToMediaSession(
- new MediaSession.Token(mediaButtonSession.getControllerLink()));
+ mediaButtonSession.getSessionToken());
} else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
mCallback.onAddressedPlayerChangedToMediaButtonReceiver(
mCurrentFullUserRecord.mLastMediaButtonReceiver
@@ -1804,7 +1799,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl {
if (mCurrentFullUserRecord.mCallback != null) {
try {
mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
- keyEvent, new MediaSession.Token(session.getControllerLink()));
+ keyEvent, session.getSessionToken());
} catch (RemoteException e) {
Log.w(TAG, "Failed to send callback", e);
}
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index ee60daa20837..c2dc554321c2 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -42,7 +42,8 @@ public interface NotificationDelegate {
void onNotificationVisibilityChanged(
NotificationVisibility[] newlyVisibleKeys,
NotificationVisibility[] noLongerVisibleKeys);
- void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
+ void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded,
+ int notificationLocation);
void onNotificationDirectReplied(String key);
void onNotificationSettingsViewed(String key);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 20c4da416d1d..47a55971b9c7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -868,7 +868,7 @@ public class NotificationManagerService extends SystemService {
@Override
public void onNotificationExpansionChanged(String key,
- boolean userAction, boolean expanded) {
+ boolean userAction, boolean expanded, int notificationLocation) {
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 559874167429..02fc51f89e62 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1263,7 +1263,7 @@ public final class NotificationRecord {
public LogMaker getAdjustmentLogMaker() {
return getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ITEM)
- .setType(MetricsEvent.NOTIFICATION_ASSISTANT_ADJUSTMENT);
+ .setType(MetricsEvent.TYPE_NOTIFICATION_ASSISTANT_ADJUSTMENT);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
index 2ae424dd4b1b..5b765dfee3a4 100644
--- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
+++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
@@ -22,63 +22,117 @@ import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Process;
import android.os.ServiceManager;
+import android.util.ByteStringUtils;
+import android.util.EventLog;
import android.util.Log;
import com.android.server.pm.dex.DexLogger;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
- * Scheduled job to trigger logging of app dynamic code loading. This runs daily while idle and
- * charging. The actual logging is performed by {@link DexLogger}.
+ * Scheduled jobs related to logging of app dynamic code loading. The idle logging job runs daily
+ * while idle and charging and calls {@link DexLogger} to write dynamic code information to the
+ * event log. The audit watching job scans the event log periodically while idle to find AVC audit
+ * messages indicating use of dynamic native code and adds the information to {@link DexLogger}.
* {@hide}
*/
public class DynamicCodeLoggingService extends JobService {
private static final String TAG = DynamicCodeLoggingService.class.getName();
- private static final int JOB_ID = 2030028;
- private static final long PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1);
+ private static final boolean DEBUG = false;
- private volatile boolean mStopRequested = false;
+ private static final int IDLE_LOGGING_JOB_ID = 2030028;
+ private static final int AUDIT_WATCHING_JOB_ID = 203142925;
- private static final boolean DEBUG = false;
+ private static final long IDLE_LOGGING_PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1);
+ private static final long AUDIT_WATCHING_PERIOD_MILLIS = TimeUnit.HOURS.toMillis(2);
+
+ private static final int AUDIT_AVC = 1400; // Defined in linux/audit.h
+ private static final String AVC_PREFIX = "type=" + AUDIT_AVC + " ";
+
+ private static final Pattern EXECUTE_NATIVE_AUDIT_PATTERN =
+ Pattern.compile(".*\\bavc: granted \\{ execute(?:_no_trans|) \\} .*"
+ + "\\bpath=(?:\"([^\" ]*)\"|([0-9A-F]+)) .*"
+ + "\\bscontext=u:r:untrusted_app_2(?:5|7):.*"
+ + "\\btcontext=u:object_r:app_data_file:.*"
+ + "\\btclass=file\\b.*");
+
+ private volatile boolean mIdleLoggingStopRequested = false;
+ private volatile boolean mAuditWatchingStopRequested = false;
/**
- * Schedule our job with the {@link JobScheduler}.
+ * Schedule our jobs with the {@link JobScheduler}.
*/
public static void schedule(Context context) {
ComponentName serviceName = new ComponentName(
"android", DynamicCodeLoggingService.class.getName());
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- js.schedule(new JobInfo.Builder(JOB_ID, serviceName)
+ js.schedule(new JobInfo.Builder(IDLE_LOGGING_JOB_ID, serviceName)
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
- .setPeriodic(PERIOD_MILLIS)
+ .setPeriodic(IDLE_LOGGING_PERIOD_MILLIS)
.build());
+ js.schedule(new JobInfo.Builder(AUDIT_WATCHING_JOB_ID, serviceName)
+ .setRequiresDeviceIdle(true)
+ .setRequiresBatteryNotLow(true)
+ .setPeriodic(AUDIT_WATCHING_PERIOD_MILLIS)
+ .build());
+
if (DEBUG) {
- Log.d(TAG, "Job scheduled");
+ Log.d(TAG, "Jobs scheduled");
}
}
@Override
public boolean onStartJob(JobParameters params) {
+ int jobId = params.getJobId();
if (DEBUG) {
- Log.d(TAG, "onStartJob");
+ Log.d(TAG, "onStartJob " + jobId);
+ }
+ switch (jobId) {
+ case IDLE_LOGGING_JOB_ID:
+ mIdleLoggingStopRequested = false;
+ new IdleLoggingThread(params).start();
+ return true; // Job is running on another thread
+ case AUDIT_WATCHING_JOB_ID:
+ mAuditWatchingStopRequested = false;
+ new AuditWatchingThread(params).start();
+ return true; // Job is running on another thread
+ default:
+ // Shouldn't happen, but indicate nothing is running.
+ return false;
}
- mStopRequested = false;
- new IdleLoggingThread(params).start();
- return true; // Job is running on another thread
}
@Override
public boolean onStopJob(JobParameters params) {
+ int jobId = params.getJobId();
if (DEBUG) {
- Log.d(TAG, "onStopJob");
+ Log.d(TAG, "onStopJob " + jobId);
}
- mStopRequested = true;
- return true; // Requests job be re-scheduled.
+ switch (jobId) {
+ case IDLE_LOGGING_JOB_ID:
+ mIdleLoggingStopRequested = true;
+ return true; // Requests job be re-scheduled.
+ case AUDIT_WATCHING_JOB_ID:
+ mAuditWatchingStopRequested = true;
+ return true; // Requests job be re-scheduled.
+ default:
+ return false;
+ }
+ }
+
+ private static DexLogger getDexLogger() {
+ PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package");
+ return pm.getDexManager().getDexLogger();
}
private class IdleLoggingThread extends Thread {
@@ -92,14 +146,13 @@ public class DynamicCodeLoggingService extends JobService {
@Override
public void run() {
if (DEBUG) {
- Log.d(TAG, "Starting logging run");
+ Log.d(TAG, "Starting IdleLoggingJob run");
}
- PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package");
- DexLogger dexLogger = pm.getDexManager().getDexLogger();
+ DexLogger dexLogger = getDexLogger();
for (String packageName : dexLogger.getAllPackagesWithDynamicCodeLoading()) {
- if (mStopRequested) {
- Log.w(TAG, "Stopping logging run at scheduler request");
+ if (mIdleLoggingStopRequested) {
+ Log.w(TAG, "Stopping IdleLoggingJob run at scheduler request");
return;
}
@@ -108,8 +161,128 @@ public class DynamicCodeLoggingService extends JobService {
jobFinished(mParams, /* reschedule */ false);
if (DEBUG) {
- Log.d(TAG, "Finished logging run");
+ Log.d(TAG, "Finished IdleLoggingJob run");
}
}
}
+
+ private class AuditWatchingThread extends Thread {
+ private final JobParameters mParams;
+
+ AuditWatchingThread(JobParameters params) {
+ super("DynamicCodeLoggingService_AuditWatchingJob");
+ mParams = params;
+ }
+
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.d(TAG, "Starting AuditWatchingJob run");
+ }
+
+ if (processAuditEvents()) {
+ jobFinished(mParams, /* reschedule */ false);
+ if (DEBUG) {
+ Log.d(TAG, "Finished AuditWatchingJob run");
+ }
+ }
+ }
+
+ private boolean processAuditEvents() {
+ // Scan the event log for SELinux (avc) audit messages indicating when an
+ // (untrusted) app has executed native code from an app data
+ // file. Matches are recorded in DexLogger.
+ //
+ // These messages come from the kernel audit system via logd. (Note that
+ // some devices may not generate these messages at all, or the format may
+ // be different, in which case nothing will be recorded.)
+ //
+ // The messages use the auditd tag and the uid of the app that executed
+ // the code.
+ //
+ // A typical message might look like this:
+ // type=1400 audit(0.0:521): avc: granted { execute } for comm="executable"
+ // path="/data/data/com.dummy.app/executable" dev="sda13" ino=1655302
+ // scontext=u:r:untrusted_app_27:s0:c66,c257,c512,c768
+ // tcontext=u:object_r:app_data_file:s0:c66,c257,c512,c768 tclass=file
+ //
+ // The information we want is the uid and the path. (Note this may be
+ // either a quoted string, as shown above, or a sequence of hex-encoded
+ // bytes.)
+ //
+ // On each run we process all the matching events in the log. This may
+ // mean re-processing events we have already seen, and in any case there
+ // may be duplicate events for the same app+file. These are de-duplicated
+ // by DexLogger.
+ //
+ // Note that any app can write a message to the event log, including one
+ // that looks exactly like an AVC audit message, so the information may
+ // be spoofed by an app; in such a case the uid we see will be the app
+ // that generated the spoof message.
+
+ try {
+ int[] tags = { EventLog.getTagCode("auditd") };
+ if (tags[0] == -1) {
+ // auditd is not a registered tag on this system, so there can't be any messages
+ // of interest.
+ return true;
+ }
+
+ DexLogger dexLogger = getDexLogger();
+
+ List<EventLog.Event> events = new ArrayList<>();
+ EventLog.readEvents(tags, events);
+
+ for (int i = 0; i < events.size(); ++i) {
+ if (mAuditWatchingStopRequested) {
+ Log.w(TAG, "Stopping AuditWatchingJob run at scheduler request");
+ return false;
+ }
+
+ EventLog.Event event = events.get(i);
+
+ // Discard clearly unrelated messages as quickly as we can.
+ int uid = event.getUid();
+ if (!Process.isApplicationUid(uid)) {
+ continue;
+ }
+ Object data = event.getData();
+ if (!(data instanceof String)) {
+ continue;
+ }
+ String message = (String) data;
+ if (!message.startsWith(AVC_PREFIX)) {
+ continue;
+ }
+
+ // And then use a regular expression to verify it's one of the messages we're
+ // interested in and to extract the path of the file being loaded.
+ Matcher matcher = EXECUTE_NATIVE_AUDIT_PATTERN.matcher(message);
+ if (!matcher.matches()) {
+ continue;
+ }
+ String path = matcher.group(1);
+ if (path == null) {
+ // If the path contains spaces or various weird characters the kernel
+ // hex-encodes the bytes; we need to undo that.
+ path = unhex(matcher.group(2));
+ }
+ dexLogger.recordNative(uid, path);
+ }
+
+ return true;
+ } catch (Exception e) {
+ Log.e(TAG, "AuditWatchingJob failed", e);
+ return true;
+ }
+ }
+ }
+
+ private static String unhex(String hexEncodedPath) {
+ byte[] bytes = ByteStringUtils.fromHexToByteArray(hexEncodedPath);
+ if (bytes == null || bytes.length == 0) {
+ return "";
+ }
+ return new String(bytes);
+ }
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 8a6105cb7fa9..efafdfaf2b54 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -611,6 +611,31 @@ public class Installer extends SystemService {
}
}
+ public boolean snapshotAppData(String pkg, @UserIdInt int userId, int storageFlags)
+ throws InstallerException {
+ if (!checkBeforeRemote()) return false;
+
+ try {
+ mInstalld.snapshotAppData(null, pkg, userId, storageFlags);
+ return true;
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ public boolean restoreAppDataSnapshot(String pkg, @AppIdInt int appId, long ceDataInode,
+ String seInfo, @UserIdInt int userId, int storageFlags) throws InstallerException {
+ if (!checkBeforeRemote()) return false;
+
+ try {
+ mInstalld.restoreAppDataSnapshot(null, pkg, appId, ceDataInode, seInfo, userId,
+ storageFlags);
+ return true;
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
private static void assertValidInstructionSet(String instructionSet)
throws InstallerException {
for (String abi : Build.SUPPORTED_ABIS) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index bf4e272d4004..0ab2a7361ac0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -310,7 +310,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
in.setInput(fis, StandardCharsets.UTF_8.name());
int type;
- PackageInstallerSession currentSession = null;
while ((type = in.next()) != END_DOCUMENT) {
if (type == START_TAG) {
final String tag = in.getName();
@@ -320,9 +319,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
session = PackageInstallerSession.readFromXml(in, mInternalCallback,
mContext, mPm, mInstallThread.getLooper(), mStagingManager,
mSessionsDir, this);
- currentSession = session;
} catch (Exception e) {
- currentSession = null;
Slog.e(TAG, "Could not read session", e);
continue;
}
@@ -347,10 +344,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
addHistoricalSessionLocked(session);
}
mAllocatedSessions.put(session.sessionId, true);
- } else if (currentSession != null
- && PackageInstallerSession.TAG_CHILD_SESSION.equals(tag)) {
- currentSession.addChildSessionIdInternal(
- PackageInstallerSession.readChildSessionIdFromXml(in));
}
}
}
@@ -1132,6 +1125,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
public void onStagedSessionChanged(PackageInstallerSession session) {
writeSessionsAsync();
+ // TODO(b/118865310): don't send broadcast if system is not ready.
mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 12d335dd87ab..b8825bbd2d72 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -992,8 +992,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Read transfers from the original owner stay open, but as the session's data
// cannot be modified anymore, there is no leak of information. For staged sessions,
- // further validation may be performed by the staging manager.
+ // further validation is performed by the staging manager.
if (!params.isMultiPackage) {
+ if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+ // For APEX, validation is done by StagingManager post-commit.
+ return;
+ }
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
@@ -1001,16 +1005,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
resolveStageDirLocked();
try {
- if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
- // TODO(b/118865310): Remove this when APEX validation is done via
- // StagingManager.
- validateApexInstallLocked(pkgInfo);
- } else {
- // Verify that stage looks sane with respect to existing application.
- // This currently only ensures packageName, versionCode, and certificate
- // consistency.
- validateApkInstallLocked(pkgInfo);
- }
+ validateApkInstallLocked(pkgInfo);
} catch (PackageManagerException e) {
throw e;
} catch (Throwable e) {
@@ -1301,54 +1296,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
(params.installFlags & PackageManager.DONT_KILL_APP) != 0;
}
- @GuardedBy("mLock")
- private void validateApexInstallLocked(@Nullable PackageInfo pkgInfo)
- throws PackageManagerException {
- mResolvedStagedFiles.clear();
- mResolvedInheritedFiles.clear();
-
- try {
- resolveStageDirLocked();
- } catch (IOException e) {
- throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
- "Failed to resolve stage location", e);
- }
-
- final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
- if (ArrayUtils.isEmpty(addedFiles)) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
- }
-
- if (addedFiles.length > 1) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Only one APEX file at a time might be installed");
- }
- File addedFile = addedFiles[0];
- final ApkLite apk;
- try {
- apk = PackageParser.parseApkLite(
- addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
- } catch (PackageParserException e) {
- throw PackageManagerException.from(e);
- }
-
- mPackageName = apk.packageName;
- mVersionCode = apk.getLongVersionCode();
- mSigningDetails = apk.signingDetails;
- mResolvedBaseFile = addedFile;
-
- // STOPSHIP: Ensure that we remove the non-staged version of APEX installs in production
- // because we currently do not verify that signatures are consistent with the previously
- // installed version in that case.
- //
- // When that happens, this hack can be reverted and we can rely on APEXd to map between
- // APEX files and their package names instead of parsing it out of the AndroidManifest
- // such as here.
- if (params.appPackageName == null) {
- params.appPackageName = mPackageName;
- }
- }
-
/**
* Validate install by confirming that all application packages are have
* consistent package name, version code, and signing certificates.
@@ -1911,22 +1858,30 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
@Override
- public void addChildSessionId(int sessionId) {
- final PackageInstallerSession session = mSessionProvider.getSession(sessionId);
- if (session == null) {
+ public void addChildSessionId(int childSessionId) {
+ final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId);
+ if (childSession == null) {
throw new RemoteException("Unable to add child.",
- new PackageManagerException("Child session " + sessionId + " does not exist"),
+ new PackageManagerException("Child session " + childSessionId
+ + " does not exist"),
+ false, true).rethrowAsRuntimeException();
+ }
+ // Session groups must be consistent wrt to isStaged parameter. Non-staging session
+ // cannot be grouped with staging sessions.
+ if (this.params.isStaged ^ childSession.params.isStaged) {
+ throw new RemoteException("Unable to add child.",
+ new PackageManagerException("Child session " + childSessionId
+ + " and parent session " + this.sessionId + " do not have consistent"
+ + " staging session settings."),
false, true).rethrowAsRuntimeException();
}
synchronized (mLock) {
- final int indexOfSession = mChildSessionIds.indexOfKey(sessionId);
+ final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId);
if (indexOfSession >= 0) {
return;
}
- session.setParentSessionId(this.sessionId);
- // TODO: sanity check, if parent session is staged then child session should be
- // marked as staged.
- addChildSessionIdInternal(sessionId);
+ childSession.setParentSessionId(this.sessionId);
+ addChildSessionIdInternal(childSessionId);
}
}
@@ -2057,6 +2012,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return mStagedSessionFailed;
}
+ /** {@hide} */
+ @StagedSessionErrorCode int getStagedSessionErrorCode() {
+ return mStagedSessionErrorCode;
+ }
+
private void destroyInternal() {
synchronized (mLock) {
mSealed = true;
@@ -2221,35 +2181,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
out.endTag(null, TAG_SESSION);
}
- private static String[] readGrantedRuntimePermissions(XmlPullParser in)
- throws IOException, XmlPullParserException {
- List<String> permissions = null;
-
- final int outerDepth = in.getDepth();
- int type;
- while ((type = in.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
- if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
- continue;
- }
- if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
- String permission = readStringAttribute(in, ATTR_NAME);
- if (permissions == null) {
- permissions = new ArrayList<>();
- }
- permissions.add(permission);
- }
- }
-
- if (permissions == null) {
- return null;
- }
-
- String[] permissionsArray = new String[permissions.size()];
- permissions.toArray(permissionsArray);
- return permissionsArray;
- }
-
// Sanity check to be performed when the session is restored from an external file. Only one
// of the session states should be true, or none of them.
private static boolean isStagedSessionStateValid(boolean isReady, boolean isApplied,
@@ -2273,8 +2204,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* @param sessionProvider
* @return The newly created session
*/
- // TODO(patb,109941548): modify readFromXml to consume to the next tag session tag so we
- // can have a complete session for the constructor
public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
@NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
@NonNull PackageManagerService pm, Looper installerThread,
@@ -2314,8 +2243,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
- params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
-
final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
if (appIconFile.exists()) {
params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
@@ -2324,16 +2251,51 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final boolean isReady = readBooleanAttribute(in, ATTR_IS_READY);
final boolean isFailed = readBooleanAttribute(in, ATTR_IS_FAILED);
final boolean isApplied = readBooleanAttribute(in, ATTR_IS_APPLIED);
- final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE);
+ final int stagedSessionErrorCode = readIntAttribute(in, ATTR_STAGED_SESSION_ERROR_CODE,
+ SessionInfo.NO_ERROR);
if (!isStagedSessionStateValid(isReady, isApplied, isFailed)) {
throw new IllegalArgumentException("Can't restore staged session with invalid state.");
}
+ // Parse sub tags of this session, typically used for repeated values / arrays.
+ // Sub tags can come in any order, therefore we need to keep track of what we find while
+ // parsing and only set the right values at the end.
+
+ // Store the current depth. We should stop parsing when we reach an end tag at the same
+ // depth.
+ List<String> permissions = new ArrayList<>();
+ List<Integer> childSessionIds = new ArrayList<>();
+ int outerDepth = in.getDepth();
+ int type;
+ while ((type = in.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
+ permissions.add(readStringAttribute(in, ATTR_NAME));
+ }
+ if (TAG_CHILD_SESSION.equals(in.getName())) {
+ childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID));
+ }
+ }
+
+ if (permissions.size() > 0) {
+ params.grantedRuntimePermissions = permissions.stream().toArray(String[]::new);
+ }
+
+ int[] childSessionIdsArray;
+ if (childSessionIds.size() > 0) {
+ childSessionIdsArray = childSessionIds.stream().mapToInt(i -> i).toArray();
+ } else {
+ childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY;
+ }
+
return new PackageInstallerSession(callback, context, pm, sessionProvider,
installerThread, stagingManager, sessionId, userId, installerPackageName,
installerUid, params, createdMillis, stageDir, stageCid, prepared, sealed,
- EMPTY_CHILD_SESSION_ARRAY, parentSessionId, isReady, isFailed, isApplied,
+ childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
stagedSessionErrorCode);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 09fe26d19019..b8342cf8f81e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -42,6 +42,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRAD
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
+import static android.content.pm.PackageManager.INSTALL_ALLOW_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
@@ -201,6 +202,7 @@ import android.content.pm.VersionedPackage;
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
+import android.content.rollback.IRollbackManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
@@ -13872,6 +13874,38 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
+ // If this is an update to a package that might be potentially downgraded, then we
+ // need to check with the rollback manager whether there's any userdata that might
+ // need to be restored for the package.
+ //
+ // TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
+ if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
+ IRollbackManager rm = IRollbackManager.Stub.asInterface(
+ ServiceManager.getService(Context.ROLLBACK_SERVICE));
+
+ final String packageName = res.pkg.applicationInfo.packageName;
+ final String seInfo = res.pkg.applicationInfo.seInfo;
+ final PackageSetting ps;
+ int appId = -1;
+ long ceDataInode = -1;
+ synchronized (mSettings) {
+ ps = mSettings.getPackageLPr(packageName);
+ if (ps != null) {
+ appId = ps.appId;
+ ceDataInode = ps.getCeDataInode(userId);
+ }
+ }
+
+ if (ps != null) {
+ try {
+ rm.restoreUserData(packageName, userId, appId, ceDataInode, seInfo, token);
+ } catch (RemoteException re) {
+ // Cannot happen, the RollbackManager is hosted in the same process.
+ }
+ doRestore = true;
+ }
+ }
+
if (!doRestore) {
// No restore possible, or the Backup Manager was mysteriously not
// available -- just fire the post-install work request directly.
@@ -14569,6 +14603,9 @@ public class PackageManagerService extends IPackageManager.Stub
enableRollbackIntent.putExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS,
installFlags);
+ enableRollbackIntent.putExtra(
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS,
+ resolveUserIds(args.user.getIdentifier()));
enableRollbackIntent.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
PACKAGE_MIME_TYPE);
enableRollbackIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -23791,6 +23828,11 @@ public class PackageManagerService extends IPackageManager.Stub
}
return mArtManagerService.compileLayouts(pkg);
}
+
+ @Override
+ public void finishPackageInstall(int token, boolean didLaunch) {
+ PackageManagerService.this.finishPackageInstall(token, didLaunch);
+ }
}
@GuardedBy("mPackages")
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 7bab0bbc7964..5311c2a55931 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -41,7 +41,9 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
/**
* This class handles staged install sessions, i.e. install sessions that require packages to
@@ -126,12 +128,24 @@ public class StagingManager {
return false;
}
- private static boolean submitSessionToApexService(int sessionId, ApexInfoList apexInfoList) {
+ private static boolean submitSessionToApexService(@NonNull PackageInstallerSession session,
+ List<PackageInstallerSession> childSessions,
+ ApexInfoList apexInfoList) {
+ return sendSubmitStagedSessionRequest(
+ session.sessionId,
+ childSessions != null
+ ? childSessions.stream().mapToInt(s -> s.sessionId).toArray() :
+ new int[]{},
+ apexInfoList);
+ }
+
+ private static boolean sendSubmitStagedSessionRequest(
+ int sessionId, int[] childSessionIds, ApexInfoList apexInfoList) {
final IApexService apex = IApexService.Stub.asInterface(
ServiceManager.getService("apexservice"));
boolean success;
try {
- success = apex.submitStagedSession(sessionId, new int[0], apexInfoList);
+ success = apex.submitStagedSession(sessionId, childSessionIds, apexInfoList);
} catch (RemoteException re) {
Slog.e(TAG, "Unable to contact apexservice", re);
return false;
@@ -139,31 +153,49 @@ public class StagingManager {
return success;
}
+ private static boolean isApexSession(@NonNull PackageInstallerSession session) {
+ return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0;
+ }
+
private void preRebootVerification(@NonNull PackageInstallerSession session) {
boolean success = true;
- if ((session.params.installFlags & PackageManager.INSTALL_APEX) != 0) {
-
- final ApexInfoList apexInfoList = new ApexInfoList();
-
- if (!submitSessionToApexService(session.sessionId, apexInfoList)) {
- success = false;
- } else {
- // For APEXes, we validate the signature here before we mark the session as ready,
- // so we fail the session early if there is a signature mismatch. For APKs, the
- // signature verification will be done by the package manager at the point at which
- // it applies the staged install.
- //
- // TODO: Decide whether we want to fail fast by detecting signature mismatches right
- // away.
- for (ApexInfo apexPackage : apexInfoList.apexInfos) {
- if (!validateApexSignatureLocked(apexPackage.packagePath,
- apexPackage.packageName)) {
- success = false;
- break;
- }
+
+ final ApexInfoList apexInfoList = new ApexInfoList();
+ // APEX checks. For single-package sessions, check if they contain an APEX. For
+ // multi-package sessions, find all the child sessions that contain an APEX.
+ if (!session.isMultiPackage()
+ && isApexSession(session)) {
+ success = submitSessionToApexService(session, null, apexInfoList);
+ } else if (session.isMultiPackage()) {
+ List<PackageInstallerSession> childSessions =
+ Arrays.stream(session.getChildSessionIds())
+ // Retrieve cached sessions matching ids.
+ .mapToObj(i -> mStagedSessions.get(i))
+ // Filter only the ones containing APEX.
+ .filter(childSession -> isApexSession(childSession))
+ .collect(Collectors.toList());
+ if (!childSessions.isEmpty()) {
+ success = submitSessionToApexService(session, childSessions, apexInfoList);
+ } // else this is a staged multi-package session with no APEX files.
+ }
+
+ if (success && (apexInfoList.apexInfos.length > 0)) {
+ // For APEXes, we validate the signature here before we mark the session as ready,
+ // so we fail the session early if there is a signature mismatch. For APKs, the
+ // signature verification will be done by the package manager at the point at which
+ // it applies the staged install.
+ //
+ // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
+ // right away.
+ for (ApexInfo apexPackage : apexInfoList.apexInfos) {
+ if (!validateApexSignatureLocked(apexPackage.packagePath,
+ apexPackage.packageName)) {
+ success = false;
+ break;
}
}
}
+
if (success) {
session.setStagedSessionReady();
} else {
@@ -206,15 +238,59 @@ public class StagingManager {
}
}
- void abortSession(@NonNull PackageInstallerSession sessionInfo) {
- updateStoredSession(sessionInfo);
+ void abortSession(@NonNull PackageInstallerSession session) {
synchronized (mStagedSessions) {
- mStagedSessions.remove(sessionInfo.sessionId);
+ updateStoredSession(session);
+ mStagedSessions.remove(session.sessionId);
+ }
+ }
+
+ @GuardedBy("mStagedSessions")
+ private boolean isMultiPackageSessionComplete(@NonNull PackageInstallerSession session) {
+ // This method assumes that the argument is either a parent session of a multi-package
+ // i.e. isMultiPackage() returns true, or that it is a child session, i.e.
+ // hasParentSessionId() returns true.
+ if (session.isMultiPackage()) {
+ // Parent session of a multi-package group. Check that we restored all the children.
+ for (int childSession : session.getChildSessionIds()) {
+ if (mStagedSessions.get(childSession) == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+ if (session.hasParentSessionId()) {
+ PackageInstallerSession parent = mStagedSessions.get(session.getParentSessionId());
+ if (parent == null) {
+ return false;
+ }
+ return isMultiPackageSessionComplete(parent);
}
+ Slog.wtf(TAG, "Attempting to restore an invalid multi-package session.");
+ return false;
}
void restoreSession(@NonNull PackageInstallerSession session) {
- updateStoredSession(session);
+ PackageInstallerSession sessionToResume = session;
+ synchronized (mStagedSessions) {
+ mStagedSessions.append(session.sessionId, session);
+ // For multi-package sessions, we don't know in which order they will be restored. We
+ // need to wait until we have restored all the session in a group before restoring them.
+ if (session.isMultiPackage() || session.hasParentSessionId()) {
+ if (!isMultiPackageSessionComplete(session)) {
+ // Still haven't recovered all sessions of the group, return.
+ return;
+ }
+ // Group recovered, find the parent if necessary and resume the installation.
+ if (session.hasParentSessionId()) {
+ sessionToResume = mStagedSessions.get(session.getParentSessionId());
+ }
+ }
+ }
+ checkStateAndResume(sessionToResume);
+ }
+
+ private void checkStateAndResume(@NonNull PackageInstallerSession session) {
// Check the state of the session and decide what to do next.
if (session.isStagedSessionFailed() || session.isStagedSessionApplied()) {
// Final states, nothing to do.
@@ -227,6 +303,8 @@ public class StagingManager {
} else {
// Session had already being marked ready. Start the checks to verify if there is any
// follow-up work.
+ // TODO(b/118865310): should this be synchronous to ensure it completes before
+ // systemReady() finishes?
mBgHandler.post(() -> resumeSession(session));
}
}
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
index 78fa82c6bcdd..59cc0cfeef45 100644
--- a/services/core/java/com/android/server/pm/dex/DexLogger.java
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -16,11 +16,15 @@
package com.android.server.pm.dex;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.FILE_TYPE_DEX;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.FILE_TYPE_NATIVE;
+
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.os.FileUtils;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.util.ByteStringUtils;
import android.util.EventLog;
@@ -35,20 +39,23 @@ import com.android.server.pm.dex.PackageDynamicCodeLoading.DynamicCodeFile;
import com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
import java.io.File;
+import java.io.IOException;
import java.util.Map;
import java.util.Set;
/**
- * This class is responsible for logging data about secondary dex files.
- * The data logged includes hashes of the name and content of each file.
+ * This class is responsible for logging data about secondary dex files and, despite the name,
+ * native code executed from an app's private directory. The data logged includes hashes of the
+ * name and content of each file.
*/
public class DexLogger {
private static final String TAG = "DexLogger";
- // Event log tag & subtag used for SafetyNet logging of dynamic
- // code loading (DCL) - see b/63927552.
+ // Event log tag & subtags used for SafetyNet logging of dynamic code loading (DCL) -
+ // see b/63927552.
private static final int SNET_TAG = 0x534e4554;
- private static final String DCL_SUBTAG = "dcl";
+ private static final String DCL_DEX_SUBTAG = "dcl";
+ private static final String DCL_NATIVE_SUBTAG = "dcln";
private final IPackageManager mPackageManager;
private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
@@ -114,12 +121,11 @@ public class DexLogger {
}
int storageFlags;
- if (appInfo.deviceProtectedDataDir != null
- && FileUtils.contains(appInfo.deviceProtectedDataDir, filePath)) {
- storageFlags = StorageManager.FLAG_STORAGE_DE;
- } else if (appInfo.credentialProtectedDataDir != null
- && FileUtils.contains(appInfo.credentialProtectedDataDir, filePath)) {
+
+ if (fileIsUnder(filePath, appInfo.credentialProtectedDataDir)) {
storageFlags = StorageManager.FLAG_STORAGE_CE;
+ } else if (fileIsUnder(filePath, appInfo.deviceProtectedDataDir)) {
+ storageFlags = StorageManager.FLAG_STORAGE_DE;
} else {
Slog.e(TAG, "Could not infer CE/DE storage for path " + filePath);
needWrite |= mPackageDynamicCodeLoading.removeFile(packageName, filePath, userId);
@@ -139,6 +145,9 @@ public class DexLogger {
+ ": " + e.getMessage());
}
+ String subtag = fileInfo.mFileType == FILE_TYPE_DEX
+ ? DCL_DEX_SUBTAG
+ : DCL_NATIVE_SUBTAG;
String fileName = new File(filePath).getName();
String message = PackageUtils.computeSha256Digest(fileName.getBytes());
@@ -165,7 +174,7 @@ public class DexLogger {
}
if (loadingUid != -1) {
- writeDclEvent(loadingUid, message);
+ writeDclEvent(subtag, loadingUid, message);
}
}
}
@@ -175,21 +184,58 @@ public class DexLogger {
}
}
+ private boolean fileIsUnder(String filePath, String directoryPath) {
+ if (directoryPath == null) {
+ return false;
+ }
+
+ try {
+ return FileUtils.contains(new File(directoryPath).getCanonicalPath(),
+ new File(filePath).getCanonicalPath());
+ } catch (IOException e) {
+ return false;
+ }
+ }
+
@VisibleForTesting
PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
return mPackageDynamicCodeLoading.getPackageDynamicCodeInfo(packageName);
}
@VisibleForTesting
- void writeDclEvent(int uid, String message) {
- EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, uid, message);
+ void writeDclEvent(String subtag, int uid, String message) {
+ EventLog.writeEvent(SNET_TAG, subtag, uid, message);
}
- void record(int loaderUserId, String dexPath,
- String owningPackageName, String loadingPackageName) {
+ void recordDex(int loaderUserId, String dexPath, String owningPackageName,
+ String loadingPackageName) {
if (mPackageDynamicCodeLoading.record(owningPackageName, dexPath,
- PackageDynamicCodeLoading.FILE_TYPE_DEX, loaderUserId,
- loadingPackageName)) {
+ FILE_TYPE_DEX, loaderUserId, loadingPackageName)) {
+ mPackageDynamicCodeLoading.maybeWriteAsync();
+ }
+ }
+
+ /**
+ * Record that an app running in the specified uid has executed native code from the file at
+ * {@link path}.
+ */
+ public void recordNative(int loadingUid, String path) {
+ String[] packages;
+ try {
+ packages = mPackageManager.getPackagesForUid(loadingUid);
+ if (packages == null || packages.length == 0) {
+ return;
+ }
+ } catch (RemoteException e) {
+ // Can't happen, we're local.
+ return;
+ }
+
+ String loadingPackageName = packages[0];
+ int loadingUserId = UserHandle.getUserId(loadingUid);
+
+ if (mPackageDynamicCodeLoading.record(loadingPackageName, path,
+ FILE_TYPE_NATIVE, loadingUserId, loadingPackageName)) {
mPackageDynamicCodeLoading.maybeWriteAsync();
}
}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index b54683673e7b..1a2b11559446 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -235,7 +235,7 @@ public class DexManager {
continue;
}
- mDexLogger.record(loaderUserId, dexPath, searchResult.mOwningPackageName,
+ mDexLogger.recordDex(loaderUserId, dexPath, searchResult.mOwningPackageName,
loadingAppInfo.packageName);
if (classLoaderContexts != null) {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
index 6d4bc8291611..cc26c9b5f76c 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
@@ -53,6 +53,9 @@ class PackageDynamicCodeLoading extends AbstractStatsBase<Void> {
// is represented in the text file format.)
static final int FILE_TYPE_DEX = 'D';
+ // Type code to indicate a secondary file containing native code.
+ static final int FILE_TYPE_NATIVE = 'N';
+
private static final String TAG = "PackageDynamicCodeLoading";
private static final String FILE_VERSION_HEADER = "DCL1";
@@ -107,7 +110,7 @@ class PackageDynamicCodeLoading extends AbstractStatsBase<Void> {
*/
boolean record(String owningPackageName, String filePath, int fileType, int ownerUserId,
String loadingPackageName) {
- if (fileType != FILE_TYPE_DEX) {
+ if (!isValidFileType(fileType)) {
throw new IllegalArgumentException("Bad file type: " + fileType);
}
synchronized (mLock) {
@@ -120,6 +123,10 @@ class PackageDynamicCodeLoading extends AbstractStatsBase<Void> {
}
}
+ private static boolean isValidFileType(int fileType) {
+ return fileType == FILE_TYPE_DEX || fileType == FILE_TYPE_NATIVE;
+ }
+
/**
* Return all packages that contain records of secondary dex files. (Note that data updates
* asynchronously, so {@link #getPackageDynamicCodeInfo} may still return null if passed
@@ -407,7 +414,7 @@ class PackageDynamicCodeLoading extends AbstractStatsBase<Void> {
if (packages.length == 0) {
throw new IOException("Malformed line: " + line);
}
- if (type != FILE_TYPE_DEX) {
+ if (!isValidFileType(type)) {
throw new IOException("Unknown file type: " + line);
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index d5af31300bf5..20d6d4e2ca79 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1290,6 +1290,7 @@ public final class DefaultPermissionGrantPolicy {
return mContext.getPackageManager().getPackageInfo(pkg,
DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags);
} catch (NameNotFoundException e) {
+ Slog.e(TAG, "PackageNot found: " + pkg, e);
return null;
}
}
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 5516b234925b..c0517fdc99d0 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -342,10 +342,21 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
@WorkerThread
private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
- if (listeners == null) {
- return;
+ if (listeners != null) {
+ notifyRoleHoldersChangedForListeners(listeners, roleName, userId);
+ }
+
+ RemoteCallbackList<IOnRoleHoldersChangedListener> allUserListeners = getListeners(
+ UserHandle.USER_ALL);
+ if (allUserListeners != null) {
+ notifyRoleHoldersChangedForListeners(allUserListeners, roleName, userId);
}
+ }
+ @WorkerThread
+ private void notifyRoleHoldersChangedForListeners(
+ @NonNull RemoteCallbackList<IOnRoleHoldersChangedListener> listeners,
+ @NonNull String roleName, @UserIdInt int userId) {
int broadcastCount = listeners.beginBroadcast();
try {
for (int i = 0; i < broadcastCount; i++) {
@@ -395,7 +406,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
Slog.e(LOG_TAG, "user " + userId + " does not exist");
return Collections.emptyList();
}
- userId = handleIncomingUser(userId, "getRoleHoldersAsUser");
+ userId = handleIncomingUser(userId, "getRoleHoldersAsUser", false);
getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
"getRoleHoldersAsUser");
@@ -423,7 +434,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
Slog.e(LOG_TAG, "user " + userId + " does not exist");
return;
}
- userId = handleIncomingUser(userId, "addRoleHolderAsUser");
+ userId = handleIncomingUser(userId, "addRoleHolderAsUser", false);
getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
"addRoleHolderAsUser");
@@ -440,7 +451,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
Slog.e(LOG_TAG, "user " + userId + " does not exist");
return;
}
- userId = handleIncomingUser(userId, "removeRoleHolderAsUser");
+ userId = handleIncomingUser(userId, "removeRoleHolderAsUser", false);
getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
"removeRoleHolderAsUser");
@@ -457,7 +468,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
Slog.e(LOG_TAG, "user " + userId + " does not exist");
return;
}
- userId = handleIncomingUser(userId, "clearRoleHoldersAsUser");
+ userId = handleIncomingUser(userId, "clearRoleHoldersAsUser", false);
getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
"clearRoleHoldersAsUser");
@@ -468,11 +479,12 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
public void addOnRoleHoldersChangedListenerAsUser(
@NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
Preconditions.checkNotNull(listener, "listener cannot be null");
- if (!mUserManagerInternal.exists(userId)) {
+ if (userId != UserHandle.USER_ALL && !mUserManagerInternal.exists(userId)) {
Slog.e(LOG_TAG, "user " + userId + " does not exist");
return;
}
- userId = handleIncomingUser(userId, "addOnRoleHoldersChangedListenerAsUser");
+ userId = handleIncomingUser(userId, "addOnRoleHoldersChangedListenerAsUser",
+ true);
getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
"addOnRoleHoldersChangedListenerAsUser");
@@ -485,11 +497,12 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
public void removeOnRoleHoldersChangedListenerAsUser(
@NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
Preconditions.checkNotNull(listener, "listener cannot be null");
- if (!mUserManagerInternal.exists(userId)) {
+ if (userId != UserHandle.USER_ALL && !mUserManagerInternal.exists(userId)) {
Slog.e(LOG_TAG, "user " + userId + " does not exist");
return;
}
- userId = handleIncomingUser(userId, "removeOnRoleHoldersChangedListenerAsUser");
+ userId = handleIncomingUser(userId, "removeOnRoleHoldersChangedListenerAsUser",
+ true);
getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
"removeOnRoleHoldersChangedListenerAsUser");
@@ -553,9 +566,10 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
}
@CheckResult
- private int handleIncomingUser(@UserIdInt int userId, @NonNull String name) {
+ private int handleIncomingUser(@UserIdInt int userId, @NonNull String name,
+ boolean allowAll) {
return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
- false, true, name, null);
+ allowAll, true, name, null);
}
@Override
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index d12f7eda9a1b..085b4afac0e2 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -43,34 +43,28 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.storage.StorageManager;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageManagerServiceUtils;
-import libcore.io.IoUtils;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.io.File;
import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.file.Files;
import java.time.Instant;
-import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* Implementation of service that manages APK level rollbacks.
@@ -108,46 +102,22 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
@GuardedBy("mLock")
private List<RollbackInfo> mRecentlyExecutedRollbacks;
- // Data for available rollbacks and recently executed rollbacks is
- // persisted in storage. Assuming the rollback data directory is
- // /data/rollback, we use the following directory structure
- // to store this data:
- // /data/rollback/
- // available/
- // XXX/
- // com.package.A/
- // base.apk
- // info.json
- // enabled.txt
- // YYY/
- // com.package.B/
- // base.apk
- // info.json
- // enabled.txt
- // recently_executed.json
- //
- // * XXX, YYY are random strings from Files.createTempDirectory
- // * info.json contains the package version to roll back from/to.
- // * enabled.txt contains a timestamp for when the rollback was first
- // made available. This file is not written until the rollback is made
- // available.
- //
- // TODO: Use AtomicFile for all the .json files?
- private final File mRollbackDataDir;
- private final File mAvailableRollbacksDir;
- private final File mRecentlyExecutedRollbacksFile;
+ private final RollbackStore mRollbackStore;
private final Context mContext;
private final HandlerThread mHandlerThread;
+ private final Installer mInstaller;
RollbackManagerServiceImpl(Context context) {
mContext = context;
+ // Note that we're calling onStart here because this object is only constructed on
+ // SystemService#onStart.
+ mInstaller = new Installer(mContext);
+ mInstaller.onStart();
mHandlerThread = new HandlerThread("RollbackManagerServiceHandler");
mHandlerThread.start();
- mRollbackDataDir = new File(Environment.getDataDirectory(), "rollback");
- mAvailableRollbacksDir = new File(mRollbackDataDir, "available");
- mRecentlyExecutedRollbacksFile = new File(mRollbackDataDir, "recently_executed.json");
+ mRollbackStore = new RollbackStore(new File(Environment.getDataDirectory(), "rollback"));
// Kick off loading of the rollback data from strorage in a background
// thread.
@@ -158,8 +128,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
// expiration.
getHandler().post(() -> ensureRollbackDataLoaded());
- PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
- installer.registerSessionCallback(new SessionCallback(), getHandler());
+ PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+ packageInstaller.registerSessionCallback(new SessionCallback(), getHandler());
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
@@ -196,10 +166,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
int installFlags = intent.getIntExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
+ int[] installedUsers = intent.getIntArrayExtra(
+ PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS);
File newPackageCodePath = new File(intent.getData().getPath());
getHandler().post(() -> {
- boolean success = enableRollback(installFlags, newPackageCodePath);
+ boolean success = enableRollback(installFlags, newPackageCodePath,
+ installedUsers);
int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
if (!success) {
ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
@@ -357,8 +330,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
PackageManager pm = context.getPackageManager();
try {
PackageInstaller packageInstaller = pm.getPackageInstaller();
+ String installerPackageName = pm.getInstallerPackageName(targetPackageName);
+ if (installerPackageName == null) {
+ sendFailure(statusReceiver, "Cannot find installer package");
+ return;
+ }
PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ parentParams.setInstallerPackageName(installerPackageName);
parentParams.setAllowDowngrade(true);
parentParams.setMultiPackage();
int parentSessionId = packageInstaller.createSession(parentParams);
@@ -367,6 +346,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
for (PackageRollbackInfo info : data.packages) {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ params.setInstallerPackageName(installerPackageName);
params.setAllowDowngrade(true);
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
@@ -387,28 +367,30 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
parentSession.addChildSessionId(sessionId);
}
- final LocalIntentReceiver receiver = new LocalIntentReceiver();
- parentSession.commit(receiver.getIntentSender());
+ final LocalIntentReceiver receiver = new LocalIntentReceiver(
+ (Intent result) -> {
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ sendFailure(statusReceiver, "Rollback downgrade install failed: "
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
+ return;
+ }
- Intent result = receiver.getResult();
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- sendFailure(statusReceiver, "Rollback downgrade install failed: "
- + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
- return;
- }
+ addRecentlyExecutedRollback(rollback);
+ sendSuccess(statusReceiver);
- addRecentlyExecutedRollback(rollback);
- sendSuccess(statusReceiver);
+ Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
+ Uri.fromParts("package", targetPackageName,
+ Manifest.permission.MANAGE_ROLLBACKS));
- Intent broadcast = new Intent(Intent.ACTION_PACKAGE_ROLLBACK_EXECUTED,
- Uri.fromParts("package", targetPackageName,
- Manifest.permission.MANAGE_ROLLBACKS));
+ // TODO: This call emits the warning "Calling a method in the
+ // system process without a qualified user". Fix that.
+ mContext.sendBroadcast(broadcast);
+ }
+ );
- // TODO: This call emits the warning "Calling a method in the
- // system process without a qualified user". Fix that.
- mContext.sendBroadcast(broadcast);
+ parentSession.commit(receiver.getIntentSender());
} catch (IOException e) {
Log.e(TAG, "Unable to roll back " + targetPackageName, e);
sendFailure(statusReceiver, "IOException: " + e.toString());
@@ -447,7 +429,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
for (PackageRollbackInfo info : data.packages) {
if (info.packageName.equals(packageName)) {
iter.remove();
- removeFile(data.backupDir);
+ mRollbackStore.deleteAvailableRollback(data);
break;
}
}
@@ -474,87 +456,20 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
@GuardedBy("mLock")
private void ensureRollbackDataLoadedLocked() {
if (mAvailableRollbacks == null) {
- loadRollbackDataLocked();
+ loadAllRollbackDataLocked();
}
}
/**
- * Load rollback data from storage.
+ * Load all rollback data from storage.
* Note: We do potentially heavy IO here while holding mLock, because we
* have to have the rollback data loaded before we can do anything else
* meaningful.
*/
@GuardedBy("mLock")
- private void loadRollbackDataLocked() {
- mAvailableRollbacksDir.mkdirs();
- mAvailableRollbacks = new ArrayList<>();
- for (File rollbackDir : mAvailableRollbacksDir.listFiles()) {
- File enabledFile = new File(rollbackDir, "enabled.txt");
- // TODO: Delete any directories without an enabled.txt? That could
- // potentially delete pending rollback data if reloadPersistedData
- // is called, though there's no reason besides testing for that to
- // be called.
- if (rollbackDir.isDirectory() && enabledFile.isFile()) {
- RollbackData data = new RollbackData(rollbackDir);
- try {
- PackageRollbackInfo info = null;
- for (File packageDir : rollbackDir.listFiles()) {
- if (packageDir.isDirectory()) {
- File jsonFile = new File(packageDir, "info.json");
- String jsonString = IoUtils.readFileAsString(
- jsonFile.getAbsolutePath());
- JSONObject jsonObject = new JSONObject(jsonString);
- String packageName = jsonObject.getString("packageName");
- long higherVersionCode = jsonObject.getLong("higherVersionCode");
- long lowerVersionCode = jsonObject.getLong("lowerVersionCode");
-
- data.packages.add(new PackageRollbackInfo(packageName,
- new PackageRollbackInfo.PackageVersion(higherVersionCode),
- new PackageRollbackInfo.PackageVersion(lowerVersionCode)));
- }
- }
-
- if (data.packages.isEmpty()) {
- throw new IOException("No package rollback info found");
- }
-
- String enabledString = IoUtils.readFileAsString(enabledFile.getAbsolutePath());
- data.timestamp = Instant.parse(enabledString.trim());
- mAvailableRollbacks.add(data);
- } catch (IOException | JSONException | DateTimeParseException e) {
- Log.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
- removeFile(rollbackDir);
- }
- }
- }
-
- mRecentlyExecutedRollbacks = new ArrayList<>();
- if (mRecentlyExecutedRollbacksFile.exists()) {
- try {
- // TODO: How to cope with changes to the format of this file from
- // when RollbackStore is updated in the future?
- String jsonString = IoUtils.readFileAsString(
- mRecentlyExecutedRollbacksFile.getAbsolutePath());
- JSONObject object = new JSONObject(jsonString);
- JSONArray array = object.getJSONArray("recentlyExecuted");
- for (int i = 0; i < array.length(); ++i) {
- JSONObject element = array.getJSONObject(i);
- String packageName = element.getString("packageName");
- long higherVersionCode = element.getLong("higherVersionCode");
- long lowerVersionCode = element.getLong("lowerVersionCode");
- PackageRollbackInfo target = new PackageRollbackInfo(packageName,
- new PackageRollbackInfo.PackageVersion(higherVersionCode),
- new PackageRollbackInfo.PackageVersion(lowerVersionCode));
- RollbackInfo rollback = new RollbackInfo(target);
- mRecentlyExecutedRollbacks.add(rollback);
- }
- } catch (IOException | JSONException e) {
- // TODO: What to do here? Surely we shouldn't just forget about
- // everything after the point of exception?
- Log.e(TAG, "Failed to read recently executed rollbacks", e);
- }
- }
-
+ private void loadAllRollbackDataLocked() {
+ mAvailableRollbacks = mRollbackStore.loadAvailableRollbacks();
+ mRecentlyExecutedRollbacks = mRollbackStore.loadRecentlyExecutedRollbacks();
scheduleExpiration(0);
}
@@ -578,7 +493,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
if (info.packageName.equals(packageName)
&& !info.higherVersion.equals(installedVersion)) {
iter.remove();
- removeFile(data.backupDir);
+ mRollbackStore.deleteAvailableRollback(data);
break;
}
}
@@ -606,38 +521,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
}
if (changed) {
- saveRecentlyExecutedRollbacksLocked();
- }
- }
- }
-
- /**
- * Write the list of recently executed rollbacks to storage.
- * Note: This happens while mLock is held, which should be okay because we
- * expect executed rollbacks to be modified only in exceptional cases.
- */
- @GuardedBy("mLock")
- private void saveRecentlyExecutedRollbacksLocked() {
- try {
- JSONObject json = new JSONObject();
- JSONArray array = new JSONArray();
- json.put("recentlyExecuted", array);
-
- for (int i = 0; i < mRecentlyExecutedRollbacks.size(); ++i) {
- RollbackInfo rollback = mRecentlyExecutedRollbacks.get(i);
- JSONObject element = new JSONObject();
- element.put("packageName", rollback.targetPackage.packageName);
- element.put("higherVersionCode", rollback.targetPackage.higherVersion.versionCode);
- element.put("lowerVersionCode", rollback.targetPackage.lowerVersion.versionCode);
- array.put(element);
+ mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks);
}
-
- PrintWriter pw = new PrintWriter(mRecentlyExecutedRollbacksFile);
- pw.println(json.toString());
- pw.close();
- } catch (IOException | JSONException e) {
- // TODO: What to do here?
- Log.e(TAG, "Failed to save recently executed rollbacks", e);
}
}
@@ -650,7 +535,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
synchronized (mLock) {
ensureRollbackDataLoadedLocked();
mRecentlyExecutedRollbacks.add(rollback);
- saveRecentlyExecutedRollbacksLocked();
+ mRollbackStore.saveRecentlyExecutedRollbacks(mRecentlyExecutedRollbacks);
}
}
@@ -701,7 +586,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
RollbackData data = iter.next();
if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) {
iter.remove();
- removeFile(data.backupDir);
+ mRollbackStore.deleteAvailableRollback(data);
} else if (oldest == null || oldest.isAfter(data.timestamp)) {
oldest = data.timestamp;
}
@@ -748,12 +633,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
* staged for install with rollback enabled. Called before the package has
* been installed.
*
- * @param id the id of the enable rollback request
* @param installFlags information about what is being installed.
* @param newPackageCodePath path to the package about to be installed.
+ * @param installedUsers the set of users for which a given package is installed.
* @return true if enabling the rollback succeeds, false otherwise.
*/
- private boolean enableRollback(int installFlags, File newPackageCodePath) {
+ private boolean enableRollback(int installFlags, File newPackageCodePath,
+ int[] installedUsers) {
if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
Log.e(TAG, "Rollbacks not supported for instant app install");
return false;
@@ -818,6 +704,25 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
PackageRollbackInfo.PackageVersion installedVersion =
new PackageRollbackInfo.PackageVersion(installedPackage.getLongVersionCode());
+ for (int user : installedUsers) {
+ final int storageFlags;
+ if (StorageManager.isFileEncryptedNativeOrEmulated()
+ && !StorageManager.isUserKeyUnlocked(user)) {
+ // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy
+ // across app user data until the user unlocks their device.
+ Log.e(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup.");
+ storageFlags = Installer.FLAG_STORAGE_DE;
+ } else {
+ storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE;
+ }
+
+ try {
+ mInstaller.snapshotAppData(packageName, user, storageFlags);
+ } catch (InstallerException ie) {
+ Log.e(TAG, "Unable to create app data snapshot for: " + packageName, ie);
+ }
+ }
+
PackageRollbackInfo info = new PackageRollbackInfo(
packageName, newVersion, installedVersion);
@@ -827,9 +732,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
mChildSessions.put(childSessionId, parentSessionId);
data = mPendingRollbacks.get(parentSessionId);
if (data == null) {
- File backupDir = Files.createTempDirectory(
- mAvailableRollbacksDir.toPath(), null).toFile();
- data = new RollbackData(backupDir);
+ data = mRollbackStore.createAvailableRollback();
mPendingRollbacks.put(parentSessionId, data);
}
data.packages.add(info);
@@ -839,78 +742,77 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
return false;
}
- File packageDir = new File(data.backupDir, packageName);
+ File packageDir = mRollbackStore.packageCodePathForAvailableRollback(data, packageName);
packageDir.mkdirs();
- try {
- JSONObject json = new JSONObject();
- json.put("packageName", packageName);
- json.put("higherVersionCode", newVersion.versionCode);
- json.put("lowerVersionCode", installedVersion.versionCode);
-
- File jsonFile = new File(packageDir, "info.json");
- PrintWriter pw = new PrintWriter(jsonFile);
- pw.println(json.toString());
- pw.close();
- } catch (IOException | JSONException e) {
- Log.e(TAG, "Unable to create rollback for " + packageName, e);
- removeFile(packageDir);
- return false;
- }
// TODO: Copy by hard link instead to save on cpu and storage space?
int status = PackageManagerServiceUtils.copyPackage(installedPackage.codePath, packageDir);
if (status != PackageManager.INSTALL_SUCCEEDED) {
Log.e(TAG, "Unable to copy package for rollback for " + packageName);
- removeFile(packageDir);
return false;
}
return true;
}
- // TODO: Don't copy this from PackageManagerShellCommand like this?
- private static class LocalIntentReceiver {
- private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
+ @Override
+ public void restoreUserData(String packageName, int userId, int appId, long ceDataInode,
+ String seInfo, int token) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("restoureUserData may only be called by the system.");
+ }
+
+ getHandler().post(() -> {
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ // TODO(narayan): Should we make sure we're in the middle of a session commit for a
+ // a package with this package name ? Otherwise it's possible we may roll back data
+ // for some other downgrade.
+ if (getRollbackForPackage(packageName) == null) {
+ pmi.finishPackageInstall(token, false);
+ return;
+ }
+
+ final int storageFlags;
+ if (StorageManager.isFileEncryptedNativeOrEmulated()
+ && !StorageManager.isUserKeyUnlocked(userId)) {
+ // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy
+ // across app user data until the user unlocks their device.
+ Log.e(TAG, "User: " + userId + " isn't unlocked, skipping CE userdata restore.");
+
+ storageFlags = Installer.FLAG_STORAGE_DE;
+ } else {
+ storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE;
+ }
+
+ try {
+ mInstaller.restoreAppDataSnapshot(packageName, appId, ceDataInode,
+ seInfo, userId, storageFlags);
+ } catch (InstallerException ie) {
+ Log.e(TAG, "Unable to restore app data snapshot: " + packageName, ie);
+ }
+
+ pmi.finishPackageInstall(token, false);
+ });
+ }
+
+ private class LocalIntentReceiver {
+ final Consumer<Intent> mConsumer;
+
+ LocalIntentReceiver(Consumer<Intent> consumer) {
+ mConsumer = consumer;
+ }
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
- try {
- mResult.offer(intent, 5, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
+ getHandler().post(() -> mConsumer.accept(intent));
}
};
public IntentSender getIntentSender() {
return new IntentSender((IIntentSender) mLocalSender);
}
-
- public Intent getResult() {
- try {
- return mResult.take();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- /**
- * Deletes a file completely.
- * If the file is a directory, its contents are deleted as well.
- * Has no effect if the directory does not exist.
- */
- private void removeFile(File file) {
- if (file.isDirectory()) {
- for (File child : file.listFiles()) {
- removeFile(child);
- }
- }
- if (file.exists()) {
- file.delete();
- }
}
/**
@@ -958,11 +860,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
if (success) {
try {
data.timestamp = Instant.now();
- File enabledFile = new File(data.backupDir, "enabled.txt");
- PrintWriter pw = new PrintWriter(enabledFile);
- pw.println(data.timestamp.toString());
- pw.close();
+ mRollbackStore.saveAvailableRollback(data);
synchronized (mLock) {
// Note: There is a small window of time between when
// the session has been committed by the package
@@ -981,12 +880,12 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub {
scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS);
} catch (IOException e) {
Log.e(TAG, "Unable to enable rollback", e);
- removeFile(data.backupDir);
+ mRollbackStore.deleteAvailableRollback(data);
}
} else {
// The install session was aborted, clean up the pending
// install.
- removeFile(data.backupDir);
+ mRollbackStore.deleteAvailableRollback(data);
}
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
new file mode 100644
index 000000000000..eb06bb24c24c
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2018 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.rollback;
+
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.time.Instant;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class for loading and saving rollback data to persistent storage.
+ */
+class RollbackStore {
+ private static final String TAG = "RollbackManager";
+
+ // Assuming the rollback data directory is /data/rollback, we use the
+ // following directory structure to store persisted data for available and
+ // recently executed rollbacks:
+ // /data/rollback/
+ // available/
+ // XXX/
+ // rollback.json
+ // com.package.A/
+ // base.apk
+ // com.package.B/
+ // base.apk
+ // YYY/
+ // rollback.json
+ // com.package.C/
+ // base.apk
+ // recently_executed.json
+ //
+ // * XXX, YYY are random strings from Files.createTempDirectory
+ // * rollback.json contains all relevant metadata for the rollback. This
+ // file is not written until the rollback is made available.
+ //
+ // TODO: Use AtomicFile for all the .json files?
+ private final File mRollbackDataDir;
+ private final File mAvailableRollbacksDir;
+ private final File mRecentlyExecutedRollbacksFile;
+
+ RollbackStore(File rollbackDataDir) {
+ mRollbackDataDir = rollbackDataDir;
+ mAvailableRollbacksDir = new File(mRollbackDataDir, "available");
+ mRecentlyExecutedRollbacksFile = new File(mRollbackDataDir, "recently_executed.json");
+ }
+
+ /**
+ * Reads the list of available rollbacks from persistent storage.
+ */
+ List<RollbackData> loadAvailableRollbacks() {
+ List<RollbackData> availableRollbacks = new ArrayList<>();
+ mAvailableRollbacksDir.mkdirs();
+ for (File rollbackDir : mAvailableRollbacksDir.listFiles()) {
+ if (rollbackDir.isDirectory()) {
+ try {
+ RollbackData data = loadRollbackData(rollbackDir);
+ availableRollbacks.add(data);
+ } catch (IOException e) {
+ // Note: Deleting the rollbackDir here will cause pending
+ // rollbacks to be deleted. This should only ever happen
+ // if reloadPersistedData is called while there are
+ // pending rollbacks. The reloadPersistedData method is
+ // currently only for testing, so that should be okay.
+ Log.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
+ removeFile(rollbackDir);
+ }
+ }
+ }
+ return availableRollbacks;
+ }
+
+ /**
+ * Reads the list of recently executed rollbacks from persistent storage.
+ */
+ List<RollbackInfo> loadRecentlyExecutedRollbacks() {
+ List<RollbackInfo> recentlyExecutedRollbacks = new ArrayList<>();
+ if (mRecentlyExecutedRollbacksFile.exists()) {
+ try {
+ // TODO: How to cope with changes to the format of this file from
+ // when RollbackStore is updated in the future?
+ String jsonString = IoUtils.readFileAsString(
+ mRecentlyExecutedRollbacksFile.getAbsolutePath());
+ JSONObject object = new JSONObject(jsonString);
+ JSONArray array = object.getJSONArray("recentlyExecuted");
+ for (int i = 0; i < array.length(); ++i) {
+ JSONObject element = array.getJSONObject(i);
+ String packageName = element.getString("packageName");
+ long higherVersionCode = element.getLong("higherVersionCode");
+ long lowerVersionCode = element.getLong("lowerVersionCode");
+ PackageRollbackInfo target = new PackageRollbackInfo(packageName,
+ new PackageRollbackInfo.PackageVersion(higherVersionCode),
+ new PackageRollbackInfo.PackageVersion(lowerVersionCode));
+ RollbackInfo rollback = new RollbackInfo(target);
+ recentlyExecutedRollbacks.add(rollback);
+ }
+ } catch (IOException | JSONException e) {
+ // TODO: What to do here? Surely we shouldn't just forget about
+ // everything after the point of exception?
+ Log.e(TAG, "Failed to read recently executed rollbacks", e);
+ }
+ }
+
+ return recentlyExecutedRollbacks;
+ }
+
+ /**
+ * Creates a new RollbackData instance with backupDir assigned.
+ */
+ RollbackData createAvailableRollback() throws IOException {
+ File backupDir = Files.createTempDirectory(mAvailableRollbacksDir.toPath(), null).toFile();
+ return new RollbackData(backupDir);
+ }
+
+ /**
+ * Returns the directory where the code for a package should be stored for
+ * given rollback <code>data</code> and <code>packageName</code>.
+ */
+ File packageCodePathForAvailableRollback(RollbackData data, String packageName) {
+ return new File(data.backupDir, packageName);
+ }
+
+ /**
+ * Writes the metadata for an available rollback to persistent storage.
+ */
+ void saveAvailableRollback(RollbackData data) throws IOException {
+ try {
+ JSONObject dataJson = new JSONObject();
+ JSONArray packagesJson = new JSONArray();
+ for (PackageRollbackInfo info : data.packages) {
+ JSONObject infoJson = new JSONObject();
+ infoJson.put("packageName", info.packageName);
+ infoJson.put("higherVersionCode", info.higherVersion.versionCode);
+ infoJson.put("lowerVersionCode", info.lowerVersion.versionCode);
+ packagesJson.put(infoJson);
+ }
+ dataJson.put("packages", packagesJson);
+ dataJson.put("timestamp", data.timestamp.toString());
+
+ PrintWriter pw = new PrintWriter(new File(data.backupDir, "rollback.json"));
+ pw.println(dataJson.toString());
+ pw.close();
+ } catch (JSONException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Removes all persistant storage associated with the given available
+ * rollback.
+ */
+ void deleteAvailableRollback(RollbackData data) {
+ // TODO(narayan): Make sure we delete the userdata snapshot along with the backup of the
+ // actual app.
+ removeFile(data.backupDir);
+ }
+
+ /**
+ * Writes the list of recently executed rollbacks to storage.
+ */
+ void saveRecentlyExecutedRollbacks(List<RollbackInfo> recentlyExecutedRollbacks) {
+ try {
+ JSONObject json = new JSONObject();
+ JSONArray array = new JSONArray();
+ json.put("recentlyExecuted", array);
+
+ for (int i = 0; i < recentlyExecutedRollbacks.size(); ++i) {
+ RollbackInfo rollback = recentlyExecutedRollbacks.get(i);
+ JSONObject element = new JSONObject();
+ element.put("packageName", rollback.targetPackage.packageName);
+ element.put("higherVersionCode", rollback.targetPackage.higherVersion.versionCode);
+ element.put("lowerVersionCode", rollback.targetPackage.lowerVersion.versionCode);
+ array.put(element);
+ }
+
+ PrintWriter pw = new PrintWriter(mRecentlyExecutedRollbacksFile);
+ pw.println(json.toString());
+ pw.close();
+ } catch (IOException | JSONException e) {
+ // TODO: What to do here?
+ Log.e(TAG, "Failed to save recently executed rollbacks", e);
+ }
+ }
+
+ /**
+ * Reads the metadata for a rollback from the given directory.
+ * @throws IOException in case of error reading the data.
+ */
+ private RollbackData loadRollbackData(File backupDir) throws IOException {
+ try {
+ RollbackData data = new RollbackData(backupDir);
+ File rollbackJsonFile = new File(backupDir, "rollback.json");
+ JSONObject dataJson = new JSONObject(
+ IoUtils.readFileAsString(rollbackJsonFile.getAbsolutePath()));
+ JSONArray packagesJson = dataJson.getJSONArray("packages");
+ for (int i = 0; i < packagesJson.length(); ++i) {
+ JSONObject infoJson = packagesJson.getJSONObject(i);
+ String packageName = infoJson.getString("packageName");
+ long higherVersionCode = infoJson.getLong("higherVersionCode");
+ long lowerVersionCode = infoJson.getLong("lowerVersionCode");
+ data.packages.add(new PackageRollbackInfo(packageName,
+ new PackageRollbackInfo.PackageVersion(higherVersionCode),
+ new PackageRollbackInfo.PackageVersion(lowerVersionCode)));
+ }
+
+ data.timestamp = Instant.parse(dataJson.getString("timestamp"));
+ return data;
+ } catch (JSONException | DateTimeParseException e) {
+ throw new IOException(e);
+ }
+ }
+
+ /**
+ * Deletes a file completely.
+ * If the file is a directory, its contents are deleted as well.
+ * Has no effect if the directory does not exist.
+ */
+ private void removeFile(File file) {
+ if (file.isDirectory()) {
+ for (File child : file.listFiles()) {
+ removeFile(child);
+ }
+ }
+ if (file.exists()) {
+ file.delete();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 4e71a054fa80..40664fef2825 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -96,10 +96,10 @@ import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BinderCallsStats.ExportedCallStat;
import com.android.internal.os.KernelCpuSpeedReader;
import com.android.internal.os.KernelCpuThreadReader;
-import com.android.internal.os.KernelUidCpuActiveTimeReader;
-import com.android.internal.os.KernelUidCpuClusterTimeReader;
-import com.android.internal.os.KernelUidCpuFreqTimeReader;
-import com.android.internal.os.KernelUidCpuTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
+import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
import com.android.internal.os.LooperStats;
@@ -231,14 +231,16 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
private final CompanionHandler mHandler;
- private KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
+ // Disables throttler on CPU time readers.
+ private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader =
+ new KernelCpuUidUserSysTimeReader(false);
private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
- private KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader =
- new KernelUidCpuFreqTimeReader();
- private KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader =
- new KernelUidCpuActiveTimeReader();
- private KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader =
- new KernelUidCpuClusterTimeReader();
+ private KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader =
+ new KernelCpuUidFreqTimeReader(false);
+ private KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader =
+ new KernelCpuUidActiveTimeReader(false);
+ private KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader =
+ new KernelCpuUidClusterTimeReader(false);
private StoragedUidIoStatsReader mStoragedUidIoStatsReader =
new StoragedUidIoStatsReader();
@Nullable
@@ -294,12 +296,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
numSpeedSteps);
firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
}
- // use default throttling in
- // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
- mKernelUidCpuFreqTimeReader.setThrottleInterval(0);
- long[] freqs = mKernelUidCpuFreqTimeReader.readFreqs(powerProfile);
- mKernelUidCpuClusterTimeReader.setThrottleInterval(0);
- mKernelUidCpuActiveTimeReader.setThrottleInterval(0);
// Enable push notifications of throttling from vendor thermal
// management subsystem via thermalservice.
@@ -914,7 +910,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private void pullKernelUidCpuTime(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
- mKernelUidCpuTimeReader.readAbsolute((uid, userTimeUs, systemTimeUs) -> {
+ mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> {
+ long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(uid);
e.writeLong(userTimeUs);
@@ -926,7 +923,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private void pullKernelUidCpuFreqTime(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
- mKernelUidCpuFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
+ mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
if (cpuFreqTimeMs[freqIndex] != 0) {
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
@@ -943,7 +940,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private void pullKernelUidCpuClusterTime(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
- mKernelUidCpuClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
+ mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
for (int i = 0; i < cpuClusterTimesMs.length; i++) {
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
wallClockNanos);
@@ -958,7 +955,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub {
private void pullKernelUidCpuActiveTime(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
- mKernelUidCpuActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
+ mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
e.writeInt(uid);
e.writeLong((long) cpuActiveTimesMs);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 7c1e6198080d..8d2bab43875d 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1219,13 +1219,13 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
- public void onNotificationExpansionChanged(String key, boolean userAction,
- boolean expanded) throws RemoteException {
+ public void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded,
+ int location) throws RemoteException {
enforceStatusBarService();
long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.onNotificationExpansionChanged(
- key, userAction, expanded);
+ key, userAction, expanded, location);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
new file mode 100644
index 000000000000..23c042a57ac8
--- /dev/null
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -0,0 +1,328 @@
+/*
+ * 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.testharness;
+
+import android.annotation.Nullable;
+import android.app.KeyguardManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.BatteryManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.PersistentDataBlockManagerInternal;
+import com.android.server.SystemService;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.Set;
+
+/**
+ * Manages the Test Harness Mode service for setting up test harness mode on the device.
+ *
+ * <p>Test Harness Mode is a feature that allows the user to clean their device, retain ADB keys,
+ * and provision the device for Instrumentation testing. This means that all parts of the device
+ * that would otherwise interfere with testing (auto-syncing accounts, package verification,
+ * automatic updates, etc.) are all disabled by default but may be re-enabled by the user.
+ */
+public class TestHarnessModeService extends SystemService {
+ private static final String TAG = TestHarnessModeService.class.getSimpleName();
+ private static final String TEST_HARNESS_MODE_PROPERTY = "persist.sys.test_harness";
+
+ private PersistentDataBlockManagerInternal mPersistentDataBlockManagerInternal;
+
+ public TestHarnessModeService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService("testharness", mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ switch (phase) {
+ case PHASE_SYSTEM_SERVICES_READY:
+ setUpTestHarnessMode();
+ break;
+ case PHASE_BOOT_COMPLETED:
+ disableAutoSync();
+ break;
+ }
+ super.onBootPhase(phase);
+ }
+
+ private void setUpTestHarnessMode() {
+ Slog.d(TAG, "Setting up test harness mode");
+ byte[] testHarnessModeData = getPersistentDataBlock().getTestHarnessModeData();
+ if (testHarnessModeData == null || testHarnessModeData.length == 0) {
+ // There's no data to apply, so leave it as-is.
+ return;
+ }
+ PersistentData persistentData = PersistentData.fromBytes(testHarnessModeData);
+
+ SystemProperties.set(TEST_HARNESS_MODE_PROPERTY, persistentData.mEnabled ? "1" : "0");
+ writeAdbKeysFile(persistentData);
+ // Clear out the data block so that we don't revert the ADB keys on every boot.
+ getPersistentDataBlock().clearTestHarnessModeData();
+
+ ContentResolver cr = getContext().getContentResolver();
+ if (Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) == 0) {
+ // Enable ADB
+ Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1);
+ } else {
+ // ADB is already enabled, we should restart the service so it picks up the new keys
+ android.os.SystemService.restart("adbd");
+ }
+
+ Settings.Global.putInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, 0);
+ Settings.Global.putInt(
+ cr,
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
+ BatteryManager.BATTERY_PLUGGED_ANY);
+ Settings.Global.putInt(cr, Settings.Global.OTA_DISABLE_AUTOMATIC_UPDATE, 1);
+ Settings.Global.putInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
+
+ setDeviceProvisioned();
+ }
+
+ private void disableAutoSync() {
+ UserInfo primaryUser = UserManager.get(getContext()).getPrimaryUser();
+ ContentResolver
+ .setMasterSyncAutomaticallyAsUser(false, primaryUser.getUserHandle().getIdentifier());
+ }
+
+ private void writeAdbKeysFile(PersistentData persistentData) {
+ Path adbKeys = Paths.get("/data/misc/adb/adb_keys");
+ try {
+ OutputStream fileOutputStream = Files.newOutputStream(adbKeys);
+ fileOutputStream.write(persistentData.mAdbKeys);
+ fileOutputStream.close();
+
+ Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(adbKeys);
+ permissions.add(PosixFilePermission.GROUP_READ);
+ Files.setPosixFilePermissions(adbKeys, permissions);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to set up adb keys", e);
+ // Note: if a device enters this block, it will remain UNAUTHORIZED in ADB, but all
+ // other settings will be set up.
+ }
+ }
+
+ // Setting the device as provisioned skips the setup wizard.
+ private void setDeviceProvisioned() {
+ ContentResolver cr = getContext().getContentResolver();
+ Settings.Global.putInt(cr, Settings.Global.DEVICE_PROVISIONED, 1);
+ Settings.Secure.putIntForUser(
+ cr,
+ Settings.Secure.USER_SETUP_COMPLETE,
+ 1,
+ UserHandle.USER_CURRENT);
+ }
+
+ @Nullable
+ private PersistentDataBlockManagerInternal getPersistentDataBlock() {
+ if (mPersistentDataBlockManagerInternal == null) {
+ Slog.d(TAG, "Getting PersistentDataBlockManagerInternal from LocalServices");
+ mPersistentDataBlockManagerInternal =
+ LocalServices.getService(PersistentDataBlockManagerInternal.class);
+ }
+ return mPersistentDataBlockManagerInternal;
+ }
+
+ private final IBinder mService = new Binder() {
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ (new TestHarnessModeShellCommand())
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+ };
+
+ private class TestHarnessModeShellCommand extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ switch (cmd) {
+ case "enable":
+ case "restore":
+ checkPermissions();
+ final long originalId = Binder.clearCallingIdentity();
+ try {
+ if (isDeviceSecure()) {
+ getErrPrintWriter().println(
+ "Test Harness Mode cannot be enabled if there is a lock "
+ + "screen");
+ return 2;
+ }
+ return handleEnable();
+ } finally {
+ Binder.restoreCallingIdentity(originalId);
+ }
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private void checkPermissions() {
+ getContext().enforceCallingPermission(
+ android.Manifest.permission.ENABLE_TEST_HARNESS_MODE,
+ "You must hold android.permission.ENABLE_TEST_HARNESS_MODE "
+ + "to enable Test Harness Mode");
+ }
+
+ private boolean isDeviceSecure() {
+ UserInfo primaryUser = UserManager.get(getContext()).getPrimaryUser();
+ KeyguardManager keyguardManager = getContext().getSystemService(KeyguardManager.class);
+ return keyguardManager.isDeviceSecure(primaryUser.id);
+ }
+
+ private int handleEnable() {
+ Path adbKeys = Paths.get("/data/misc/adb/adb_keys");
+ if (!Files.exists(adbKeys)) {
+ // This should only be accessible on eng builds that haven't yet set up ADB keys
+ getErrPrintWriter()
+ .println("No ADB keys stored; not enabling test harness mode");
+ return 1;
+ }
+
+ try (InputStream inputStream = Files.newInputStream(adbKeys)) {
+ long size = Files.size(adbKeys);
+ byte[] adbKeysBytes = new byte[(int) size];
+ int numBytes = inputStream.read(adbKeysBytes);
+ if (numBytes != size) {
+ getErrPrintWriter().println("Failed to read all bytes of adb_keys");
+ return 1;
+ }
+ PersistentData persistentData = new PersistentData(true, adbKeysBytes);
+ getPersistentDataBlock().setTestHarnessModeData(persistentData.toBytes());
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to store ADB keys.", e);
+ getErrPrintWriter().println("Failed to enable Test Harness Mode");
+ return 1;
+ }
+
+ Intent i = new Intent(Intent.ACTION_FACTORY_RESET);
+ i.setPackage("android");
+ i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ i.putExtra(Intent.EXTRA_REASON, TAG);
+ i.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, true);
+ getContext().sendBroadcastAsUser(i, UserHandle.SYSTEM);
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("About:");
+ pw.println(" Test Harness Mode is a mode that the device can be placed in to prepare");
+ pw.println(" the device for running UI tests. The device is placed into this mode by");
+ pw.println(" first wiping all data from the device, preserving ADB keys.");
+ pw.println();
+ pw.println(" By default, the following settings are configured:");
+ pw.println(" * Package Verifier is disabled");
+ pw.println(" * Stay Awake While Charging is enabled");
+ pw.println(" * OTA Updates are disabled");
+ pw.println(" * Auto-Sync for accounts is disabled");
+ pw.println();
+ pw.println(" Other apps may configure themselves differently in Test Harness Mode by");
+ pw.println(" checking ActivityManager.isRunningInUserTestHarness()");
+ pw.println();
+ pw.println("Test Harness Mode commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" enable|restore");
+ pw.println(" Erase all data from this device and enable Test Harness Mode,");
+ pw.println(" preserving the stored ADB keys currently on the device and toggling");
+ pw.println(" settings in a way that are conducive to Instrumentation testing.");
+ }
+ }
+
+ /**
+ * The object that will serialize/deserialize the Test Harness Mode data to and from the
+ * persistent data block.
+ */
+ public static class PersistentData {
+ static final byte VERSION_1 = 1;
+
+ final int mVersion;
+ final boolean mEnabled;
+ final byte[] mAdbKeys;
+
+ PersistentData(boolean enabled, byte[] adbKeys) {
+ this(VERSION_1, enabled, adbKeys);
+ }
+
+ PersistentData(int version, boolean enabled, byte[] adbKeys) {
+ this.mVersion = version;
+ this.mEnabled = enabled;
+ this.mAdbKeys = adbKeys;
+ }
+
+ static PersistentData fromBytes(byte[] bytes) {
+ try {
+ DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes));
+ int version = is.readInt();
+ boolean enabled = is.readBoolean();
+ int adbKeysLength = is.readInt();
+ byte[] adbKeys = new byte[adbKeysLength];
+ is.readFully(adbKeys);
+ return new PersistentData(version, enabled, adbKeys);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ byte[] toBytes() {
+ try {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ DataOutputStream dos = new DataOutputStream(os);
+ dos.writeInt(VERSION_1);
+ dos.writeBoolean(mEnabled);
+ dos.writeInt(mAdbKeys.length);
+ dos.write(mAdbKeys);
+ dos.close();
+ return os.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index ced593565983..423ec4c869c1 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -22,10 +22,8 @@ import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.app.admin.DevicePolicyManager;
-import android.hardware.biometrics.BiometricSourceType;
import android.app.trust.ITrustListener;
import android.app.trust.ITrustManager;
-import android.app.UserSwitchObserver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -41,6 +39,7 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricSourceType;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -72,13 +71,15 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
+
/**
* Manages trust agents and trust listeners.
@@ -119,7 +120,7 @@ public class TrustManagerService extends SystemService {
private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser";
- private static final long TRUST_TIMEOUT_IN_MILLIS = 20 * 1000; //4 * 60 * 60 * 1000;
+ private static final long TRUST_TIMEOUT_IN_MILLIS = 4 * 60 * 60 * 1000;
private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>();
private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>();
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b3eafa4d25ce..45689ce73c9f 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -65,7 +65,6 @@ import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
-import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.utils.ManagedApplicationService;
import com.android.server.utils.ManagedApplicationService.BinderChecker;
import com.android.server.utils.ManagedApplicationService.LogEvent;
@@ -623,14 +622,6 @@ public class VrManagerService extends SystemService
}
@Override
- public void setVrInputMethod(ComponentName componentName) {
- enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS);
- InputMethodManagerInternal imm =
- LocalServices.getService(InputMethodManagerInternal.class);
- imm.startVrInputMethodNoCheck(componentName);
- }
-
- @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index caebf1528270..545b69b2fd54 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -49,6 +49,7 @@ import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
+import android.view.Display;
import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
@@ -83,23 +84,36 @@ final class AccessibilityController {
mService = service;
}
- private DisplayMagnifier mDisplayMagnifier;
+ private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
- public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
+ public boolean setMagnificationCallbacksLocked(int displayId,
+ MagnificationCallbacks callbacks) {
+ boolean result = false;
if (callbacks != null) {
- if (mDisplayMagnifier != null) {
+ if (mDisplayMagnifiers.get(displayId) != null) {
throw new IllegalStateException("Magnification callbacks already set!");
}
- mDisplayMagnifier = new DisplayMagnifier(mService, callbacks);
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ if (dc != null) {
+ final Display display = dc.getDisplay();
+ if (display != null && display.getType() != Display.TYPE_OVERLAY) {
+ mDisplayMagnifiers.put(displayId, new DisplayMagnifier(
+ mService, dc, display, callbacks));
+ result = true;
+ }
+ }
} else {
- if (mDisplayMagnifier == null) {
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier == null) {
throw new IllegalStateException("Magnification callbacks already cleared!");
}
- mDisplayMagnifier.destroyLocked();
- mDisplayMagnifier = null;
+ displayMagnifier.destroyLocked();
+ mDisplayMagnifiers.remove(displayId);
+ result = true;
}
+ return result;
}
public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
@@ -129,58 +143,72 @@ final class AccessibilityController {
}
}
- public void setMagnificationSpecLocked(MagnificationSpec spec) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.setMagnificationSpecLocked(spec);
+ public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) {
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.setMagnificationSpecLocked(spec);
}
- if (mWindowsForAccessibilityObserver != null) {
+ // TODO: support multi-display for windows observer
+ if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
}
}
- public void getMagnificationRegionLocked(Region outMagnificationRegion) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
+ public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) {
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
}
}
- public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
+ public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) {
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
}
// Not relevant for the window observer.
}
- public void onWindowLayersChangedLocked() {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.onWindowLayersChangedLocked();
+ public void onWindowLayersChangedLocked(int displayId) {
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.onWindowLayersChangedLocked();
}
- if (mWindowsForAccessibilityObserver != null) {
+ // TODO: support multi-display for windows observer
+ if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
}
}
public void onRotationChangedLocked(DisplayContent displayContent) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.onRotationChangedLocked(displayContent);
+ final int displayId = displayContent.getDisplayId();
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.onRotationChangedLocked(displayContent);
}
- if (mWindowsForAccessibilityObserver != null) {
+ // TODO: support multi-display for windows observer
+ if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
}
}
public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
+ final int displayId = windowState.getDisplayId();
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.onAppWindowTransitionLocked(windowState, transition);
}
// Not relevant for the window observer.
}
public void onWindowTransitionLocked(WindowState windowState, int transition) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
+ final int displayId = windowState.getDisplayId();
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.onWindowTransitionLocked(windowState, transition);
}
- if (mWindowsForAccessibilityObserver != null) {
+ // TODO: support multi-display for windows observer
+ if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) {
mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
}
}
@@ -197,7 +225,6 @@ final class AccessibilityController {
}
}
-
public void onSomeWindowResizedOrMovedLocked() {
// Not relevant for the display magnifier.
@@ -207,29 +234,34 @@ final class AccessibilityController {
}
/** NOTE: This has to be called within a surface transaction. */
- public void drawMagnifiedRegionBorderIfNeededLocked() {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+ public void drawMagnifiedRegionBorderIfNeededLocked(int displayId) {
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
}
// Not relevant for the window observer.
}
public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
- if (mDisplayMagnifier != null) {
- return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
+ final int displayId = windowState.getDisplayId();
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ return displayMagnifier.getMagnificationSpecForWindowLocked(windowState);
}
return null;
}
public boolean hasCallbacksLocked() {
- return (mDisplayMagnifier != null
+ // TODO: support multi-display for windows observer
+ return (mDisplayMagnifiers.size() > 0
|| mWindowsForAccessibilityObserver != null);
}
- public void setForceShowMagnifiableBoundsLocked(boolean show) {
- if (mDisplayMagnifier != null) {
- mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
- mDisplayMagnifier.showMagnificationBoundsIfNeeded();
+ public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) {
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier != null) {
+ displayMagnifier.setForceShowMagnifiableBoundsLocked(show);
+ displayMagnifier.showMagnificationBoundsIfNeeded();
}
}
@@ -263,6 +295,8 @@ final class AccessibilityController {
private final WindowManagerService mService;
private final MagnifiedViewport mMagnifedViewport;
private final Handler mHandler;
+ private final DisplayContent mDisplayContent;
+ private final Display mDisplay;
private final MagnificationCallbacks mCallbacks;
@@ -271,10 +305,14 @@ final class AccessibilityController {
private boolean mForceShowMagnifiableBounds = false;
public DisplayMagnifier(WindowManagerService windowManagerService,
+ DisplayContent displayContent,
+ Display display,
MagnificationCallbacks callbacks) {
mContext = windowManagerService.mContext;
mService = windowManagerService;
mCallbacks = callbacks;
+ mDisplayContent = displayContent;
+ mDisplay = display;
mHandler = new MyHandler(mService.mH.getLooper());
mMagnifedViewport = new MagnifiedViewport();
mLongAnimationDuration = mContext.getResources().getInteger(
@@ -285,7 +323,7 @@ final class AccessibilityController {
mMagnifedViewport.updateMagnificationSpecLocked(spec);
mMagnifedViewport.recomputeBoundsLocked();
- mService.applyMagnificationSpec(spec);
+ mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec);
mService.scheduleAnimationLocked();
}
@@ -482,7 +520,7 @@ final class AccessibilityController {
if (mContext.getResources().getConfiguration().isScreenRound()) {
mCircularPath = new Path();
- mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+ mDisplay.getRealSize(mTempPoint);
final int centerXY = mTempPoint.x / 2;
mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
} else {
@@ -512,7 +550,7 @@ final class AccessibilityController {
}
public void recomputeBoundsLocked() {
- mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+ mDisplay.getRealSize(mTempPoint);
final int screenWidth = mTempPoint.x;
final int screenHeight = mTempPoint.y;
@@ -671,9 +709,8 @@ final class AccessibilityController {
}
private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
- final DisplayContent dc = mService.getDefaultDisplayContentLocked();
mTempLayer = 0;
- dc.forAllWindows((w) -> {
+ mDisplayContent.forAllWindows((w) -> {
if (w.isOnScreen() && w.isVisibleLw()
&& (w.mAttrs.alpha != 0)
&& !w.mWinAnimator.mEnterAnimationPending) {
@@ -703,8 +740,9 @@ final class AccessibilityController {
public ViewportWindow(Context context) {
SurfaceControl surfaceControl = null;
try {
- mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
- surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
+ mDisplay.getRealSize(mTempPoint);
+ surfaceControl = mDisplayContent
+ .makeOverlay()
.setName(SURFACE_TITLE)
.setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo
.setFormat(PixelFormat.TRANSLUCENT)
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index e817dd47e756..65d66f44b5dd 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -558,26 +558,22 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
}
/**
- * Pause all activities in either all of the stacks or just the back stacks. This is done before
- * resuming a new activity and to make sure that previously active activities are
- * paused in stacks that are no longer visible or in pinned windowing mode. This does not
- * pause activities in visible stacks, so if an activity is launched within the same stack/task,
- * then we should explicitly pause that stack's top activity.
+ * Pause all activities in either all of the stacks or just the back stacks.
* @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
* @param resuming The resuming activity.
* @param dontWait The resuming activity isn't going to wait for all activities to be paused
* before resuming.
- * @return {@code true} if any activity was paused as a result of this call.
+ * @return true if any activity was paused as a result of this call.
*/
boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
boolean someActivityPaused = false;
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
- final ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity != null
- && (!stack.shouldBeVisible(resuming) || !stack.isFocusable())) {
+ // TODO(b/111541062): Check if resumed activity on this display instead
+ if (!mRootActivityContainer.isTopDisplayFocusedStack(stack)
+ && stack.getResumedActivity() != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
- " mResumedActivity=" + resumedActivity);
+ " mResumedActivity=" + stack.getResumedActivity());
someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
dontWait);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6213fa02cb9f..b8634d88319a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1946,84 +1946,30 @@ final class ActivityRecord extends ConfigurationContainer {
try {
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
WindowVisibilityItem.obtain(true /* showWindow */));
- makeActiveIfNeeded(null /* activeActivity*/);
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
- }
- }
-
- /**
- * Make activity resumed or paused if needed.
- * @param activeActivity an activity that is resumed or just completed pause action.
- * We won't change the state of this activity.
- */
- boolean makeActiveIfNeeded(ActivityRecord activeActivity) {
- if (shouldResumeActivity(activeActivity)) {
- if (DEBUG_VISIBILITY) {
- Slog.v("TAG_VISIBILITY", "Resume visible activity, " + this);
- }
- return getActivityStack().resumeTopActivityUncheckedLocked(activeActivity /* prev */,
- null /* options */);
- } else if (shouldPauseActivity(activeActivity)) {
- if (DEBUG_VISIBILITY) {
- Slog.v("TAG_VISIBILITY", "Pause visible activity, " + this);
- }
- // An activity must be in the {@link PAUSING} state for the system to validate
- // the move to {@link PAUSED}.
- setState(PAUSING, "makeVisibleIfNeeded");
- try {
+ if (shouldPauseWhenBecomingVisible()) {
+ // An activity must be in the {@link PAUSING} state for the system to validate
+ // the move to {@link PAUSED}.
+ setState(PAUSING, "makeVisibleIfNeeded");
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
PauseActivityItem.obtain(finishing, false /* userLeaving */,
configChangeFlags, false /* dontReport */));
- } catch (Exception e) {
- Slog.w(TAG, "Exception thrown sending pause: " + intent.getComponent(), e);
}
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception thrown sending visibility update: " + intent.getComponent(), e);
}
- return false;
}
- /**
- * Check if activity should be moved to PAUSED state. The activity:
- * - should be eligible to be made active (see {@link #shouldMakeActive(ActivityRecord)})
- * - should be non-focusable
- * - should not be currently pausing or paused
- * @param activeActivity the activity that is active or just completed pause action. We won't
- * resume if this activity is active.
- */
- private boolean shouldPauseActivity(ActivityRecord activeActivity) {
- return shouldMakeActive(activeActivity) && !isFocusable() && !isState(PAUSING, PAUSED);
- }
-
- /**
- * Check if activity should be moved to RESUMED state. The activity:
- * - should be eligible to be made active (see {@link #shouldMakeActive(ActivityRecord)})
- * - should be focusable
- * @param activeActivity the activity that is active or just completed pause action. We won't
- * resume if this activity is active.
- */
- private boolean shouldResumeActivity(ActivityRecord activeActivity) {
- return shouldMakeActive(activeActivity) && isFocusable() && !isState(RESUMED);
- }
-
- /**
- * Check if activity is eligible to be made active (resumed of paused). The activity:
- * - should be paused, stopped or stopping
- * - should not be the currently active one
- * - should be either the topmost in task, or right below the top activity that is finishing
- * If all of these conditions are not met at the same time, the activity cannot be made active.
- */
- private boolean shouldMakeActive(ActivityRecord activeActivity) {
- // If the activity is stopped, stopping, cycle to an active state. We avoid doing
+ /** Check if activity should be moved to PAUSED state when it becomes visible. */
+ private boolean shouldPauseWhenBecomingVisible() {
+ // If the activity is stopped or stopping, cycle to the paused state. We avoid doing
// this when there is an activity waiting to become translucent as the extra binder
// calls will lead to noticeable jank. A later call to
- // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to a proper
- // active state.
- if (!isState(RESUMED, PAUSED, STOPPED, STOPPING)
- || getActivityStack().mTranslucentActivityWaiting != null) {
- return false;
- }
-
- if (this == activeActivity) {
+ // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to the proper
+ // paused state. We also avoid doing this for the activity the stack supervisor
+ // considers the resumed activity, as normal means will bring the activity from STOPPED
+ // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
+ if (!isState(STOPPED, STOPPING) || getActivityStack().mTranslucentActivityWaiting != null
+ || isResumedActivityOnDisplay()) {
return false;
}
@@ -2033,14 +1979,14 @@ final class ActivityRecord extends ConfigurationContainer {
throw new IllegalStateException("Activity not found in its task");
}
if (positionInTask == task.mActivities.size() - 1) {
- // It's the topmost activity in the task - should become resumed now
+ // It's the topmost activity in the task - should become paused now
return true;
}
// Check if activity above is finishing now and this one becomes the topmost in task.
final ActivityRecord activityAbove = task.mActivities.get(positionInTask + 1);
if (activityAbove.finishing && results == null) {
- // We will only allow making active if activity above wasn't launched for result.
- // Otherwise it will cause this activity to resume before getting result.
+ // We will only allow pausing if activity above wasn't launched for result. Otherwise it
+ // will cause this activity to resume before getting result.
return true;
}
return false;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 3aef8e1f84bf..891c3da90b93 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -357,11 +357,6 @@ class ActivityStack extends ConfigurationContainer {
*/
boolean mForceHidden = false;
- /**
- * Used to keep resumeTopActivityUncheckedLocked() from being entered recursively
- */
- boolean mInResumeTopActivity = false;
-
private boolean mUpdateBoundsDeferred;
private boolean mUpdateBoundsDeferredCalled;
private boolean mUpdateDisplayedBoundsDeferredCalled;
@@ -1737,7 +1732,6 @@ class ActivityStack extends ConfigurationContainer {
"Activity paused: token=" + token + ", timeout=" + timeout);
final ActivityRecord r = isInStackLocked(token);
-
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
if (mPausingActivity == r) {
@@ -2094,7 +2088,8 @@ class ActivityStack extends ConfigurationContainer {
boolean aboveTop = top != null;
final boolean stackShouldBeVisible = shouldBeVisible(starting);
boolean behindFullscreenActivity = !stackShouldBeVisible;
- boolean resumeNextActivity = isFocusable() && isInStackLocked(starting) == null;
+ boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this)
+ && (isInStackLocked(starting) == null);
final boolean isTopNotPinnedStack =
isAttached() && getDisplay().isTopNotPinnedStack(this);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -2155,10 +2150,6 @@ class ActivityStack extends ConfigurationContainer {
if (r.handleAlreadyVisible()) {
resumeNextActivity = false;
}
-
- if (notifyClients) {
- r.makeActiveIfNeeded(starting);
- }
} else {
r.makeVisibleIfNeeded(starting, notifyClients);
}
@@ -2336,7 +2327,7 @@ class ActivityStack extends ConfigurationContainer {
r.setVisible(true);
}
if (r != starting) {
- mStackSupervisor.startSpecificActivityLocked(r, andResume, true /* checkConfig */);
+ mStackSupervisor.startSpecificActivityLocked(r, andResume, false);
return true;
}
}
@@ -2514,7 +2505,7 @@ class ActivityStack extends ConfigurationContainer {
*/
@GuardedBy("mService")
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
- if (mInResumeTopActivity) {
+ if (mStackSupervisor.inResumeTopActivity) {
// Don't even start recursing.
return false;
}
@@ -2522,7 +2513,7 @@ class ActivityStack extends ConfigurationContainer {
boolean result = false;
try {
// Protect against recursion.
- mInResumeTopActivity = true;
+ mStackSupervisor.inResumeTopActivity = true;
result = resumeTopActivityInnerLocked(prev, options);
// When resuming the top activity, it may be necessary to pause the top activity (for
@@ -2537,7 +2528,7 @@ class ActivityStack extends ConfigurationContainer {
checkReadyForSleep();
}
} finally {
- mInResumeTopActivity = false;
+ mStackSupervisor.inResumeTopActivity = false;
}
return result;
@@ -2570,7 +2561,7 @@ class ActivityStack extends ConfigurationContainer {
// Find the next top-most activity to resume in this stack that is not finishing and is
// focusable. If it is not focusable, we will fall into the case below to resume the
// top activity in the next focusable task.
- ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
+ final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
final boolean hasRunningActivity = next != null;
@@ -2658,12 +2649,6 @@ class ActivityStack extends ConfigurationContainer {
if (!mRootActivityContainer.allPausedActivitiesComplete()) {
if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
"resumeTopActivityLocked: Skip resume: some activity pausing.");
-
- // Adding previous activity to the waiting visible list, or it would be stopped
- // before top activity being visible.
- if (prev != null && !next.nowVisible) {
- mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev);
- }
return false;
}
@@ -2873,9 +2858,7 @@ class ActivityStack extends ConfigurationContainer {
// the screen based on the new activity order.
boolean notUpdated = true;
- // Activity should also be visible if set mLaunchTaskBehind to true (see
- // ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
- if (shouldBeVisible(next)) {
+ if (isFocusedStackOnDisplay()) {
// We have special rotation behavior when here is some active activity that
// requests specific orientation or Keyguard is locked. Make sure all activity
// visibilities are set correctly as well as the transition is updated if needed
@@ -4104,12 +4087,6 @@ class ActivityStack extends ConfigurationContainer {
mStackSupervisor.mFinishingActivities.add(r);
r.resumeKeyDispatchingLocked();
mRootActivityContainer.resumeFocusedStacksTopActivities();
- // If activity was not paused at this point - explicitly pause it to start finishing
- // process. Finishing will be completed once it reports pause back.
- if (r.isState(RESUMED) && mPausingActivity != null) {
- startPausingLocked(false /* userLeaving */, false /* uiSleeping */, next /* resuming */,
- false /* dontWait */);
- }
return r;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a83ef34f1cac..3a288ca5560d 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -327,6 +327,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
*/
PowerManager.WakeLock mGoingToSleep;
+ /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
+ boolean inResumeTopActivity;
+
/**
* Temporary rect used during docked stack resize calculation so we don't need to create a new
* object each time.
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 43c12064a3c1..280709461c98 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -179,10 +179,7 @@ public class ActivityStartController {
.setActivityOptions(options.toBundle())
.execute();
mLastHomeActivityStartRecord = tmpOutRecord[0];
- final ActivityDisplay display =
- mService.mRootActivityContainer.getActivityDisplay(displayId);
- final ActivityStack homeStack = display != null ? display.getHomeStack() : null;
- if (homeStack != null && homeStack.mInResumeTopActivity) {
+ if (mSupervisor.inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
// resumed (to avoid recursive resume) and will stay that way until something pokes it
// again. We need to schedule another resume.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4e2dffc2ba78..d36e545aa74f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1629,7 +1629,7 @@ class ActivityStarter {
// Also, we don't want to resume activities in a task that currently has an overlay
// as the starting activity just needs to be in the visible paused state until the
// over is removed.
- mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS);
+ mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
// Go ahead and tell window manager to execute app transition for this activity
// since the app transition will not be triggered through the resume channel.
mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 780eda49faf4..750c5ca5922e 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -25,7 +25,6 @@ import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -591,9 +590,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
delayed = runningAppAnimation = true;
}
final WindowState window = findMainWindow();
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (window != null && accessibilityController != null
- && getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
+ if (window != null && accessibilityController != null) {
accessibilityController.onAppWindowTransitionLocked(window, transit);
}
changed = true;
@@ -1975,7 +1972,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
} else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else if (taskSwitch && allowTaskSnapshot) {
- if (mWmService.mLowRamTaskSnapshots) {
+ if (mWmService.mLowRamTaskSnapshotsAndRecents) {
// For low RAM devices, we use the splash screen starting window instead of the
// task snapshot starting window.
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
@@ -2407,7 +2404,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
@Override
protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
if (!mSurfaceAnimator.hasLeash()) {
- t.reparent(mSurfaceControl, newParent.getHandle());
+ t.reparent(mSurfaceControl, newParent);
}
}
@@ -2453,7 +2450,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
// Reparent leash to animation bounds layer.
- t.reparent(leash, mAnimationBoundsLayer.getHandle());
+ t.reparent(leash, mAnimationBoundsLayer);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6527ca0e751d..8fefd352e027 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -30,11 +30,14 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.View.GONE;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_TOP;
@@ -164,6 +167,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -1051,6 +1055,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*/
void setInsetProvider(@InternalInsetType int type, WindowState win,
@Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+ if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL) {
+ if (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR) {
+ return;
+ }
+ }
mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider);
}
@@ -1457,11 +1466,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- // TODO (multi-display): Magnification is supported only for the default display.
// Announce rotation only if we will not animate as we already have the
// windows in final state. Otherwise, we make this call at the rotation end.
- if (screenRotationAnimation == null && mWmService.mAccessibilityController != null
- && isDefaultDisplay) {
+ if (screenRotationAnimation == null && mWmService.mAccessibilityController != null) {
mWmService.mAccessibilityController.onRotationChangedLocked(this);
}
}
@@ -4581,7 +4588,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Reparents the given surface to mOverlayLayer.
*/
void reparentToOverlay(Transaction transaction, SurfaceControl surface) {
- transaction.reparent(surface, mOverlayLayer.getHandle());
+ transaction.reparent(surface, mOverlayLayer);
}
void applyMagnificationSpec(MagnificationSpec spec) {
@@ -4824,11 +4831,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
* {@link #mOverlayLayer} to the specified surfaceControl.
*
- * @param surfaceControlHandle The handle for the new SurfaceControl, where the DisplayContent's
+ * @param surfaceControlHandle The new SurfaceControl, where the DisplayContent's
* surfaces will be re-parented to.
*/
- void reparentDisplayContent(IBinder surfaceControlHandle) {
- mPendingTransaction.reparent(mWindowingLayer, surfaceControlHandle)
- .reparent(mOverlayLayer, surfaceControlHandle);
+ void reparentDisplayContent(SurfaceControl sc) {
+ mPendingTransaction.reparent(mWindowingLayer, sc)
+ .reparent(mOverlayLayer, sc);
}
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 786a3064ab4e..3f77e1c6886b 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -120,6 +120,8 @@ class DragState {
// A surface used to catch input events for the drag-and-drop operation.
SurfaceControl mInputSurface;
+ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+
private final Rect mTmpClipRect = new Rect();
/**
@@ -240,7 +242,7 @@ class DragState {
// Clear the internal variables.
if (mSurfaceControl != null) {
- mSurfaceControl.destroy();
+ mTransaction.reparent(mSurfaceControl, null).apply();
mSurfaceControl = null;
}
if (mAnimator != null && !mAnimationCompleted) {
@@ -500,18 +502,13 @@ class DragState {
mCurrentY = y;
// Move the surface to the given touch
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
- TAG_WM, ">>> OPEN TRANSACTION notifyMoveLocked");
- mService.openSurfaceTransaction();
- try {
- mSurfaceControl.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
- if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " DRAG "
- + mSurfaceControl + ": pos=(" +
- (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
- } finally {
- mService.closeSurfaceTransaction("notifyMoveLw");
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(
- TAG_WM, "<<< CLOSE TRANSACTION notifyMoveLocked");
+ if (SHOW_LIGHT_TRANSACTIONS) {
+ Slog.i(TAG_WM, ">>> OPEN TRANSACTION notifyMoveLocked");
+ }
+ mTransaction.setPosition(mSurfaceControl, x - mThumbOffsetX, y - mThumbOffsetY).apply();
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG_WM, " DRAG " + mSurfaceControl + ": pos=(" + (int) (x - mThumbOffsetX) + ","
+ + (int) (y - mThumbOffsetY) + ")");
}
notifyLocationLocked(x, y);
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index d6f161645327..e49e4c0711bd 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
@@ -159,7 +161,7 @@ class InsetsSourceProvider {
}
boolean isClientVisible() {
- return !ViewRootImpl.USE_NEW_INSETS || mClientVisible;
+ return ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible;
}
private class ControlAdapter implements AnimationAdapter {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index bc01f7c2595d..32dbe96d39f7 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -19,6 +19,9 @@ package com.android.server.wm;
import static android.view.InsetsState.TYPE_IME;
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.ViewRootImpl.sNewInsetsMode;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -160,7 +163,7 @@ class InsetsStateController {
}
private void onControlChanged(int type, @Nullable WindowState win) {
- if (!ViewRootImpl.USE_NEW_INSETS) {
+ if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
return;
}
final WindowState previous = mTypeWinControlMap.get(type);
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index c4a853dc3483..9b7214120aed 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -1108,41 +1108,28 @@ class RootActivityContainer extends ConfigurationContainer
return false;
}
- boolean result = false;
if (targetStack != null && (targetStack.isTopStackOnDisplay()
|| getTopDisplayFocusedStack() == targetStack)) {
- result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
+ return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
+ // Resume all top activities in focused stacks on all displays.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- boolean resumedOnDisplay = false;
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord topRunningActivity = stack.topRunningActivityLocked();
- if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
- continue;
- }
- if (topRunningActivity.isState(RESUMED)) {
- // Kick off any lingering app transitions form the MoveTaskToFront operation.
- stack.executeAppTransition(targetOptions);
- } else {
- resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
- }
+ final ActivityStack focusedStack = display.getFocusedStack();
+ if (focusedStack == null) {
+ continue;
}
- if (!resumedOnDisplay) {
- // In cases when there are no valid activities (e.g. device just booted or launcher
- // crashed) it's possible that nothing was resumed on a display. Requesting resume
- // of top activity in focused stack explicitly will make sure that at least home
- // activity is started and resumed, and no recursion occurs.
- final ActivityStack focusedStack = display.getFocusedStack();
- if (focusedStack != null) {
- focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
- }
+ final ActivityRecord r = focusedStack.topRunningActivityLocked();
+ if (r == null || !r.isState(RESUMED)) {
+ focusedStack.resumeTopActivityUncheckedLocked(null, null);
+ } else if (r.isState(RESUMED)) {
+ // Kick off any lingering app transitions form the MoveTaskToFront operation.
+ focusedStack.executeAppTransition(targetOptions);
}
}
- return result;
+ return false;
}
void applySleepTokens(boolean applyToStacks) {
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 9d9b48a5b36a..1a8a9110c649 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -199,7 +199,7 @@ class SurfaceAnimator {
* @see #setLayer
*/
void reparent(Transaction t, SurfaceControl newParent) {
- t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent.getHandle());
+ t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent);
}
/**
@@ -228,8 +228,8 @@ class SurfaceAnimator {
// Cancel source animation, but don't let animation runner cancel the animation.
from.cancelAnimation(t, false /* restarting */, false /* forwardCancel */);
- t.reparent(surface, mLeash.getHandle());
- t.reparent(mLeash, parent.getHandle());
+ t.reparent(surface, mLeash);
+ t.reparent(mLeash, parent);
mAnimatable.onAnimationLeashCreated(t, mLeash);
mService.mAnimationTransferMap.put(mAnimation, this);
}
@@ -275,7 +275,7 @@ class SurfaceAnimator {
final boolean destroy = mLeash != null && surface != null && parent != null;
if (destroy) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent");
- t.reparent(surface, parent.getHandle());
+ t.reparent(surface, parent);
scheduleAnim = true;
}
mService.mAnimationTransferMap.remove(mAnimation);
@@ -308,7 +308,7 @@ class SurfaceAnimator {
if (!hidden) {
t.show(leash);
}
- t.reparent(surface, leash.getHandle());
+ t.reparent(surface, leash);
return leash;
}
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 0529ed128130..69dcaf473b12 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -698,14 +698,6 @@ class TaskRecord extends ConfigurationContainer {
return false;
}
- final boolean toTopOfStack = position == MAX_VALUE;
- if (toTopOfStack && toStack.getResumedActivity() != null
- && toStack.topRunningActivityLocked() != null) {
- // Pause the resumed activity on the target stack while re-parenting task on top of it.
- toStack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
- null /* resuming */, false /* pauseImmediately */);
- }
-
final int toStackWindowingMode = toStack.getWindowingMode();
final ActivityRecord topActivity = getTopActivity();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 01a5622c2b60..beb3d82c05fb 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static com.android.server.wm.TaskSnapshotPersister.DISABLE_FULL_SIZED_BITMAPS;
-import static com.android.server.wm.TaskSnapshotPersister.REDUCED_SCALE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -90,9 +89,8 @@ class TaskSnapshotController {
private final WindowManagerService mService;
private final TaskSnapshotCache mCache;
- private final TaskSnapshotPersister mPersister = new TaskSnapshotPersister(
- Environment::getDataSystemCeDirectory);
- private final TaskSnapshotLoader mLoader = new TaskSnapshotLoader(mPersister);
+ private final TaskSnapshotPersister mPersister;
+ private final TaskSnapshotLoader mLoader;
private final ArraySet<Task> mSkipClosingAppSnapshotTasks = new ArraySet<>();
private final ArraySet<Task> mTmpTasks = new ArraySet<>();
private final Handler mHandler = new Handler();
@@ -116,6 +114,8 @@ class TaskSnapshotController {
TaskSnapshotController(WindowManagerService service) {
mService = service;
+ mPersister = new TaskSnapshotPersister(mService, Environment::getDataSystemCeDirectory);
+ mLoader = new TaskSnapshotLoader(mPersister);
mCache = new TaskSnapshotCache(mService, mLoader);
mIsRunningOnTv = mService.mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK);
@@ -270,7 +270,7 @@ class TaskSnapshotController {
}
final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
- final float scaleFraction = isLowRamDevice ? REDUCED_SCALE : 1f;
+ final float scaleFraction = isLowRamDevice ? mPersister.getReducedScale() : 1f;
task.getBounds(mTmpRect);
mTmpRect.offsetTo(0, 0);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
index 0e1570b6e462..d30843b9c589 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static com.android.server.wm.TaskSnapshotPersister.*;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -92,7 +91,7 @@ class TaskSnapshotLoader {
proto.topActivityComponent);
return new TaskSnapshot(topActivityComponent, buffer, proto.orientation,
new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
- reducedResolution, reducedResolution ? REDUCED_SCALE : 1f,
+ reducedResolution, reducedResolution ? mPersister.getReducedScale() : 1f,
proto.isRealSnapshot, proto.windowingMode, proto.systemUiVisibility,
proto.isTranslucent);
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 24b5b618210e..e6d646cb11d4 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -52,7 +52,9 @@ class TaskSnapshotPersister {
private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM;
private static final String SNAPSHOTS_DIRNAME = "snapshots";
private static final String REDUCED_POSTFIX = "_reduced";
- static final float REDUCED_SCALE = ActivityManager.isLowRamDeviceStatic() ? 0.6f : 0.5f;
+ private static final float REDUCED_SCALE = .5f;
+ private static final float LOW_RAM_REDUCED_SCALE = .6f;
+ private static final float LOW_RAM_RECENTS_REDUCED_SCALE = .1f;
static final boolean DISABLE_FULL_SIZED_BITMAPS = ActivityManager.isLowRamDeviceStatic();
private static final long DELAY_MS = 100;
private static final int QUALITY = 95;
@@ -71,6 +73,7 @@ class TaskSnapshotPersister {
private boolean mStarted;
private final Object mLock = new Object();
private final DirectoryResolver mDirectoryResolver;
+ private final float mReducedScale;
/**
* The list of ids of the tasks that have been persisted since {@link #removeObsoleteFiles} was
@@ -79,8 +82,16 @@ class TaskSnapshotPersister {
@GuardedBy("mLock")
private final ArraySet<Integer> mPersistedTaskIdsSinceLastRemoveObsolete = new ArraySet<>();
- TaskSnapshotPersister(DirectoryResolver resolver) {
+ TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) {
mDirectoryResolver = resolver;
+ if (service.mLowRamTaskSnapshotsAndRecents) {
+ // Use very low res snapshots if we are using Go version of recents.
+ mReducedScale = LOW_RAM_RECENTS_REDUCED_SCALE;
+ } else {
+ // TODO(122671846) Replace the low RAM value scale with the above when it is fully built
+ mReducedScale = ActivityManager.isLowRamDeviceStatic()
+ ? LOW_RAM_REDUCED_SCALE : REDUCED_SCALE;
+ }
}
/**
@@ -144,6 +155,15 @@ class TaskSnapshotPersister {
}
}
+ /**
+ * Gets the scaling the persister uses for low resolution task snapshots.
+ *
+ * @return the reduced scale of task snapshots when they are set to be low res
+ */
+ float getReducedScale() {
+ return mReducedScale;
+ }
+
@TestApi
void waitForQueueEmpty() {
while (true) {
@@ -350,8 +370,8 @@ class TaskSnapshotPersister {
final Bitmap reduced = mSnapshot.isReducedResolution()
? swBitmap
: Bitmap.createScaledBitmap(swBitmap,
- (int) (bitmap.getWidth() * REDUCED_SCALE),
- (int) (bitmap.getHeight() * REDUCED_SCALE), true /* filter */);
+ (int) (bitmap.getWidth() * mReducedScale),
+ (int) (bitmap.getHeight() * mReducedScale), true /* filter */);
try {
FileOutputStream reducedFos = new FileOutputStream(reducedFile);
reduced.compress(JPEG, QUALITY, reducedFos);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index b8a0739b9c83..b8db98b3c2f3 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -167,13 +167,11 @@ public class WindowAnimator {
screenRotationAnimation.kill();
displayAnimator.mScreenRotationAnimation = null;
- //TODO (multidisplay): Accessibility supported only for the default
// display.
- if (accessibilityController != null && dc.isDefaultDisplay) {
+ if (accessibilityController != null) {
// We just finished rotation animation which means we did not
// announce the rotation and waited for it to end, announce now.
- accessibilityController.onRotationChangedLocked(
- mService.getDefaultDisplayContentLocked());
+ accessibilityController.onRotationChangedLocked(dc);
}
}
}
@@ -197,9 +195,8 @@ public class WindowAnimator {
screenRotationAnimation.updateSurfaces(mTransaction);
}
orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (accessibilityController != null && dc.isDefaultDisplay) {
- accessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
+ if (accessibilityController != null) {
+ accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 1691dc0e3806..5267e7e55793 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -212,34 +212,40 @@ public abstract class WindowManagerInternal {
* and has access to the raw window data while the accessibility layer serves
* as a controller.
*
+ * @param displayId The logical display id.
* @param callbacks The callbacks to invoke.
+ * @return {@code false} if display id is not valid.
*/
- public abstract void setMagnificationCallbacks(@Nullable MagnificationCallbacks callbacks);
+ public abstract boolean setMagnificationCallbacks(int displayId,
+ @Nullable MagnificationCallbacks callbacks);
/**
* Set by the accessibility layer to specify the magnification and panning to
* be applied to all windows that should be magnified.
*
+ * @param displayId The logical display id.
* @param spec The MagnficationSpec to set.
*
- * @see #setMagnificationCallbacks(MagnificationCallbacks)
+ * @see #setMagnificationCallbacks(int, MagnificationCallbacks)
*/
- public abstract void setMagnificationSpec(MagnificationSpec spec);
+ public abstract void setMagnificationSpec(int displayId, MagnificationSpec spec);
/**
* Set by the accessibility framework to indicate whether the magnifiable regions of the display
* should be shown.
*
+ * @param displayId The logical display id.
* @param show {@code true} to show magnifiable region bounds, {@code false} to hide
*/
- public abstract void setForceShowMagnifiableBounds(boolean show);
+ public abstract void setForceShowMagnifiableBounds(int displayId, boolean show);
/**
* Obtains the magnification regions.
*
+ * @param displayId The logical display id.
* @param magnificationRegion the current magnification region
*/
- public abstract void getMagnificationRegion(@NonNull Region magnificationRegion);
+ public abstract void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion);
/**
* Gets the magnification and translation applied to a window given its token.
@@ -251,7 +257,7 @@ public abstract class WindowManagerInternal {
*
* @return The magnification spec for the window.
*
- * @see #setMagnificationCallbacks(MagnificationCallbacks)
+ * @see #setMagnificationCallbacks(int, MagnificationCallbacks)
*/
public abstract MagnificationSpec getCompatibleMagnificationSpecForWindow(
IBinder windowToken);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fda7a85c1270..c6679a9ad0d7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -433,11 +433,12 @@ public class WindowManagerService extends IWindowManager.Stub
final long mDrawLockTimeoutMillis;
final boolean mAllowAnimationsInLowPowerMode;
+ // TODO(b/122671846) Remove the flag below in favor of isLowRam once feature is stable
/**
* Use very low resolution task snapshots. Replaces task snapshot starting windows with
* splashscreen starting windows. Used on low RAM devices to save memory.
*/
- final boolean mLowRamTaskSnapshots;
+ final boolean mLowRamTaskSnapshotsAndRecents;
final boolean mAllowBootMessages;
@@ -955,7 +956,7 @@ public class WindowManagerService extends IWindowManager.Stub
com.android.internal.R.bool.config_disableTransitionAnimation);
mPerDisplayFocusEnabled = context.getResources().getBoolean(
com.android.internal.R.bool.config_perDisplayFocusEnabled);
- mLowRamTaskSnapshots = context.getResources().getBoolean(
+ mLowRamTaskSnapshotsAndRecents = context.getResources().getBoolean(
com.android.internal.R.bool.config_lowRamTaskSnapshotsAndRecents);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -1845,9 +1846,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
if (mAccessibilityController != null) {
WindowState window = mWindowMap.get(token);
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (window != null && window.getDisplayId() == DEFAULT_DISPLAY) {
- mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle);
+ if (window != null) {
+ mAccessibilityController.onRectangleOnScreenRequestedLocked(
+ window.getDisplayId(), rectangle);
}
}
}
@@ -2236,8 +2237,7 @@ public class WindowManagerService extends IWindowManager.Stub
win.mDestroying = true;
win.destroySurface(false, stopped);
}
- // TODO(multidisplay): Magnification is supported only for the default display.
- if (mAccessibilityController != null && win.getDisplayId() == DEFAULT_DISPLAY) {
+ if (mAccessibilityController != null) {
mAccessibilityController.onWindowTransitionLocked(win, transit);
}
@@ -6822,10 +6822,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void setMagnificationSpec(MagnificationSpec spec) {
+ public void setMagnificationSpec(int displayId, MagnificationSpec spec) {
synchronized (mGlobalLock) {
if (mAccessibilityController != null) {
- mAccessibilityController.setMagnificationSpecLocked(spec);
+ mAccessibilityController.setMagnificationSpecLocked(displayId, spec);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
}
@@ -6836,10 +6836,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void setForceShowMagnifiableBounds(boolean show) {
+ public void setForceShowMagnifiableBounds(int displayId, boolean show) {
synchronized (mGlobalLock) {
if (mAccessibilityController != null) {
- mAccessibilityController.setForceShowMagnifiableBoundsLocked(show);
+ mAccessibilityController.setForceShowMagnifiableBoundsLocked(displayId, show);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
}
@@ -6847,10 +6847,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void getMagnificationRegion(@NonNull Region magnificationRegion) {
+ public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) {
synchronized (mGlobalLock) {
if (mAccessibilityController != null) {
- mAccessibilityController.getMagnificationRegionLocked(magnificationRegion);
+ mAccessibilityController.getMagnificationRegionLocked(displayId,
+ magnificationRegion);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
}
@@ -6878,16 +6879,19 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void setMagnificationCallbacks(@Nullable MagnificationCallbacks callbacks) {
+ public boolean setMagnificationCallbacks(int displayId,
+ @Nullable MagnificationCallbacks callbacks) {
synchronized (mGlobalLock) {
if (mAccessibilityController == null) {
mAccessibilityController = new AccessibilityController(
WindowManagerService.this);
}
- mAccessibilityController.setMagnificationCallbacksLocked(callbacks);
+ boolean result = mAccessibilityController.setMagnificationCallbacksLocked(
+ displayId, callbacks);
if (!mAccessibilityController.hasCallbacksLocked()) {
mAccessibilityController = null;
}
+ return result;
}
}
@@ -7266,8 +7270,12 @@ public class WindowManagerService extends IWindowManager.Stub
}, false /* traverseTopToBottom */);
}
- public void applyMagnificationSpec(MagnificationSpec spec) {
- getDefaultDisplayContentLocked().applyMagnificationSpec(spec);
+ /** Called from Accessibility Controller to apply magnification spec */
+ public void applyMagnificationSpecLocked(int displayId, MagnificationSpec spec) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent != null) {
+ displayContent.applyMagnificationSpec(spec);
+ }
}
SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
@@ -7326,7 +7334,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void reparentDisplayContent(int displayId, IBinder surfaceControlHandle) {
+ public void reparentDisplayContent(int displayId, SurfaceControl sc) {
final Display display = mDisplayManager.getDisplay(displayId);
if (display == null) {
throw new IllegalArgumentException(
@@ -7343,7 +7351,7 @@ public class WindowManagerService extends IWindowManager.Stub
long token = Binder.clearCallingIdentity();
try {
DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
- displayContent.reparentDisplayContent(surfaceControlHandle);
+ displayContent.reparentDisplayContent(sc);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8f86c003e4b0..ce5eb8476764 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1607,8 +1607,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mWmService.mAccessibilityController;
final int winTransit = TRANSIT_EXIT;
mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */);
- //TODO (multidisplay): Magnification is supported only for the default
- if (accessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+ if (accessibilityController != null) {
accessibilityController.onWindowTransitionLocked(this, winTransit);
}
}
@@ -1625,8 +1624,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
if (isVisibleNow()) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
- //TODO (multidisplay): Magnification is supported only for the default
- if (mWmService.mAccessibilityController != null && isDefaultDisplay()) {
+ if (mWmService.mAccessibilityController != null) {
mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
}
changed = true;
@@ -1915,9 +1913,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
setDisplayLayoutNeeded();
mWmService.requestTraversal();
}
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mWmService.mAccessibilityController != null
- && displayId == DEFAULT_DISPLAY) {
+ if (mWmService.mAccessibilityController != null) {
mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index fb5c556e1643..6b4d6d2bfb3c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
@@ -1308,9 +1307,7 @@ class WindowStateAnimator {
transit = WindowManagerPolicy.TRANSIT_SHOW;
}
applyAnimationLocked(transit, true);
- //TODO (multidisplay): Magnification is supported only for the default display.
- if (mService.mAccessibilityController != null
- && mWin.getDisplayId() == DEFAULT_DISPLAY) {
+ if (mService.mAccessibilityController != null) {
mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
}
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index fb00aebb622f..3729eaf63ddb 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -53,6 +53,7 @@ cc_library_static {
],
include_dirs: [
+ "bionic/libc/private",
"frameworks/base/libs",
"frameworks/native/services",
"system/gatekeeper/include",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index dc0d53b41595..159a4960731d 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -24,6 +24,8 @@
#include <sensorservice/SensorService.h>
#include <sensorservicehidl/SensorManager.h>
+#include <bionic_malloc.h>
+
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/misc.h>
@@ -64,6 +66,11 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /
ALOGE_IF(err != OK, "Cannot register %s: %d", ISchedulingPolicyService::descriptor, err);
}
+static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
+ jobject /* clazz */) {
+ android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0);
+}
+
/*
* JNI registration.
*/
@@ -71,6 +78,8 @@ static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "startSensorService", "()V", (void*) android_server_SystemServer_startSensorService },
{ "startHidlServices", "()V", (void*) android_server_SystemServer_startHidlServices },
+ { "initZygoteChildHeapProfiling", "()V",
+ (void*) android_server_SystemServer_initZygoteChildHeapProfiling },
};
int register_android_server_SystemServer(JNIEnv* env)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f8e24ff03410..fb7e47d70d64 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4223,7 +4223,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setPasswordHistoryLength(ComponentName who, int length, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
@@ -4246,13 +4246,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getPasswordHistoryLength(ComponentName who, int userHandle, boolean parent) {
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ return 0;
+ }
return getStrictestPasswordRequirement(who, userHandle, parent,
admin -> admin.passwordHistoryLength, PASSWORD_QUALITY_UNSPECIFIED);
}
@Override
public void setPasswordExpirationTimeout(ComponentName who, long timeout, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
@@ -4288,7 +4291,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
*/
@Override
public long getPasswordExpirationTimeout(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return 0L;
}
enforceFullCrossUsersPermission(userHandle);
@@ -4423,7 +4426,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public long getPasswordExpiration(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return 0L;
}
enforceFullCrossUsersPermission(userHandle);
@@ -4770,6 +4773,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ return 0;
+ }
enforceFullCrossUsersPermission(userHandle);
synchronized (getLockObject()) {
if (!isCallerWithSystemUid()) {
@@ -4789,7 +4795,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setMaximumFailedPasswordsForWipe(ComponentName who, int num, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
@@ -4815,7 +4821,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getMaximumFailedPasswordsForWipe(ComponentName who, int userHandle, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return 0;
}
enforceFullCrossUsersPermission(userHandle);
@@ -4829,7 +4835,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return UserHandle.USER_NULL;
}
enforceFullCrossUsersPermission(userHandle);
@@ -4910,6 +4916,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
@Override
public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
+ if (!mLockPatternUtils.hasSecureLockScreen()) {
+ Slog.w(LOG_TAG, "Cannot reset password when the device has no lock screen");
+ return false;
+ }
+
final int callingUid = mInjector.binderGetCallingUid();
final int userHandle = mInjector.userHandleGetCallingUserId();
@@ -5252,7 +5263,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setRequiredStrongAuthTimeout(ComponentName who, long timeoutMs,
boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null");
@@ -5285,7 +5296,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
*/
@Override
public long getRequiredStrongAuthTimeout(ComponentName who, int userId, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
}
enforceFullCrossUsersPermission(userId);
@@ -6494,7 +6505,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
*/
@Override
public void setActivePasswordState(PasswordMetrics metrics, int userHandle) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
enforceFullCrossUsersPermission(userHandle);
@@ -6514,7 +6525,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void reportPasswordChanged(@UserIdInt int userId) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
enforceFullCrossUsersPermission(userId);
@@ -7656,18 +7667,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
// Shutting down backup manager service permanently.
- long ident = mInjector.binderClearCallingIdentity();
- try {
- if (mInjector.getIBackupManager() != null) {
- mInjector.getIBackupManager()
- .setBackupServiceActive(UserHandle.USER_SYSTEM, false);
- }
- } catch (RemoteException e) {
- throw new IllegalStateException("Failed deactivating backup service.", e);
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
- }
-
+ toggleBackupServiceActive(UserHandle.USER_SYSTEM, /* makeActive= */ false);
if (isAdb()) {
// Log device owner provisioning was started using adb.
MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER);
@@ -7695,7 +7695,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
saveUserRestrictionsLocked(userId);
}
- ident = mInjector.binderClearCallingIdentity();
+ long ident = mInjector.binderClearCallingIdentity();
try {
// TODO Send to system too?
sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED, userId);
@@ -7952,6 +7952,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
.write();
}
+ // Shutting down backup manager service permanently.
+ toggleBackupServiceActive(userHandle, /* makeActive= */ false);
+
mOwners.setProfileOwner(who, ownerName, userHandle);
mOwners.writeProfileOwner(userHandle);
Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
@@ -7975,6 +7978,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+
+ private void toggleBackupServiceActive(int userId, boolean makeActive) {
+ // Shutting down backup manager service permanently.
+ enforceUserUnlocked(userId);
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ if (mInjector.getIBackupManager() != null) {
+ mInjector.getIBackupManager()
+ .setBackupServiceActive(userId, makeActive);
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed deactivating backup service.", e);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+
+ }
+
@Override
public void clearProfileOwner(ComponentName who) {
if (!mHasFeature) {
@@ -8790,7 +8811,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
PersistableBundle args, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return;
}
Preconditions.checkNotNull(admin, "admin is null");
@@ -8807,7 +8828,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
ComponentName agent, int userHandle, boolean parent) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return null;
}
Preconditions.checkNotNull(agent, "agent null");
@@ -12727,22 +12748,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return;
}
Preconditions.checkNotNull(admin);
- synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- }
-
- final long ident = mInjector.binderClearCallingIdentity();
- try {
- IBackupManager ibm = mInjector.getIBackupManager();
- if (ibm != null) {
- ibm.setBackupServiceActive(UserHandle.USER_SYSTEM, enabled);
- }
- } catch (RemoteException e) {
- throw new IllegalStateException(
- "Failed " + (enabled ? "" : "de") + "activating backup service.", e);
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
- }
+ enforceProfileOrDeviceOwner(admin);
+ int userId = mInjector.userHandleGetCallingUserId();
+ toggleBackupServiceActive(userId, enabled);
}
@Override
@@ -12751,11 +12759,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return true;
}
+
+ enforceProfileOrDeviceOwner(admin);
synchronized (getLockObject()) {
try {
- getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
IBackupManager ibm = mInjector.getIBackupManager();
- return ibm != null && ibm.isBackupServiceActive(UserHandle.USER_SYSTEM);
+ return ibm != null && ibm.isBackupServiceActive(
+ mInjector.userHandleGetCallingUserId());
} catch (RemoteException e) {
throw new IllegalStateException("Failed requesting backup service state.", e);
}
@@ -13009,7 +13019,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
throw new IllegalStateException("logging is not available");
}
if (mNetworkLogger != null) {
- return mNetworkLogger.forceBatchFinalization();
+ final long ident = mInjector.binderClearCallingIdentity();
+ try {
+ return mNetworkLogger.forceBatchFinalization();
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
}
return 0;
}
@@ -13211,7 +13226,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean setResetPasswordToken(ComponentName admin, byte[] token) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return false;
}
if (token == null || token.length < 32) {
@@ -13239,7 +13254,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean clearResetPasswordToken(ComponentName admin) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
return false;
}
synchronized (getLockObject()) {
@@ -13265,6 +13280,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean isResetPasswordTokenActive(ComponentName admin) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
+ return false;
+ }
synchronized (getLockObject()) {
final int userHandle = mInjector.userHandleGetCallingUserId();
getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -13286,6 +13304,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
@Override
public boolean resetPasswordWithToken(ComponentName admin, String passwordOrNull, byte[] token,
int flags) {
+ if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
+ return false;
+ }
Preconditions.checkNotNull(token);
synchronized (getLockObject()) {
final int userHandle = mInjector.userHandleGetCallingUserId();
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
index 238f07715ce3..e99dd4f1cbae 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreDatabase.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -21,9 +21,12 @@ import android.annotation.Nullable;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.database.sqlite.SQLiteCursor;
+import android.database.sqlite.SQLiteCursorDriver;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQuery;
import android.net.NetworkUtils;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.Status;
@@ -35,6 +38,7 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
+import java.util.StringJoiner;
/**
* Encapsulating class for using the SQLite database backing the memory store.
@@ -46,6 +50,9 @@ import java.util.List;
*/
public class IpMemoryStoreDatabase {
private static final String TAG = IpMemoryStoreDatabase.class.getSimpleName();
+ // A pair of NetworkAttributes objects is group-close if the confidence that they are
+ // the same is above this cutoff. See NetworkAttributes and SameL3NetworkResponse.
+ private static final float GROUPCLOSE_CONFIDENCE = 0.5f;
/**
* Contract class for the Network Attributes table.
@@ -134,12 +141,14 @@ public class IpMemoryStoreDatabase {
}
/** Called when the database is created */
+ @Override
public void onCreate(@NonNull final SQLiteDatabase db) {
db.execSQL(NetworkAttributesContract.CREATE_TABLE);
db.execSQL(PrivateDataContract.CREATE_TABLE);
}
/** Called when the database is upgraded */
+ @Override
public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion,
final int newVersion) {
// No upgrade supported yet.
@@ -149,6 +158,7 @@ public class IpMemoryStoreDatabase {
}
/** Called when the database is downgraded */
+ @Override
public void onDowngrade(@NonNull final SQLiteDatabase db, final int oldVersion,
final int newVersion) {
// Downgrades always nuke all data and recreate an empty table.
@@ -184,30 +194,35 @@ public class IpMemoryStoreDatabase {
return addresses;
}
+ @NonNull
+ private static ContentValues toContentValues(@Nullable final NetworkAttributes attributes) {
+ final ContentValues values = new ContentValues();
+ if (null == attributes) return values;
+ if (null != attributes.assignedV4Address) {
+ values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS,
+ NetworkUtils.inet4AddressToIntHTH(attributes.assignedV4Address));
+ }
+ if (null != attributes.groupHint) {
+ values.put(NetworkAttributesContract.COLNAME_GROUPHINT, attributes.groupHint);
+ }
+ if (null != attributes.dnsAddresses) {
+ values.put(NetworkAttributesContract.COLNAME_DNSADDRESSES,
+ encodeAddressList(attributes.dnsAddresses));
+ }
+ if (null != attributes.mtu) {
+ values.put(NetworkAttributesContract.COLNAME_MTU, attributes.mtu);
+ }
+ return values;
+ }
+
// Convert a NetworkAttributes object to content values to store them in a table compliant
// with the contract defined in NetworkAttributesContract.
@NonNull
private static ContentValues toContentValues(@NonNull final String key,
@Nullable final NetworkAttributes attributes, final long expiry) {
- final ContentValues values = new ContentValues();
+ final ContentValues values = toContentValues(attributes);
values.put(NetworkAttributesContract.COLNAME_L2KEY, key);
values.put(NetworkAttributesContract.COLNAME_EXPIRYDATE, expiry);
- if (null != attributes) {
- if (null != attributes.assignedV4Address) {
- values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS,
- NetworkUtils.inet4AddressToIntHTH(attributes.assignedV4Address));
- }
- if (null != attributes.groupHint) {
- values.put(NetworkAttributesContract.COLNAME_GROUPHINT, attributes.groupHint);
- }
- if (null != attributes.dnsAddresses) {
- values.put(NetworkAttributesContract.COLNAME_DNSADDRESSES,
- encodeAddressList(attributes.dnsAddresses));
- }
- if (null != attributes.mtu) {
- values.put(NetworkAttributesContract.COLNAME_MTU, attributes.mtu);
- }
- }
return values;
}
@@ -225,6 +240,32 @@ public class IpMemoryStoreDatabase {
return values;
}
+ @Nullable
+ private static NetworkAttributes readNetworkAttributesLine(@NonNull final Cursor cursor) {
+ // Make sure the data hasn't expired
+ final long expiry = getLong(cursor, NetworkAttributesContract.COLNAME_EXPIRYDATE, -1L);
+ if (expiry < System.currentTimeMillis()) return null;
+
+ final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
+ final int assignedV4AddressInt = getInt(cursor,
+ NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS, 0);
+ final String groupHint = getString(cursor, NetworkAttributesContract.COLNAME_GROUPHINT);
+ final byte[] dnsAddressesBlob =
+ getBlob(cursor, NetworkAttributesContract.COLNAME_DNSADDRESSES);
+ final int mtu = getInt(cursor, NetworkAttributesContract.COLNAME_MTU, -1);
+ if (0 != assignedV4AddressInt) {
+ builder.setAssignedV4Address(NetworkUtils.intToInet4AddressHTH(assignedV4AddressInt));
+ }
+ builder.setGroupHint(groupHint);
+ if (null != dnsAddressesBlob) {
+ builder.setDnsAddresses(decodeAddressList(dnsAddressesBlob));
+ }
+ if (mtu >= 0) {
+ builder.setMtu(mtu);
+ }
+ return builder.build();
+ }
+
private static final String[] EXPIRY_COLUMN = new String[] {
NetworkAttributesContract.COLNAME_EXPIRYDATE
};
@@ -247,7 +288,9 @@ public class IpMemoryStoreDatabase {
// result here. 0 results means the key was not found.
if (cursor.getCount() != 1) return EXPIRY_ERROR;
cursor.moveToFirst();
- return cursor.getLong(0); // index in the EXPIRY_COLUMN array
+ final long result = cursor.getLong(0); // index in the EXPIRY_COLUMN array
+ cursor.close();
+ return result;
}
static final int RELEVANCE_ERROR = -1; // Legal values for relevance are positive
@@ -308,30 +351,9 @@ public class IpMemoryStoreDatabase {
// result here. 0 results means the key was not found.
if (cursor.getCount() != 1) return null;
cursor.moveToFirst();
-
- // Make sure the data hasn't expired
- final long expiry = cursor.getLong(
- cursor.getColumnIndexOrThrow(NetworkAttributesContract.COLNAME_EXPIRYDATE));
- if (expiry < System.currentTimeMillis()) return null;
-
- final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
- final int assignedV4AddressInt = getInt(cursor,
- NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS, 0);
- final String groupHint = getString(cursor, NetworkAttributesContract.COLNAME_GROUPHINT);
- final byte[] dnsAddressesBlob =
- getBlob(cursor, NetworkAttributesContract.COLNAME_DNSADDRESSES);
- final int mtu = getInt(cursor, NetworkAttributesContract.COLNAME_MTU, -1);
- if (0 != assignedV4AddressInt) {
- builder.setAssignedV4Address(NetworkUtils.intToInet4AddressHTH(assignedV4AddressInt));
- }
- builder.setGroupHint(groupHint);
- if (null != dnsAddressesBlob) {
- builder.setDnsAddresses(decodeAddressList(dnsAddressesBlob));
- }
- if (mtu >= 0) {
- builder.setMtu(mtu);
- }
- return builder.build();
+ final NetworkAttributes attributes = readNetworkAttributesLine(cursor);
+ cursor.close();
+ return attributes;
}
private static final String[] DATA_COLUMN = new String[] {
@@ -353,20 +375,139 @@ public class IpMemoryStoreDatabase {
// get more than one result here. 0 results means the key was not found.
if (cursor.getCount() != 1) return null;
cursor.moveToFirst();
- return cursor.getBlob(0); // index in the DATA_COLUMN array
+ final byte[] result = cursor.getBlob(0); // index in the DATA_COLUMN array
+ cursor.close();
+ return result;
+ }
+
+ /**
+ * The following is a horrible hack that is necessary because the Android SQLite API does not
+ * have a way to query a binary blob. This, almost certainly, is an overlook.
+ *
+ * The Android SQLite API has two family of methods : one for query that returns data, and
+ * one for more general SQL statements that can execute any statement but may not return
+ * anything. All the query methods, however, take only String[] for the arguments.
+ *
+ * In principle it is simple to write a function that will encode the binary blob in the
+ * way SQLite expects it. However, because the API forces the argument to be coerced into a
+ * String, the SQLiteQuery object generated by the default query methods will bind all
+ * arguments as Strings and SQL will *sanitize* them. This works okay for numeric types,
+ * but the format for blobs is x'<hex string>'. Note the presence of quotes, which will
+ * be sanitized, changing the contents of the field, and the query will fail to match the
+ * blob.
+ *
+ * As far as I can tell, there are two possible ways around this problem. The first one
+ * is to put the data in the query string and eschew it being an argument. This would
+ * require doing the sanitizing by hand. The other is to call bindBlob directly on the
+ * generated SQLiteQuery object, which not only is a lot less dangerous than rolling out
+ * sanitizing, but also will do the right thing if the underlying format ever changes.
+ *
+ * But none of the methods that take an SQLiteQuery object can return data ; this *must*
+ * be called with SQLiteDatabase#query. This object is not accessible from outside.
+ * However, there is a #query version that accepts a CursorFactory and this is pretty
+ * straightforward to implement as all the arguments are coming in and the SQLiteCursor
+ * class is public API.
+ * With this, it's possible to intercept the SQLiteQuery object, and assuming the args
+ * are available, to bind them directly and work around the API's oblivious coercion into
+ * Strings.
+ *
+ * This is really sad, but I don't see another way of having this work than this or the
+ * hand-rolled sanitizing, and this is the lesser evil.
+ */
+ private static class CustomCursorFactory implements SQLiteDatabase.CursorFactory {
+ @NonNull
+ private final ArrayList<Object> mArgs;
+ CustomCursorFactory(@NonNull final ArrayList<Object> args) {
+ mArgs = args;
+ }
+ @Override
+ public Cursor newCursor(final SQLiteDatabase db, final SQLiteCursorDriver masterQuery,
+ final String editTable,
+ final SQLiteQuery query) {
+ int index = 1; // bind is 1-indexed
+ for (final Object arg : mArgs) {
+ if (arg instanceof String) {
+ query.bindString(index++, (String) arg);
+ } else if (arg instanceof Long) {
+ query.bindLong(index++, (Long) arg);
+ } else if (arg instanceof Integer) {
+ query.bindLong(index++, Long.valueOf((Integer) arg));
+ } else if (arg instanceof byte[]) {
+ query.bindBlob(index++, (byte[]) arg);
+ } else {
+ throw new IllegalStateException("Unsupported type CustomCursorFactory "
+ + arg.getClass().toString());
+ }
+ }
+ return new SQLiteCursor(masterQuery, editTable, query);
+ }
+ }
+
+ // Returns the l2key of the closest match, if and only if it matches
+ // closely enough (as determined by group-closeness).
+ @Nullable
+ static String findClosestAttributes(@NonNull final SQLiteDatabase db,
+ @NonNull final NetworkAttributes attr) {
+ if (attr.isEmpty()) return null;
+ final ContentValues values = toContentValues(attr);
+
+ // Build the selection and args. To cut down on the number of lines to search, limit
+ // the search to those with at least one argument equals to the requested attributes.
+ // This works only because null attributes match only will not result in group-closeness.
+ final StringJoiner sj = new StringJoiner(" OR ");
+ final ArrayList<Object> args = new ArrayList<>();
+ args.add(System.currentTimeMillis());
+ for (final String field : values.keySet()) {
+ sj.add(field + " = ?");
+ args.add(values.get(field));
+ }
+
+ final String selection = NetworkAttributesContract.COLNAME_EXPIRYDATE + " > ? AND ("
+ + sj.toString() + ")";
+ final Cursor cursor = db.queryWithFactory(new CustomCursorFactory(args),
+ false, // distinct
+ NetworkAttributesContract.TABLENAME,
+ null, // columns, null means everything
+ selection, // selection
+ null, // selectionArgs, horrendously passed to the cursor factory instead
+ null, // groupBy
+ null, // having
+ null, // orderBy
+ null); // limit
+ if (cursor.getCount() <= 0) return null;
+ cursor.moveToFirst();
+ String bestKey = null;
+ float bestMatchConfidence = GROUPCLOSE_CONFIDENCE; // Never return a match worse than this.
+ while (!cursor.isAfterLast()) {
+ final NetworkAttributes read = readNetworkAttributesLine(cursor);
+ final float confidence = read.getNetworkGroupSamenessConfidence(attr);
+ if (confidence > bestMatchConfidence) {
+ bestKey = getString(cursor, NetworkAttributesContract.COLNAME_L2KEY);
+ bestMatchConfidence = confidence;
+ }
+ cursor.moveToNext();
+ }
+ cursor.close();
+ return bestKey;
}
// Helper methods
- static String getString(final Cursor cursor, final String columnName) {
+ private static String getString(final Cursor cursor, final String columnName) {
final int columnIndex = cursor.getColumnIndex(columnName);
return (columnIndex >= 0) ? cursor.getString(columnIndex) : null;
}
- static byte[] getBlob(final Cursor cursor, final String columnName) {
+ private static byte[] getBlob(final Cursor cursor, final String columnName) {
final int columnIndex = cursor.getColumnIndex(columnName);
return (columnIndex >= 0) ? cursor.getBlob(columnIndex) : null;
}
- static int getInt(final Cursor cursor, final String columnName, final int defaultValue) {
+ private static int getInt(final Cursor cursor, final String columnName,
+ final int defaultValue) {
final int columnIndex = cursor.getColumnIndex(columnName);
return (columnIndex >= 0) ? cursor.getInt(columnIndex) : defaultValue;
}
+ private static long getLong(final Cursor cursor, final String columnName,
+ final long defaultValue) {
+ final int columnIndex = cursor.getColumnIndex(columnName);
+ return (columnIndex >= 0) ? cursor.getLong(columnIndex) : defaultValue;
+ }
}
diff --git a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
index 444b299d49e6..d43dc6a24260 100644
--- a/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
+++ b/services/ipmemorystore/java/com/android/server/net/ipmemorystore/IpMemoryStoreService.java
@@ -37,6 +37,7 @@ import android.net.ipmemorystore.IOnSameNetworkResponseListener;
import android.net.ipmemorystore.IOnStatusListener;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.SameL3NetworkResponse;
import android.net.ipmemorystore.Status;
import android.net.ipmemorystore.StatusParcelable;
import android.net.ipmemorystore.Utils;
@@ -249,9 +250,26 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub {
* Through the listener, returns the L2 key if one matched, or null.
*/
@Override
- public void findL2Key(@NonNull final NetworkAttributesParcelable attributes,
- @NonNull final IOnL2KeyResponseListener listener) {
- // TODO : implement this.
+ public void findL2Key(@Nullable final NetworkAttributesParcelable attributes,
+ @Nullable final IOnL2KeyResponseListener listener) {
+ if (null == listener) return;
+ mExecutor.execute(() -> {
+ try {
+ if (null == attributes) {
+ listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
+ return;
+ }
+ if (null == mDb) {
+ listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
+ return;
+ }
+ final String key = IpMemoryStoreDatabase.findClosestAttributes(mDb,
+ new NetworkAttributes(attributes));
+ listener.onL2KeyResponse(makeStatus(SUCCESS), key);
+ } catch (final RemoteException e) {
+ // Client at the other end died
+ }
+ });
}
/**
@@ -264,9 +282,40 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub {
* Through the listener, a SameL3NetworkResponse containing the answer and confidence.
*/
@Override
- public void isSameNetwork(@NonNull final String l2Key1, @NonNull final String l2Key2,
- @NonNull final IOnSameNetworkResponseListener listener) {
- // TODO : implement this.
+ public void isSameNetwork(@Nullable final String l2Key1, @Nullable final String l2Key2,
+ @Nullable final IOnSameNetworkResponseListener listener) {
+ if (null == listener) return;
+ mExecutor.execute(() -> {
+ try {
+ if (null == l2Key1 || null == l2Key2) {
+ listener.onSameNetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
+ return;
+ }
+ if (null == mDb) {
+ listener.onSameNetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
+ return;
+ }
+ try {
+ final NetworkAttributes attr1 =
+ IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key1);
+ final NetworkAttributes attr2 =
+ IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key2);
+ if (null == attr1 || null == attr2) {
+ listener.onSameNetworkResponse(makeStatus(SUCCESS),
+ new SameL3NetworkResponse(l2Key1, l2Key2,
+ -1f /* never connected */).toParcelable());
+ return;
+ }
+ final float confidence = attr1.getNetworkGroupSamenessConfidence(attr2);
+ listener.onSameNetworkResponse(makeStatus(SUCCESS),
+ new SameL3NetworkResponse(l2Key1, l2Key2, confidence).toParcelable());
+ } catch (Exception e) {
+ listener.onSameNetworkResponse(makeStatus(ERROR_GENERIC), null);
+ }
+ } catch (final RemoteException e) {
+ // Client at the other end died
+ }
+ });
}
/**
@@ -285,21 +334,22 @@ public class IpMemoryStoreService extends IIpMemoryStore.Stub {
mExecutor.execute(() -> {
try {
if (null == l2Key) {
- listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), l2Key, null);
+ listener.onNetworkAttributesRetrieved(
+ makeStatus(ERROR_ILLEGAL_ARGUMENT), l2Key, null);
return;
}
if (null == mDb) {
- listener.onL2KeyResponse(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), l2Key,
- null);
+ listener.onNetworkAttributesRetrieved(
+ makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), l2Key, null);
return;
}
try {
final NetworkAttributes attributes =
IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key);
- listener.onL2KeyResponse(makeStatus(SUCCESS), l2Key,
+ listener.onNetworkAttributesRetrieved(makeStatus(SUCCESS), l2Key,
null == attributes ? null : attributes.toParcelable());
} catch (final Exception e) {
- listener.onL2KeyResponse(makeStatus(ERROR_GENERIC), l2Key, null);
+ listener.onNetworkAttributesRetrieved(makeStatus(ERROR_GENERIC), l2Key, null);
}
} catch (final RemoteException e) {
// Client at the other end died
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cef47caff740..623990ba211a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -136,6 +136,7 @@ import com.android.server.stats.StatsCompanionService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
import com.android.server.telecom.TelecomLoaderService;
+import com.android.server.testharness.TestHarnessModeService;
import com.android.server.textclassifier.TextClassificationManagerService;
import com.android.server.textservices.TextServicesManagerService;
import com.android.server.trust.TrustManagerService;
@@ -327,6 +328,11 @@ public final class SystemServer {
private static native void startHidlServices();
/**
+ * Mark this process' heap as profileable. Only for debug builds.
+ */
+ private static native void initZygoteChildHeapProfiling();
+
+ /**
* The main entry point from zygote.
*/
public static void main(String[] args) {
@@ -448,6 +454,11 @@ public final class SystemServer {
// Initialize native services.
System.loadLibrary("android_servers");
+ // Debug builds - allow heap profiling.
+ if (Build.IS_DEBUGGABLE) {
+ initZygoteChildHeapProfiling();
+ }
+
// Check whether we failed to shut down last time we tried.
// This call may not return.
performPendingShutdown();
@@ -1147,6 +1158,10 @@ public final class SystemServer {
traceBeginAndSlog("StartPersistentDataBlock");
mSystemServiceManager.startService(PersistentDataBlockService.class);
traceEnd();
+
+ traceBeginAndSlog("StartTestHarnessMode");
+ mSystemServiceManager.startService(TestHarnessModeService.class);
+ traceEnd();
}
if (hasPdb || OemLockService.isHalPresent()) {
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 3351b25d0eec..494395285f5b 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -20,6 +20,7 @@ import static android.net.util.NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
import static android.net.util.NetworkConstants.ICMPV6_ROUTER_SOLICITATION;
+import static android.net.util.SocketUtils.makePacketSocketAddress;
import static android.system.OsConstants.AF_PACKET;
import static android.system.OsConstants.ARPHRD_ETHER;
import static android.system.OsConstants.ETH_P_ARP;
@@ -55,7 +56,6 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
-import android.system.PacketSocketAddress;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
@@ -72,6 +72,7 @@ import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.BufferUnderflowException;
@@ -472,7 +473,7 @@ public class ApfFilter {
installNewProgramLocked();
}
socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
- PacketSocketAddress addr = new PacketSocketAddress(
+ SocketAddress addr = makePacketSocketAddress(
(short) ETH_P_IPV6, mInterfaceParams.index);
Os.bind(socket, addr);
NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 15acc0ede8b2..04ac9a301813 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -28,6 +28,7 @@ import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.net.util.SocketUtils.makePacketSocketAddress;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_PACKET;
import static android.system.OsConstants.ETH_P_IP;
@@ -35,7 +36,6 @@ import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
import static android.system.OsConstants.SOCK_RAW;
import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BINDTODEVICE;
import static android.system.OsConstants.SO_BROADCAST;
import static android.system.OsConstants.SO_RCVBUF;
import static android.system.OsConstants.SO_REUSEADDR;
@@ -44,23 +44,22 @@ import android.content.Context;
import android.net.DhcpResults;
import android.net.NetworkUtils;
import android.net.TrafficStats;
+import android.net.ip.IpClient;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.IpConnectivityLog;
import android.net.util.InterfaceParams;
+import android.net.util.SocketUtils;
import android.os.Message;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
-import android.system.PacketSocketAddress;
import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
-import android.util.TimeUtils;
import com.android.internal.util.HexDump;
import com.android.internal.util.MessageUtils;
-import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
@@ -70,6 +69,7 @@ import libcore.io.IoBridge;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.Inet4Address;
+import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.Arrays;
@@ -117,7 +117,8 @@ public class DhcpClient extends StateMachine {
// t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
private static final int DHCP_TIMEOUT_MS = 36 * SECONDS;
- private static final int PUBLIC_BASE = Protocol.BASE_DHCP;
+ // DhcpClient uses IpClient's handler.
+ private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE;
/* Commands from controller to start/stop DHCP */
public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
@@ -147,7 +148,7 @@ public class DhcpClient extends StateMachine {
public static final int DHCP_FAILURE = 2;
// Internal messages.
- private static final int PRIVATE_BASE = Protocol.BASE_DHCP + 100;
+ private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100;
private static final int CMD_KICK = PRIVATE_BASE + 1;
private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
@@ -204,7 +205,7 @@ public class DhcpClient extends StateMachine {
private InterfaceParams mIface;
// TODO: MacAddress-ify more of this class hierarchy.
private byte[] mHwAddr;
- private PacketSocketAddress mInterfaceBroadcastAddr;
+ private SocketAddress mInterfaceBroadcastAddr;
private int mTransactionId;
private long mTransactionStartMillis;
private DhcpResults mDhcpLease;
@@ -293,7 +294,7 @@ public class DhcpClient extends StateMachine {
}
mHwAddr = mIface.macAddr.toByteArray();
- mInterfaceBroadcastAddr = new PacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
+ mInterfaceBroadcastAddr = makePacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
return true;
}
@@ -309,7 +310,7 @@ public class DhcpClient extends StateMachine {
private boolean initPacketSocket() {
try {
mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
- PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.index);
+ SocketAddress addr = makePacketSocketAddress((short) ETH_P_IP, mIface.index);
Os.bind(mPacketSock, addr);
NetworkUtils.attachDhcpFilter(mPacketSock);
} catch(SocketException|ErrnoException e) {
@@ -323,12 +324,11 @@ public class DhcpClient extends StateMachine {
final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP);
try {
mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
- Os.setsockoptIfreq(mUdpSock, SOL_SOCKET, SO_BINDTODEVICE, mIfaceName);
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT);
- NetworkUtils.protectFromVpn(mUdpSock);
} catch(SocketException|ErrnoException e) {
Log.e(TAG, "Error creating UDP socket", e);
return false;
@@ -544,13 +544,13 @@ public class DhcpClient extends StateMachine {
private String messageToString(Message message) {
long now = SystemClock.uptimeMillis();
- StringBuilder b = new StringBuilder(" ");
- TimeUtils.formatDuration(message.getWhen() - now, b);
- b.append(" ").append(messageName(message.what))
+ return new StringBuilder(" ")
+ .append(message.getWhen() - now)
+ .append(messageName(message.what))
.append(" ").append(message.arg1)
.append(" ").append(message.arg2)
- .append(" ").append(message.obj);
- return b.toString();
+ .append(" ").append(message.obj)
+ .toString();
}
@Override
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index bef425a37da4..385dd52e4576 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -16,6 +16,7 @@
package android.net.ip;
+import static android.net.util.SocketUtils.makePacketSocketAddress;
import static android.system.OsConstants.AF_PACKET;
import static android.system.OsConstants.ARPHRD_ETHER;
import static android.system.OsConstants.ETH_P_ALL;
@@ -28,7 +29,6 @@ import android.net.util.PacketReader;
import android.os.Handler;
import android.system.ErrnoException;
import android.system.Os;
-import android.system.PacketSocketAddress;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
@@ -103,7 +103,7 @@ public class ConnectivityPacketTracker {
try {
s = Os.socket(AF_PACKET, SOCK_RAW, 0);
NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
- Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mInterface.index));
+ Os.bind(s, makePacketSocketAddress((short) ETH_P_ALL, mInterface.index));
} catch (ErrnoException | IOException e) {
logError("Failed to create packet tracking socket: ", e);
closeFd(s);
diff --git a/services/net/java/android/net/ip/InterfaceController.java b/services/net/java/android/net/ip/InterfaceController.java
index 55dfcef81890..b3af67cdbdc3 100644
--- a/services/net/java/android/net/ip/InterfaceController.java
+++ b/services/net/java/android/net/ip/InterfaceController.java
@@ -18,13 +18,14 @@ package android.net.ip;
import android.net.INetd;
import android.net.InterfaceConfiguration;
+import android.net.InterfaceConfigurationParcel;
import android.net.LinkAddress;
import android.net.util.SharedLog;
-import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.system.OsConstants;
+import java.net.Inet4Address;
import java.net.InetAddress;
@@ -39,76 +40,96 @@ public class InterfaceController {
private final static boolean DBG = false;
private final String mIfName;
- private final INetworkManagementService mNMS;
private final INetd mNetd;
private final SharedLog mLog;
- public InterfaceController(String ifname, INetworkManagementService nms, INetd netd,
- SharedLog log) {
+ public InterfaceController(String ifname, INetd netd, SharedLog log) {
mIfName = ifname;
- mNMS = nms;
mNetd = netd;
mLog = log;
}
- public boolean setIPv4Address(LinkAddress address) {
- final InterfaceConfiguration ifcg = new InterfaceConfiguration();
- ifcg.setLinkAddress(address);
+ private boolean setInterfaceConfig(InterfaceConfiguration config) {
+ final InterfaceConfigurationParcel cfgParcel = config.toParcel(mIfName);
+
try {
- mNMS.setInterfaceConfig(mIfName, ifcg);
- if (DBG) mLog.log("IPv4 configuration succeeded");
- } catch (IllegalStateException | RemoteException e) {
- logError("IPv4 configuration failed: %s", e);
+ mNetd.interfaceSetCfg(cfgParcel);
+ } catch (RemoteException | ServiceSpecificException e) {
+ logError("Setting IPv4 address to %s/%d failed: %s",
+ cfgParcel.ipv4Addr, cfgParcel.prefixLength, e);
return false;
}
return true;
}
- public boolean clearIPv4Address() {
- try {
- final InterfaceConfiguration ifcg = new InterfaceConfiguration();
- ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
- mNMS.setInterfaceConfig(mIfName, ifcg);
- } catch (IllegalStateException | RemoteException e) {
- logError("Failed to clear IPv4 address on interface %s: %s", mIfName, e);
+ /**
+ * Set the IPv4 address of the interface.
+ */
+ public boolean setIPv4Address(LinkAddress address) {
+ if (!(address.getAddress() instanceof Inet4Address)) {
return false;
}
- return true;
+ final InterfaceConfiguration ifConfig = new InterfaceConfiguration();
+ ifConfig.setLinkAddress(address);
+ return setInterfaceConfig(ifConfig);
}
- public boolean enableIPv6() {
+ /**
+ * Clear the IPv4Address of the interface.
+ */
+ public boolean clearIPv4Address() {
+ final InterfaceConfiguration ifConfig = new InterfaceConfiguration();
+ ifConfig.setLinkAddress(new LinkAddress("0.0.0.0/0"));
+ return setInterfaceConfig(ifConfig);
+ }
+
+ private boolean setEnableIPv6(boolean enabled) {
try {
- mNMS.enableIpv6(mIfName);
- } catch (IllegalStateException | RemoteException e) {
- logError("enabling IPv6 failed: %s", e);
+ mNetd.interfaceSetEnableIPv6(mIfName, enabled);
+ } catch (RemoteException | ServiceSpecificException e) {
+ logError("%s IPv6 failed: %s", (enabled ? "enabling" : "disabling"), e);
return false;
}
return true;
}
+ /**
+ * Enable IPv6 on the interface.
+ */
+ public boolean enableIPv6() {
+ return setEnableIPv6(true);
+ }
+
+ /**
+ * Disable IPv6 on the interface.
+ */
public boolean disableIPv6() {
- try {
- mNMS.disableIpv6(mIfName);
- } catch (IllegalStateException | RemoteException e) {
- logError("disabling IPv6 failed: %s", e);
- return false;
- }
- return true;
+ return setEnableIPv6(false);
}
+ /**
+ * Enable or disable IPv6 privacy extensions on the interface.
+ * @param enabled Whether the extensions should be enabled.
+ */
public boolean setIPv6PrivacyExtensions(boolean enabled) {
try {
- mNMS.setInterfaceIpv6PrivacyExtensions(mIfName, enabled);
- } catch (IllegalStateException | RemoteException e) {
- logError("error setting IPv6 privacy extensions: %s", e);
+ mNetd.interfaceSetIPv6PrivacyExtensions(mIfName, enabled);
+ } catch (RemoteException | ServiceSpecificException e) {
+ logError("error %s IPv6 privacy extensions: %s",
+ (enabled ? "enabling" : "disabling"), e);
return false;
}
return true;
}
+ /**
+ * Set IPv6 address generation mode on the interface.
+ *
+ * <p>IPv6 should be disabled before changing the mode.
+ */
public boolean setIPv6AddrGenModeIfSupported(int mode) {
try {
- mNMS.setIPv6AddrGenMode(mIfName, mode);
+ mNetd.setIPv6AddrGenMode(mIfName, mode);
} catch (RemoteException e) {
logError("Unable to set IPv6 addrgen mode: %s", e);
return false;
@@ -121,10 +142,16 @@ public class InterfaceController {
return true;
}
+ /**
+ * Add an address to the interface.
+ */
public boolean addAddress(LinkAddress addr) {
return addAddress(addr.getAddress(), addr.getPrefixLength());
}
+ /**
+ * Add an address to the interface.
+ */
public boolean addAddress(InetAddress ip, int prefixLen) {
try {
mNetd.interfaceAddAddress(mIfName, ip.getHostAddress(), prefixLen);
@@ -135,6 +162,9 @@ public class InterfaceController {
return true;
}
+ /**
+ * Remove an address from the interface.
+ */
public boolean removeAddress(InetAddress ip, int prefixLen) {
try {
mNetd.interfaceDelAddress(mIfName, ip.getHostAddress(), prefixLen);
@@ -145,9 +175,12 @@ public class InterfaceController {
return true;
}
+ /**
+ * Remove all addresses from the interface.
+ */
public boolean clearAllAddresses() {
try {
- mNMS.clearInterfaceAddresses(mIfName);
+ mNetd.interfaceClearAddrs(mIfName);
} catch (Exception e) {
logError("Failed to clear addresses: %s", e);
return false;
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 9f1557354dc4..233b86f5f8b2 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -19,12 +19,12 @@ package android.net.ip;
import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.DhcpResults;
import android.net.INetd;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.LinkProperties.ProvisioningChange;
import android.net.Network;
import android.net.ProvisioningConfigurationParcelable;
import android.net.ProxyInfo;
@@ -38,7 +38,6 @@ import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpManagerEvent;
import android.net.shared.InitialConfiguration;
import android.net.util.InterfaceParams;
-import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
import android.net.util.SharedLog;
import android.os.ConditionVariable;
@@ -359,6 +358,9 @@ public class IpClient extends StateMachine {
private static final int CMD_JUMP_RUNNING_TO_STOPPING = 101;
private static final int CMD_JUMP_STOPPING_TO_STOPPED = 102;
+ // IpClient shares a handler with DhcpClient: commands must not overlap
+ public static final int DHCPCLIENT_CMD_BASE = 1000;
+
private static final int MAX_LOG_RECORDS = 500;
private static final int MAX_PACKET_RECORDS = 100;
@@ -371,6 +373,11 @@ public class IpClient extends StateMachine {
private static final int IMMEDIATE_FAILURE_DURATION = 0;
+ private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1;
+ private static final int PROV_CHANGE_LOST_PROVISIONING = 2;
+ private static final int PROV_CHANGE_GAINED_PROVISIONING = 3;
+ private static final int PROV_CHANGE_STILL_PROVISIONED = 4;
+
private final State mStoppedState = new StoppedState();
private final State mStoppingState = new StoppingState();
private final State mStartedState = new StartedState();
@@ -384,6 +391,7 @@ public class IpClient extends StateMachine {
protected final IpClientCallbacks mCallback;
private final Dependencies mDependencies;
private final CountDownLatch mShutdownLatch;
+ private final ConnectivityManager mCm;
private final INetworkManagementService mNwService;
private final NetlinkTracker mNetlinkTracker;
private final WakeupMessage mProvisioningTimeoutAlarm;
@@ -401,7 +409,6 @@ public class IpClient extends StateMachine {
*/
private LinkProperties mLinkProperties;
private android.net.shared.ProvisioningConfiguration mConfiguration;
- private MultinetworkPolicyTracker mMultinetworkPolicyTracker;
private IpReachabilityMonitor mIpReachabilityMonitor;
private DhcpClient mDhcpClient;
private DhcpResults mDhcpResults;
@@ -469,6 +476,7 @@ public class IpClient extends StateMachine {
mCallback = new LoggingCallbackWrapper(callback);
mDependencies = deps;
mShutdownLatch = new CountDownLatch(1);
+ mCm = mContext.getSystemService(ConnectivityManager.class);
mNwService = deps.getNMS();
sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
@@ -479,7 +487,7 @@ public class IpClient extends StateMachine {
// TODO: Consider creating, constructing, and passing in some kind of
// InterfaceController.Dependencies class.
- mInterfaceCtrl = new InterfaceController(mInterfaceName, mNwService, deps.getNetd(), mLog);
+ mInterfaceCtrl = new InterfaceController(mInterfaceName, deps.getNetd(), mLog);
mNetlinkTracker = new NetlinkTracker(
mInterfaceName,
@@ -911,18 +919,18 @@ public class IpClient extends StateMachine {
// object that is a correct and complete assessment of what changed, taking
// account of the asymmetries described in the comments in this function.
// Then switch to using it everywhere (IpReachabilityMonitor, etc.).
- private ProvisioningChange compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
- ProvisioningChange delta;
+ private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
+ int delta;
InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null;
final boolean wasProvisioned = isProvisioned(oldLp, config);
final boolean isProvisioned = isProvisioned(newLp, config);
if (!wasProvisioned && isProvisioned) {
- delta = ProvisioningChange.GAINED_PROVISIONING;
+ delta = PROV_CHANGE_GAINED_PROVISIONING;
} else if (wasProvisioned && isProvisioned) {
- delta = ProvisioningChange.STILL_PROVISIONED;
+ delta = PROV_CHANGE_STILL_PROVISIONED;
} else if (!wasProvisioned && !isProvisioned) {
- delta = ProvisioningChange.STILL_NOT_PROVISIONED;
+ delta = PROV_CHANGE_STILL_NOT_PROVISIONED;
} else {
// (wasProvisioned && !isProvisioned)
//
@@ -934,7 +942,7 @@ public class IpClient extends StateMachine {
// that to be a network without DNS servers and connect anyway.
//
// See the comment below.
- delta = ProvisioningChange.LOST_PROVISIONING;
+ delta = PROV_CHANGE_LOST_PROVISIONING;
}
final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned();
@@ -954,8 +962,9 @@ public class IpClient extends StateMachine {
// Note that we can still be disconnected by IpReachabilityMonitor
// if the IPv6 default gateway (but not the IPv6 DNS servers; see
// accompanying code in IpReachabilityMonitor) is unreachable.
- final boolean ignoreIPv6ProvisioningLoss = (mMultinetworkPolicyTracker != null)
- && !mMultinetworkPolicyTracker.getAvoidBadWifi();
+ final boolean ignoreIPv6ProvisioningLoss =
+ mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
+ && mCm.getAvoidBadWifi();
// Additionally:
//
@@ -970,7 +979,7 @@ public class IpClient extends StateMachine {
// delta will never be LOST_PROVISIONING. So check for loss of
// provisioning here too.
if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
- delta = ProvisioningChange.LOST_PROVISIONING;
+ delta = PROV_CHANGE_LOST_PROVISIONING;
}
// Additionally:
@@ -979,15 +988,15 @@ public class IpClient extends StateMachine {
// IPv6 default route then also consider the loss of that default route
// to be a loss of provisioning. See b/27962810.
if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
- delta = ProvisioningChange.LOST_PROVISIONING;
+ delta = PROV_CHANGE_LOST_PROVISIONING;
}
return delta;
}
- private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
+ private void dispatchCallback(int delta, LinkProperties newLp) {
switch (delta) {
- case GAINED_PROVISIONING:
+ case PROV_CHANGE_GAINED_PROVISIONING:
if (DBG) {
Log.d(mTag, "onProvisioningSuccess()");
}
@@ -995,7 +1004,7 @@ public class IpClient extends StateMachine {
mCallback.onProvisioningSuccess(newLp);
break;
- case LOST_PROVISIONING:
+ case PROV_CHANGE_LOST_PROVISIONING:
if (DBG) {
Log.d(mTag, "onProvisioningFailure()");
}
@@ -1015,7 +1024,7 @@ public class IpClient extends StateMachine {
// Updates all IpClient-related state concerned with LinkProperties.
// Returns a ProvisioningChange for possibly notifying other interested
// parties that are not fronted by IpClient.
- private ProvisioningChange setLinkProperties(LinkProperties newLp) {
+ private int setLinkProperties(LinkProperties newLp) {
if (mApfFilter != null) {
mApfFilter.setLinkProperties(newLp);
}
@@ -1023,10 +1032,10 @@ public class IpClient extends StateMachine {
mIpReachabilityMonitor.updateLinkProperties(newLp);
}
- ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp);
+ int delta = compareProvisioning(mLinkProperties, newLp);
mLinkProperties = new LinkProperties(newLp);
- if (delta == ProvisioningChange.GAINED_PROVISIONING) {
+ if (delta == PROV_CHANGE_GAINED_PROVISIONING) {
// TODO: Add a proper ProvisionedState and cancel the alarm in
// its enter() method.
mProvisioningTimeoutAlarm.cancel();
@@ -1122,17 +1131,17 @@ public class IpClient extends StateMachine {
if (Objects.equals(newLp, mLinkProperties)) {
return true;
}
- final ProvisioningChange delta = setLinkProperties(newLp);
+ final int delta = setLinkProperties(newLp);
if (sendCallbacks) {
dispatchCallback(delta, newLp);
}
- return (delta != ProvisioningChange.LOST_PROVISIONING);
+ return (delta != PROV_CHANGE_LOST_PROVISIONING);
}
private void handleIPv4Success(DhcpResults dhcpResults) {
mDhcpResults = new DhcpResults(dhcpResults);
final LinkProperties newLp = assembleLinkProperties();
- final ProvisioningChange delta = setLinkProperties(newLp);
+ final int delta = setLinkProperties(newLp);
if (DBG) {
Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
@@ -1160,7 +1169,7 @@ public class IpClient extends StateMachine {
private void handleProvisioningFailure() {
final LinkProperties newLp = assembleLinkProperties();
- ProvisioningChange delta = setLinkProperties(newLp);
+ int delta = setLinkProperties(newLp);
// If we've gotten here and we're still not provisioned treat that as
// a total loss of provisioning.
//
@@ -1169,12 +1178,12 @@ public class IpClient extends StateMachine {
// timeout expired.
//
// Regardless: GAME OVER.
- if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
- delta = ProvisioningChange.LOST_PROVISIONING;
+ if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) {
+ delta = PROV_CHANGE_LOST_PROVISIONING;
}
dispatchCallback(delta, newLp);
- if (delta == ProvisioningChange.LOST_PROVISIONING) {
+ if (delta == PROV_CHANGE_LOST_PROVISIONING) {
transitionTo(mStoppingState);
}
}
@@ -1246,7 +1255,7 @@ public class IpClient extends StateMachine {
mCallback.onReachabilityLost(logMsg);
}
},
- mMultinetworkPolicyTracker);
+ mConfiguration.mUsingMultinetworkPolicyTracker);
} catch (IllegalArgumentException iae) {
// Failed to start IpReachabilityMonitor. Log it and call
// onProvisioningFailure() immediately.
@@ -1479,13 +1488,6 @@ public class IpClient extends StateMachine {
return;
}
- if (mConfiguration.mUsingMultinetworkPolicyTracker) {
- mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(
- mContext, getHandler(),
- () -> mLog.log("OBSERVED AvoidBadWifi changed"));
- mMultinetworkPolicyTracker.start();
- }
-
if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
doImmediateProvisioningFailure(
IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
@@ -1503,11 +1505,6 @@ public class IpClient extends StateMachine {
mIpReachabilityMonitor = null;
}
- if (mMultinetworkPolicyTracker != null) {
- mMultinetworkPolicyTracker.shutdown();
- mMultinetworkPolicyTracker = null;
- }
-
if (mDhcpClient != null) {
mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
mDhcpClient.doQuit();
@@ -1646,7 +1643,7 @@ public class IpClient extends StateMachine {
mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
} else {
logError("Failed to set IPv4 address.");
- dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
+ dispatchCallback(PROV_CHANGE_LOST_PROVISIONING,
new LinkProperties(mLinkProperties));
transitionTo(mStoppingState);
}
diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java
index 34bf4b63a883..eb993a4243a9 100644
--- a/services/net/java/android/net/ip/IpNeighborMonitor.java
+++ b/services/net/java/android/net/ip/IpNeighborMonitor.java
@@ -19,6 +19,7 @@ package android.net.ip;
import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
import static android.net.netlink.NetlinkConstants.hexify;
import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
+import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
import android.net.MacAddress;
import android.net.netlink.NetlinkErrorMessage;
@@ -31,7 +32,6 @@ import android.net.util.SharedLog;
import android.os.Handler;
import android.os.SystemClock;
import android.system.ErrnoException;
-import android.system.NetlinkSocketAddress;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
@@ -148,15 +148,12 @@ public class IpNeighborMonitor extends PacketReader {
try {
fd = NetlinkSocket.forProto(OsConstants.NETLINK_ROUTE);
- Os.bind(fd, (SocketAddress)(new NetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH)));
- Os.connect(fd, (SocketAddress)(new NetlinkSocketAddress(0, 0)));
+ Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH));
+ NetlinkSocket.connectToKernel(fd);
if (VDBG) {
- final NetlinkSocketAddress nlAddr = (NetlinkSocketAddress) Os.getsockname(fd);
- Log.d(TAG, "bound to sockaddr_nl{"
- + BitUtils.uint32(nlAddr.getPortId()) + ", "
- + nlAddr.getGroupsMask()
- + "}");
+ final SocketAddress nlAddr = Os.getsockname(fd);
+ Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
}
} catch (ErrnoException|SocketException e) {
logError("Failed to create rtnetlink socket", e);
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 8b02156430f1..761db6822fb4 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -22,15 +22,14 @@ import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST;
import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST_ORGANIC;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.LinkProperties;
-import android.net.LinkProperties.ProvisioningChange;
import android.net.RouteInfo;
import android.net.ip.IpNeighborMonitor.NeighborEvent;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpReachabilityEvent;
import android.net.netlink.StructNdMsg;
import android.net.util.InterfaceParams;
-import android.net.util.MultinetworkPolicyTracker;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.PowerManager;
@@ -166,7 +165,8 @@ public class IpReachabilityMonitor {
private final SharedLog mLog;
private final Callback mCallback;
private final Dependencies mDependencies;
- private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
+ private final boolean mUsingMultinetworkPolicyTracker;
+ private final ConnectivityManager mCm;
private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
private LinkProperties mLinkProperties = new LinkProperties();
private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>();
@@ -175,19 +175,21 @@ public class IpReachabilityMonitor {
public IpReachabilityMonitor(
Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
- MultinetworkPolicyTracker tracker) {
- this(ifParams, h, log, callback, tracker, Dependencies.makeDefault(context, ifParams.name));
+ boolean usingMultinetworkPolicyTracker) {
+ this(context, ifParams, h, log, callback, usingMultinetworkPolicyTracker,
+ Dependencies.makeDefault(context, ifParams.name));
}
@VisibleForTesting
- IpReachabilityMonitor(InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
- MultinetworkPolicyTracker tracker, Dependencies dependencies) {
+ IpReachabilityMonitor(Context context, InterfaceParams ifParams, Handler h, SharedLog log,
+ Callback callback, boolean usingMultinetworkPolicyTracker, Dependencies dependencies) {
if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
mInterfaceParams = ifParams;
mLog = log.forSubComponent(TAG);
mCallback = callback;
- mMultinetworkPolicyTracker = tracker;
+ mUsingMultinetworkPolicyTracker = usingMultinetworkPolicyTracker;
+ mCm = context.getSystemService(ConnectivityManager.class);
mDependencies = dependencies;
mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
@@ -308,10 +310,11 @@ public class IpReachabilityMonitor {
}
}
- final ProvisioningChange delta = LinkProperties.compareProvisioning(
- mLinkProperties, whatIfLp);
+ final boolean lostProvisioning =
+ (mLinkProperties.isIPv4Provisioned() && !whatIfLp.isIPv4Provisioned())
+ || (mLinkProperties.isIPv6Provisioned() && !whatIfLp.isIPv6Provisioned());
- if (delta == ProvisioningChange.LOST_PROVISIONING) {
+ if (lostProvisioning) {
final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
Log.w(TAG, logMsg);
if (mCallback != null) {
@@ -320,11 +323,11 @@ public class IpReachabilityMonitor {
mCallback.notifyLost(ip, logMsg);
}
}
- logNudFailed(delta);
+ logNudFailed(lostProvisioning);
}
private boolean avoidingBadLinks() {
- return (mMultinetworkPolicyTracker == null) || mMultinetworkPolicyTracker.getAvoidBadWifi();
+ return !mUsingMultinetworkPolicyTracker || mCm.getAvoidBadWifi();
}
public void probeAll() {
@@ -370,11 +373,10 @@ public class IpReachabilityMonitor {
mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
}
- private void logNudFailed(ProvisioningChange delta) {
+ private void logNudFailed(boolean lostProvisioning) {
long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs;
boolean isFromProbe = (duration < getProbeWakeLockDuration());
- boolean isProvisioningLost = (delta == ProvisioningChange.LOST_PROVISIONING);
- int eventType = nudFailureEventType(isFromProbe, isProvisioningLost);
+ int eventType = nudFailureEventType(isFromProbe, lostProvisioning);
mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
}
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 8b22f68286af..7910c9a69310 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -226,7 +226,7 @@ public class IpServer extends StateMachine {
mNetd = deps.getNetdService();
mStatsService = statsService;
mCallback = callback;
- mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
+ mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
mLinkProperties = new LinkProperties();
diff --git a/services/net/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java
index 40098c1532b1..2a98d90e5577 100644
--- a/services/net/java/android/net/netlink/NetlinkSocket.java
+++ b/services/net/java/android/net/netlink/NetlinkSocket.java
@@ -16,26 +16,26 @@
package android.net.netlink;
+import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
import static android.system.OsConstants.AF_NETLINK;
import static android.system.OsConstants.EIO;
import static android.system.OsConstants.EPROTO;
import static android.system.OsConstants.ETIMEDOUT;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOL_SOCKET;
import static android.system.OsConstants.SO_RCVBUF;
import static android.system.OsConstants.SO_RCVTIMEO;
import static android.system.OsConstants.SO_SNDTIMEO;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOL_SOCKET;
import android.system.ErrnoException;
-import android.system.NetlinkSocketAddress;
import android.system.Os;
import android.system.StructTimeval;
import android.util.Log;
+
import libcore.io.IoUtils;
import java.io.FileDescriptor;
import java.io.InterruptedIOException;
-import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -106,7 +106,7 @@ public class NetlinkSocket {
}
public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException {
- Os.connect(fd, (SocketAddress) (new NetlinkSocketAddress(0, 0)));
+ Os.connect(fd, makeNetlinkSocketAddress(0, 0));
}
private static void checkTimeout(long timeoutMs) {
diff --git a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
index d5213dfcebf8..51d955d5c2ad 100644
--- a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
+++ b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java
@@ -182,9 +182,6 @@ public final class LinkPropertiesParcelableUtil {
parcel.mtu = lp.getMtu();
parcel.tcpBufferSizes = lp.getTcpBufferSizes();
parcel.nat64Prefix = toStableParcelable(lp.getNat64Prefix());
- parcel.stackedLinks = toParcelableArray(
- lp.getStackedLinks(), LinkPropertiesParcelableUtil::toStableParcelable,
- LinkPropertiesParcelable.class);
return parcel;
}
@@ -216,9 +213,6 @@ public final class LinkPropertiesParcelableUtil {
lp.setMtu(parcel.mtu);
lp.setTcpBufferSizes(parcel.tcpBufferSizes);
lp.setNat64Prefix(fromStableParcelable(parcel.nat64Prefix));
- for (LinkPropertiesParcelable stackedLink : parcel.stackedLinks) {
- lp.addStackedLink(fromStableParcelable(stackedLink));
- }
return lp;
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 1a16e568ca53..53d72bb9a415 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -1376,6 +1376,45 @@ public class DeviceIdleControllerTest {
verifyLightStateConditions(LIGHT_STATE_ACTIVE);
}
+ @Test
+ public void testStepToIdleMode() {
+ float delta = mDeviceIdleController.MIN_PRE_IDLE_FACTOR_CHANGE;
+ for (int mode = PowerManager.PRE_IDLE_TIMEOUT_MODE_NORMAL;
+ mode <= PowerManager.PRE_IDLE_TIMEOUT_MODE_LONG;
+ mode++) {
+ int ret = mDeviceIdleController.setPreIdleTimeoutMode(mode);
+ if (mode == PowerManager.PRE_IDLE_TIMEOUT_MODE_NORMAL) {
+ assertEquals("setPreIdleTimeoutMode: " + mode + " failed.",
+ mDeviceIdleController.SET_IDLE_FACTOR_RESULT_IGNORED, ret);
+ } else {
+ assertEquals("setPreIdleTimeoutMode: " + mode + " failed.",
+ mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK, ret);
+ }
+ //TODO(b/123045185): Mocked Handler of DeviceIdleController to make message loop
+ //workable in this test class
+ mDeviceIdleController.updatePreIdleFactor();
+ float expectedfactor = mDeviceIdleController.getPreIdleTimeoutByMode(mode);
+ float curfactor = mDeviceIdleController.getPreIdleTimeoutFactor();
+ assertEquals("Pre idle time factor of mode [" + mode + "].",
+ expectedfactor, curfactor, delta);
+ mDeviceIdleController.resetPreIdleTimeoutMode();
+ mDeviceIdleController.updatePreIdleFactor();
+
+ checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_INACTIVE);
+ checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_IDLE_PENDING);
+
+ checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_SENSING);
+ checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_LOCATING);
+ checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_QUICK_DOZE_DELAY);
+ checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_IDLE_MAINTENANCE);
+ checkNextAlarmTimeWithNewPreIdleFactor(expectedfactor, STATE_IDLE);
+ checkMaybeDoAnImmediateMaintenance(expectedfactor);
+ }
+ float curfactor = mDeviceIdleController.getPreIdleTimeoutFactor();
+ assertEquals("Pre idle time factor of mode default.",
+ 1.0f, curfactor, delta);
+ }
+
private void enterDeepState(int state) {
switch (state) {
case STATE_ACTIVE:
@@ -1599,4 +1638,84 @@ public class DeviceIdleControllerTest {
fail("Conditions for " + lightStateToString(expectedLightState) + " unknown.");
}
}
+
+ private void checkNextAlarmTimeWithNewPreIdleFactor(float factor, int state) {
+ final long errorTolerance = 1000;
+ enterDeepState(state);
+ long now = SystemClock.elapsedRealtime();
+ long alarm = mDeviceIdleController.getNextAlarmTime();
+ if (state == STATE_INACTIVE || state == STATE_IDLE_PENDING) {
+ int ret = mDeviceIdleController.setPreIdleTimeoutFactor(factor);
+ if (Float.compare(factor, 1.0f) == 0) {
+ assertEquals("setPreIdleTimeoutMode: " + factor + " failed.",
+ mDeviceIdleController.SET_IDLE_FACTOR_RESULT_IGNORED, ret);
+ } else {
+ assertEquals("setPreIdleTimeoutMode: " + factor + " failed.",
+ mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK, ret);
+ }
+ if (ret == mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK) {
+ mDeviceIdleController.updatePreIdleFactor();
+ long newAlarm = mDeviceIdleController.getNextAlarmTime();
+ long newDelay = (long) ((alarm - now) * factor);
+ assertTrue("setPreIdleTimeoutFactor: " + factor,
+ Math.abs(newDelay - (newAlarm - now)) < errorTolerance);
+ mDeviceIdleController.resetPreIdleTimeoutMode();
+ mDeviceIdleController.updatePreIdleFactor();
+ mDeviceIdleController.maybeDoImmediateMaintenance();
+ newAlarm = mDeviceIdleController.getNextAlarmTime();
+ assertTrue("resetPreIdleTimeoutMode from: " + factor,
+ Math.abs(newAlarm - alarm) < errorTolerance);
+ mDeviceIdleController.setPreIdleTimeoutFactor(factor);
+ now = SystemClock.elapsedRealtime();
+ enterDeepState(state);
+ newAlarm = mDeviceIdleController.getNextAlarmTime();
+ assertTrue("setPreIdleTimeoutFactor: " + factor + " before step to idle",
+ Math.abs(newDelay - (newAlarm - now)) < errorTolerance);
+ mDeviceIdleController.resetPreIdleTimeoutMode();
+ mDeviceIdleController.updatePreIdleFactor();
+ mDeviceIdleController.maybeDoImmediateMaintenance();
+ }
+ } else {
+ mDeviceIdleController.setPreIdleTimeoutFactor(factor);
+ mDeviceIdleController.updatePreIdleFactor();
+ long newAlarm = mDeviceIdleController.getNextAlarmTime();
+ assertTrue("setPreIdleTimeoutFactor: " + factor
+ + " shounld not change next alarm" ,
+ (newAlarm == alarm));
+ mDeviceIdleController.resetPreIdleTimeoutMode();
+ mDeviceIdleController.updatePreIdleFactor();
+ mDeviceIdleController.maybeDoImmediateMaintenance();
+ }
+ }
+
+ private void checkMaybeDoAnImmediateMaintenance(float factor) {
+ int ret = mDeviceIdleController.setPreIdleTimeoutFactor(factor);
+ final long minuteInMillis = 60 * 1000;
+ if (Float.compare(factor, 1.0f) == 0) {
+ assertEquals("setPreIdleTimeoutMode: " + factor + " failed.",
+ mDeviceIdleController.SET_IDLE_FACTOR_RESULT_IGNORED, ret);
+ } else {
+ assertEquals("setPreIdleTimeoutMode: " + factor + " failed.",
+ mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK, ret);
+ }
+ if (ret == mDeviceIdleController.SET_IDLE_FACTOR_RESULT_OK) {
+ enterDeepState(STATE_IDLE);
+ long now = SystemClock.elapsedRealtime();
+ long alarm = mDeviceIdleController.getNextAlarmTime();
+ mDeviceIdleController.setIdleStartTimeForTest(
+ now - (long) (mConstants.IDLE_TIMEOUT * 0.6));
+ mDeviceIdleController.maybeDoImmediateMaintenance();
+ long newAlarm = mDeviceIdleController.getNextAlarmTime();
+ assertTrue("maintenance not reschedule IDLE_TIMEOUT * 0.6",
+ newAlarm == alarm);
+ mDeviceIdleController.setIdleStartTimeForTest(
+ now - (long) (mConstants.IDLE_TIMEOUT * 1.2));
+ mDeviceIdleController.maybeDoImmediateMaintenance();
+ newAlarm = mDeviceIdleController.getNextAlarmTime();
+ assertTrue("maintenance not reschedule IDLE_TIMEOUT * 1.2",
+ (newAlarm - now) < minuteInMillis);
+ mDeviceIdleController.resetPreIdleTimeoutMode();
+ mDeviceIdleController.updatePreIdleFactor();
+ }
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 57ee6dcad9f2..cad71a26a76b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -25,6 +25,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RARE_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
@@ -370,16 +371,19 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(0, "com.android.test.stay", one);
ExecutionStats expectedStats = new ExecutionStats();
- expectedStats.invalidTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
- mQuotaController.onAppRemovedLocked("com.android.test.remove", 10001);
+ final int uid = 10001;
+ mQuotaController.onAppRemovedLocked("com.android.test.remove", uid);
assertNull(mQuotaController.getTimingSessions(0, "com.android.test.remove"));
assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test.stay"));
assertEquals(expectedStats,
mQuotaController.getExecutionStatsLocked(0, "com.android.test.remove", RARE_INDEX));
assertNotEquals(expectedStats,
mQuotaController.getExecutionStatsLocked(0, "com.android.test.stay", RARE_INDEX));
+
+ assertFalse(mQuotaController.getForegroundUids().get(uid));
}
@Test
@@ -405,7 +409,7 @@ public class QuotaControllerTest {
mQuotaController.saveTimingSession(10, "com.android.test", one);
ExecutionStats expectedStats = new ExecutionStats();
- expectedStats.invalidTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
mQuotaController.onUserRemovedLocked(0);
@@ -440,14 +444,14 @@ public class QuotaControllerTest {
inputStats.windowSizeMs = expectedStats.windowSizeMs = 12 * HOUR_IN_MILLIS;
// Invalid time is now +24 hours since there are no sessions at all for the app.
- expectedStats.invalidTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats);
assertEquals(expectedStats, inputStats);
inputStats.windowSizeMs = expectedStats.windowSizeMs = MINUTE_IN_MILLIS;
// Invalid time is now +18 hours since there are no sessions in the window but the earliest
// session is 6 hours ago.
- expectedStats.invalidTimeElapsed = now + 18 * HOUR_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 18 * HOUR_IN_MILLIS;
expectedStats.executionTimeInWindowMs = 0;
expectedStats.bgJobCountInWindow = 0;
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -457,7 +461,7 @@ public class QuotaControllerTest {
inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * MINUTE_IN_MILLIS;
// Invalid time is now since the session straddles the window cutoff time.
- expectedStats.invalidTimeElapsed = now;
+ expectedStats.expirationTimeElapsed = now;
expectedStats.executionTimeInWindowMs = 2 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 3;
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -468,7 +472,7 @@ public class QuotaControllerTest {
inputStats.windowSizeMs = expectedStats.windowSizeMs = 5 * MINUTE_IN_MILLIS;
// Invalid time is now since the start of the session is at the very edge of the window
// cutoff time.
- expectedStats.invalidTimeElapsed = now;
+ expectedStats.expirationTimeElapsed = now;
expectedStats.executionTimeInWindowMs = 4 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 3;
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -479,7 +483,7 @@ public class QuotaControllerTest {
inputStats.windowSizeMs = expectedStats.windowSizeMs = 49 * MINUTE_IN_MILLIS;
// Invalid time is now +44 minutes since the earliest session in the window is now-5
// minutes.
- expectedStats.invalidTimeElapsed = now + 44 * MINUTE_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 44 * MINUTE_IN_MILLIS;
expectedStats.executionTimeInWindowMs = 4 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 3;
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -489,7 +493,7 @@ public class QuotaControllerTest {
inputStats.windowSizeMs = expectedStats.windowSizeMs = 50 * MINUTE_IN_MILLIS;
// Invalid time is now since the session is at the very edge of the window cutoff time.
- expectedStats.invalidTimeElapsed = now;
+ expectedStats.expirationTimeElapsed = now;
expectedStats.executionTimeInWindowMs = 5 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 4;
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -500,7 +504,7 @@ public class QuotaControllerTest {
inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS;
// Invalid time is now since the start of the session is at the very edge of the window
// cutoff time.
- expectedStats.invalidTimeElapsed = now;
+ expectedStats.expirationTimeElapsed = now;
expectedStats.executionTimeInWindowMs = 6 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 5;
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -510,7 +514,7 @@ public class QuotaControllerTest {
inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
// Invalid time is now since the session straddles the window cutoff time.
- expectedStats.invalidTimeElapsed = now;
+ expectedStats.expirationTimeElapsed = now;
expectedStats.executionTimeInWindowMs = 11 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 10;
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -523,7 +527,7 @@ public class QuotaControllerTest {
inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * HOUR_IN_MILLIS;
// Invalid time is now +59 minutes since the earliest session in the window is now-121
// minutes.
- expectedStats.invalidTimeElapsed = now + 59 * MINUTE_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 59 * MINUTE_IN_MILLIS;
expectedStats.executionTimeInWindowMs = 12 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 10;
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -536,7 +540,7 @@ public class QuotaControllerTest {
inputStats.windowSizeMs = expectedStats.windowSizeMs = 6 * HOUR_IN_MILLIS;
// Invalid time is now since the start of the session is at the very edge of the window
// cutoff time.
- expectedStats.invalidTimeElapsed = now;
+ expectedStats.expirationTimeElapsed = now;
expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 15;
expectedStats.executionTimeInMaxPeriodMs = 22 * MINUTE_IN_MILLIS;
@@ -546,14 +550,14 @@ public class QuotaControllerTest {
mQuotaController.updateExecutionStatsLocked(0, "com.android.test", inputStats);
assertEquals(expectedStats, inputStats);
- // Make sure invalidTimeElapsed is set correctly when it's dependent on the max period.
+ // Make sure expirationTimeElapsed is set correctly when it's dependent on the max period.
mQuotaController.getTimingSessions(0, "com.android.test")
.add(0,
createTimingSession(now - (23 * HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 3));
inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
// Invalid time is now +1 hour since the earliest session in the max period is 1 hour
// before the end of the max period cutoff time.
- expectedStats.invalidTimeElapsed = now + HOUR_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 15;
expectedStats.executionTimeInMaxPeriodMs = 23 * MINUTE_IN_MILLIS;
@@ -569,7 +573,7 @@ public class QuotaControllerTest {
2 * MINUTE_IN_MILLIS, 2));
inputStats.windowSizeMs = expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
// Invalid time is now since the earlist session straddles the max period cutoff time.
- expectedStats.invalidTimeElapsed = now;
+ expectedStats.expirationTimeElapsed = now;
expectedStats.executionTimeInWindowMs = 22 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 15;
expectedStats.executionTimeInMaxPeriodMs = 24 * MINUTE_IN_MILLIS;
@@ -599,7 +603,7 @@ public class QuotaControllerTest {
// Active
expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
- expectedStats.invalidTimeElapsed = now + 4 * MINUTE_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + 4 * MINUTE_IN_MILLIS;
expectedStats.executionTimeInWindowMs = 3 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 5;
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
@@ -609,7 +613,7 @@ public class QuotaControllerTest {
// Working
expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
- expectedStats.invalidTimeElapsed = now;
+ expectedStats.expirationTimeElapsed = now;
expectedStats.executionTimeInWindowMs = 13 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 10;
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
@@ -621,7 +625,7 @@ public class QuotaControllerTest {
// Frequent
expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
- expectedStats.invalidTimeElapsed = now + HOUR_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
expectedStats.executionTimeInWindowMs = 23 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 15;
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
@@ -633,7 +637,7 @@ public class QuotaControllerTest {
// Rare
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
- expectedStats.invalidTimeElapsed = now + HOUR_IN_MILLIS;
+ expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
expectedStats.executionTimeInWindowMs = 33 * MINUTE_IN_MILLIS;
expectedStats.bgJobCountInWindow = 20;
expectedStats.executionTimeInMaxPeriodMs = 33 * MINUTE_IN_MILLIS;
@@ -675,7 +679,7 @@ public class QuotaControllerTest {
ExecutionStats expectedStats = new ExecutionStats();
expectedStats.windowSizeMs = originalStatsActive.windowSizeMs;
- expectedStats.invalidTimeElapsed = originalStatsActive.invalidTimeElapsed;
+ expectedStats.expirationTimeElapsed = originalStatsActive.expirationTimeElapsed;
expectedStats.executionTimeInWindowMs = originalStatsActive.executionTimeInWindowMs;
expectedStats.bgJobCountInWindow = originalStatsActive.bgJobCountInWindow;
expectedStats.executionTimeInMaxPeriodMs = originalStatsActive.executionTimeInMaxPeriodMs;
@@ -688,7 +692,7 @@ public class QuotaControllerTest {
assertEquals(expectedStats, newStatsActive);
expectedStats.windowSizeMs = originalStatsWorking.windowSizeMs;
- expectedStats.invalidTimeElapsed = originalStatsWorking.invalidTimeElapsed;
+ expectedStats.expirationTimeElapsed = originalStatsWorking.expirationTimeElapsed;
expectedStats.executionTimeInWindowMs = originalStatsWorking.executionTimeInWindowMs;
expectedStats.bgJobCountInWindow = originalStatsWorking.bgJobCountInWindow;
expectedStats.quotaCutoffTimeElapsed = originalStatsWorking.quotaCutoffTimeElapsed;
@@ -698,7 +702,7 @@ public class QuotaControllerTest {
assertNotEquals(expectedStats, newStatsWorking);
expectedStats.windowSizeMs = originalStatsFrequent.windowSizeMs;
- expectedStats.invalidTimeElapsed = originalStatsFrequent.invalidTimeElapsed;
+ expectedStats.expirationTimeElapsed = originalStatsFrequent.expirationTimeElapsed;
expectedStats.executionTimeInWindowMs = originalStatsFrequent.executionTimeInWindowMs;
expectedStats.bgJobCountInWindow = originalStatsFrequent.bgJobCountInWindow;
expectedStats.quotaCutoffTimeElapsed = originalStatsFrequent.quotaCutoffTimeElapsed;
@@ -708,7 +712,7 @@ public class QuotaControllerTest {
assertNotEquals(expectedStats, newStatsFrequent);
expectedStats.windowSizeMs = originalStatsRare.windowSizeMs;
- expectedStats.invalidTimeElapsed = originalStatsRare.invalidTimeElapsed;
+ expectedStats.expirationTimeElapsed = originalStatsRare.expirationTimeElapsed;
expectedStats.executionTimeInWindowMs = originalStatsRare.executionTimeInWindowMs;
expectedStats.bgJobCountInWindow = originalStatsRare.bgJobCountInWindow;
expectedStats.quotaCutoffTimeElapsed = originalStatsRare.quotaCutoffTimeElapsed;
@@ -719,6 +723,77 @@ public class QuotaControllerTest {
}
@Test
+ public void testIsWithinQuotaLocked_NeverApp() {
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX));
+ }
+
+ @Test
+ public void testIsWithinQuotaLocked_Charging() {
+ setCharging();
+ assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
+ }
+
+ @Test
+ public void testIsWithinQuotaLocked_UnderDuration_UnderJobCount() {
+ setDischarging();
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.incrementJobCount(0, "com.android.test", 5);
+ assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ }
+
+ @Test
+ public void testIsWithinQuotaLocked_UnderDuration_OverJobCount() {
+ setDischarging();
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final int jobCount = mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
+ mQuotaController.saveTimingSession(0, "com.android.test.spam",
+ createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
+ mQuotaController.saveTimingSession(0, "com.android.test.spam",
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount));
+ mQuotaController.incrementJobCount(0, "com.android.test.spam", jobCount);
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.spam",
+ WORKING_INDEX));
+
+ mQuotaController.saveTimingSession(0, "com.android.test.frequent",
+ createTimingSession(now - (2 * HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 2000));
+ mQuotaController.saveTimingSession(0, "com.android.test.frequent",
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500));
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test.frequent",
+ FREQUENT_INDEX));
+ }
+
+ @Test
+ public void testIsWithinQuotaLocked_OverDuration_UnderJobCount() {
+ setDischarging();
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (30 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5));
+ mQuotaController.incrementJobCount(0, "com.android.test", 5);
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ }
+
+ @Test
+ public void testIsWithinQuotaLocked_OverDuration_OverJobCount() {
+ setDischarging();
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final int jobCount = mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (HOUR_IN_MILLIS), 15 * MINUTE_IN_MILLIS, 25));
+ mQuotaController.saveTimingSession(0, "com.android.test",
+ createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, jobCount));
+ mQuotaController.incrementJobCount(0, "com.android.test", jobCount);
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
+ }
+
+ @Test
public void testMaybeScheduleCleanupAlarmLocked() {
// No sessions saved yet.
mQuotaController.maybeScheduleCleanupAlarmLocked();
@@ -752,6 +827,7 @@ public class QuotaControllerTest {
// Active window size is 10 minutes.
final int standbyBucket = ACTIVE_INDEX;
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
// No sessions saved yet.
mQuotaController.maybeScheduleStartAlarmLocked(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1016,11 +1092,37 @@ public class QuotaControllerTest {
.set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", ACTIVE_INDEX);
- inOrder.verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
- any());
+ inOrder.verify(mAlarmManager, never())
+ .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
inOrder.verify(mAlarmManager, times(1)).cancel(any(AlarmManager.OnAlarmListener.class));
}
+ @Test
+ public void testMaybeScheduleStartAlarmLocked_JobCount_AllowedTime() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final int standbyBucket = WORKING_INDEX;
+ ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
+ SOURCE_PACKAGE, standbyBucket);
+ stats.jobCountInAllowedTime =
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME + 2;
+
+ // Invalid time in the past, so the count shouldn't be used.
+ stats.jobCountExpirationTimeElapsed =
+ now - mQuotaController.getAllowedTimePerPeriodMs() / 2;
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+ // Invalid time in the future, so the count should be used.
+ stats.jobCountExpirationTimeElapsed =
+ now + mQuotaController.getAllowedTimePerPeriodMs() / 2;
+ final long expectedWorkingAlarmTime =
+ stats.jobCountExpirationTimeElapsed + mQuotaController.getAllowedTimePerPeriodMs();
+ mQuotaController.maybeScheduleStartAlarmLocked(
+ SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ verify(mAlarmManager, times(1))
+ .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+ }
/**
* Tests that the start alarm is properly rescheduled if the earliest session that contributes
@@ -1172,6 +1274,11 @@ public class QuotaControllerTest {
mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = 45 * MINUTE_IN_MILLIS;
mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = 60 * MINUTE_IN_MILLIS;
mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = 3 * HOUR_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = 5000;
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = 4000;
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = 3000;
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = 2000;
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 500;
mQuotaController.onConstantsUpdatedLocked();
@@ -1183,11 +1290,16 @@ public class QuotaControllerTest {
mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
+ assertEquals(500, mQuotaController.getMaxJobCountPerAllowedTime());
+ assertEquals(5000, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
+ assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
+ assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
+ assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
}
@Test
public void testConstantsUpdating_InvalidValues() {
- // Test negatives
+ // Test negatives/too low.
mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = -MINUTE_IN_MILLIS;
mConstants.QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = -MINUTE_IN_MILLIS;
mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = -MINUTE_IN_MILLIS;
@@ -1195,6 +1307,11 @@ public class QuotaControllerTest {
mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = -MINUTE_IN_MILLIS;
mConstants.QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = -MINUTE_IN_MILLIS;
mConstants.QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = -MINUTE_IN_MILLIS;
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = -1;
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = 1;
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = 1;
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = 1;
+ mConstants.QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 0;
mQuotaController.onConstantsUpdatedLocked();
@@ -1205,6 +1322,11 @@ public class QuotaControllerTest {
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[RARE_INDEX]);
assertEquals(HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
+ assertEquals(10, mQuotaController.getMaxJobCountPerAllowedTime());
+ assertEquals(100, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
+ assertEquals(100, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
+ assertEquals(100, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
+ assertEquals(100, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
// Test larger than a day. Controller should cap at one day.
mConstants.QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = 25 * HOUR_IN_MILLIS;
@@ -1246,6 +1368,7 @@ public class QuotaControllerTest {
@Test
public void testTimerTracking_Discharging() {
setDischarging();
+ setProcessState(ActivityManager.PROCESS_STATE_BACKUP);
JobStatus jobStatus = createJobStatus("testTimerTracking_Discharging", 1);
mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
@@ -1293,6 +1416,8 @@ public class QuotaControllerTest {
*/
@Test
public void testTimerTracking_ChargingAndDischarging() {
+ setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+
JobStatus jobStatus = createJobStatus("testTimerTracking_ChargingAndDischarging", 1);
mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
JobStatus jobStatus2 = createJobStatus("testTimerTracking_ChargingAndDischarging", 2);
@@ -1363,6 +1488,7 @@ public class QuotaControllerTest {
@Test
public void testTimerTracking_AllBackground() {
setDischarging();
+ setProcessState(ActivityManager.PROCESS_STATE_RECEIVER);
JobStatus jobStatus = createJobStatus("testTimerTracking_AllBackground", 1);
mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
@@ -1503,6 +1629,64 @@ public class QuotaControllerTest {
}
/**
+ * Tests that Timers don't track job counts while in the foreground.
+ */
+ @Test
+ public void testTimerTracking_JobCount_Foreground() {
+ setDischarging();
+
+ final int standbyBucket = ACTIVE_INDEX;
+ JobStatus jobFg1 = createJobStatus("testTimerTracking_JobCount_Foreground", 1);
+ JobStatus jobFg2 = createJobStatus("testTimerTracking_JobCount_Foreground", 2);
+
+ mQuotaController.maybeStartTrackingJobLocked(jobFg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobFg2, null);
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+ ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
+ SOURCE_PACKAGE, standbyBucket);
+ assertEquals(0, stats.jobCountInAllowedTime);
+
+ setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ mQuotaController.prepareForExecutionLocked(jobFg1);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.prepareForExecutionLocked(jobFg2);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobFg1, null, false);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobFg2, null, false);
+ assertNull(mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE));
+
+ assertEquals(0, stats.jobCountInAllowedTime);
+ }
+
+ /**
+ * Tests that Timers properly track job counts while in the background.
+ */
+ @Test
+ public void testTimerTracking_JobCount_Background() {
+ final int standbyBucket = WORKING_INDEX;
+ JobStatus jobBg1 = createJobStatus("testTimerTracking_JobCount_Background", 1);
+ JobStatus jobBg2 = createJobStatus("testTimerTracking_JobCount_Background", 2);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg1, null);
+ mQuotaController.maybeStartTrackingJobLocked(jobBg2, null);
+
+ ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
+ SOURCE_PACKAGE, standbyBucket);
+ assertEquals(0, stats.jobCountInAllowedTime);
+
+ setProcessState(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+ mQuotaController.prepareForExecutionLocked(jobBg1);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.prepareForExecutionLocked(jobBg2);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobBg1, null, false);
+ advanceElapsedClock(10 * SECOND_IN_MILLIS);
+ mQuotaController.maybeStopTrackingJobLocked(jobBg2, null, false);
+
+ assertEquals(2, stats.jobCountInAllowedTime);
+ }
+
+ /**
* Tests that Timers properly track overlapping top and background jobs.
*/
@Test
@@ -1680,6 +1864,7 @@ public class QuotaControllerTest {
JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1);
mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
+ setProcessState(ActivityManager.PROCESS_STATE_HOME);
// Now the package only has two seconds to run.
final long remainingTimeMs = 2 * SECOND_IN_MILLIS;
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1707,6 +1892,7 @@ public class QuotaControllerTest {
JobStatus jobStatus = createJobStatus("testTracking_OutOfQuota", 1);
mQuotaController.maybeStartTrackingJobLocked(jobStatus, null);
setStandbyBucket(WORKING_INDEX, jobStatus); // 2 hour window
+ setProcessState(ActivityManager.PROCESS_STATE_SERVICE);
Handler handler = mQuotaController.getHandler();
spyOn(handler);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index feffeef3f044..773b8778c7bf 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -20,7 +20,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
@@ -36,19 +38,15 @@ import android.animation.ValueAnimator;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
-import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.view.MagnificationSpec;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.R;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
@@ -81,48 +79,36 @@ public class MagnificationControllerTest {
static final Region OTHER_REGION = new Region(OTHER_MAGNIFICATION_BOUNDS);
static final int SERVICE_ID_1 = 1;
static final int SERVICE_ID_2 = 2;
+ static final int DISPLAY_0 = 0;
+ static final int DISPLAY_1 = 1;
+ static final int DISPLAY_COUNT = 2;
+ static final int INVALID_DISPLAY = 2;
+ final MagnificationController.ControllerContext mMockControllerCtx =
+ mock(MagnificationController.ControllerContext.class);
final Context mMockContext = mock(Context.class);
final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
- final MessageCapturingHandler mMessageCapturingHandler =
- new MessageCapturingHandler(null);
+ final MessageCapturingHandler mMessageCapturingHandler = new MessageCapturingHandler(null);
- final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class);
- MagnificationController.SettingsBridge mMockSettingsBridge;
+ ValueAnimator mMockValueAnimator;
+ ValueAnimator.AnimatorUpdateListener mTargetAnimationListener;
MagnificationController mMagnificationController;
- ValueAnimator.AnimatorUpdateListener mTargetAnimationListener;
@Before
public void setUp() {
Looper looper = InstrumentationRegistry.getContext().getMainLooper();
// Pretending ID of the Thread associated with looper as main thread ID in controller
when(mMockContext.getMainLooper()).thenReturn(looper);
- Resources mockResources = mock(Resources.class);
- when(mMockContext.getResources()).thenReturn(mockResources);
- when(mockResources.getInteger(R.integer.config_longAnimTime))
- .thenReturn(1000);
- mMockSettingsBridge = mock(MagnificationController.SettingsBridge.class);
- mMagnificationController = new MagnificationController(mMockContext, mMockAms, new Object(),
- mMessageCapturingHandler, mMockWindowManager, mMockValueAnimator,
- mMockSettingsBridge);
-
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
- Object[] args = invocationOnMock.getArguments();
- Region regionArg = (Region) args[0];
- regionArg.set(INITIAL_MAGNIFICATION_REGION);
- return null;
- }
- }).when(mMockWindowManager).getMagnificationRegion((Region) anyObject());
-
- ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> listenerArgumentCaptor =
- ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class);
- verify(mMockValueAnimator).addUpdateListener(listenerArgumentCaptor.capture());
- mTargetAnimationListener = listenerArgumentCaptor.getValue();
- Mockito.reset(mMockValueAnimator); // Ignore other initialization
+ when(mMockControllerCtx.getContext()).thenReturn(mMockContext);
+ when(mMockControllerCtx.getAms()).thenReturn(mMockAms);
+ when(mMockControllerCtx.getWindowManager()).thenReturn(mMockWindowManager);
+ when(mMockControllerCtx.getHandler()).thenReturn(mMessageCapturingHandler);
+ when(mMockControllerCtx.getAnimationDuration()).thenReturn(1000L);
+ initMockWindowManager();
+
+ mMagnificationController = new MagnificationController(mMockControllerCtx, new Object());
}
@After
@@ -133,88 +119,132 @@ public class MagnificationControllerTest {
@Test
public void testRegister_WindowManagerAndContextRegisterListeners() {
- mMagnificationController.register();
+ register(DISPLAY_0);
+ register(DISPLAY_1);
+ register(INVALID_DISPLAY);
verify(mMockContext).registerReceiver(
(BroadcastReceiver) anyObject(), (IntentFilter) anyObject());
- verify(mMockWindowManager).setMagnificationCallbacks((MagnificationCallbacks) anyObject());
- assertTrue(mMagnificationController.isRegisteredLocked());
+ verify(mMockWindowManager).setMagnificationCallbacks(
+ eq(DISPLAY_0), (MagnificationCallbacks) anyObject());
+ verify(mMockWindowManager).setMagnificationCallbacks(
+ eq(DISPLAY_1), (MagnificationCallbacks) anyObject());
+ verify(mMockWindowManager).setMagnificationCallbacks(
+ eq(INVALID_DISPLAY), (MagnificationCallbacks) anyObject());
+ assertTrue(mMagnificationController.isRegistered(DISPLAY_0));
+ assertTrue(mMagnificationController.isRegistered(DISPLAY_1));
+ assertFalse(mMagnificationController.isRegistered(INVALID_DISPLAY));
}
@Test
public void testRegister_WindowManagerAndContextUnregisterListeners() {
- mMagnificationController.register();
- mMagnificationController.unregister();
-
+ register(DISPLAY_0);
+ register(DISPLAY_1);
+ mMagnificationController.unregister(DISPLAY_0);
+ verify(mMockContext, times(0)).unregisterReceiver((BroadcastReceiver) anyObject());
+ mMagnificationController.unregister(DISPLAY_1);
verify(mMockContext).unregisterReceiver((BroadcastReceiver) anyObject());
- verify(mMockWindowManager).setMagnificationCallbacks(null);
- assertFalse(mMagnificationController.isRegisteredLocked());
+ verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_0), eq(null));
+ verify(mMockWindowManager).setMagnificationCallbacks(eq(DISPLAY_1), eq(null));
+ assertFalse(mMagnificationController.isRegistered(DISPLAY_0));
+ assertFalse(mMagnificationController.isRegistered(DISPLAY_1));
}
@Test
public void testInitialState_noMagnificationAndMagnificationRegionReadFromWindowManager() {
- mMagnificationController.register();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ initialState_noMagnificationAndMagnificationRegionReadFromWindowManager(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void initialState_noMagnificationAndMagnificationRegionReadFromWindowManager(
+ int displayId) {
+ register(displayId);
MagnificationSpec expectedInitialSpec = getMagnificationSpec(1.0f, 0.0f, 0.0f);
Region initialMagRegion = new Region();
Rect initialBounds = new Rect();
- assertEquals(expectedInitialSpec, getCurrentMagnificationSpec());
- mMagnificationController.getMagnificationRegion(initialMagRegion);
- mMagnificationController.getMagnificationBounds(initialBounds);
+ assertEquals(expectedInitialSpec, getCurrentMagnificationSpec(displayId));
+ mMagnificationController.getMagnificationRegion(displayId, initialMagRegion);
+ mMagnificationController.getMagnificationBounds(displayId, initialBounds);
assertEquals(INITIAL_MAGNIFICATION_REGION, initialMagRegion);
assertEquals(INITIAL_MAGNIFICATION_BOUNDS, initialBounds);
assertEquals(INITIAL_MAGNIFICATION_BOUNDS.centerX(),
- mMagnificationController.getCenterX(), 0.0f);
+ mMagnificationController.getCenterX(displayId), 0.0f);
assertEquals(INITIAL_MAGNIFICATION_BOUNDS.centerY(),
- mMagnificationController.getCenterY(), 0.0f);
+ mMagnificationController.getCenterY(displayId), 0.0f);
}
@Test
public void testNotRegistered_publicMethodsShouldBeBenign() {
- assertFalse(mMagnificationController.isMagnifying());
- assertFalse(mMagnificationController.magnificationRegionContains(100, 100));
- assertFalse(mMagnificationController.reset(true));
- assertFalse(mMagnificationController.setScale(2, 100, 100, true, 0));
- assertFalse(mMagnificationController.setCenter(100, 100, false, 1));
- assertFalse(mMagnificationController.setScaleAndCenter(1.5f, 100, 100, false, 2));
- assertTrue(mMagnificationController.getIdOfLastServiceToMagnify() < 0);
-
- mMagnificationController.getMagnificationRegion(new Region());
- mMagnificationController.getMagnificationBounds(new Rect());
- mMagnificationController.getScale();
- mMagnificationController.getOffsetX();
- mMagnificationController.getOffsetY();
- mMagnificationController.getCenterX();
- mMagnificationController.getCenterY();
- mMagnificationController.offsetMagnifiedRegion(50, 50, 1);
- mMagnificationController.unregister();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ notRegistered_publicMethodsShouldBeBenign(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void notRegistered_publicMethodsShouldBeBenign(int displayId) {
+ assertFalse(mMagnificationController.isMagnifying(displayId));
+ assertFalse(mMagnificationController.magnificationRegionContains(displayId, 100, 100));
+ assertFalse(mMagnificationController.reset(displayId, true));
+ assertFalse(mMagnificationController.setScale(displayId, 2, 100, 100, true, 0));
+ assertFalse(mMagnificationController.setCenter(displayId, 100, 100, false, 1));
+ assertFalse(mMagnificationController.setScaleAndCenter(displayId,
+ 1.5f, 100, 100, false, 2));
+ assertTrue(mMagnificationController.getIdOfLastServiceToMagnify(displayId) < 0);
+
+ mMagnificationController.getMagnificationRegion(displayId, new Region());
+ mMagnificationController.getMagnificationBounds(displayId, new Rect());
+ mMagnificationController.getScale(displayId);
+ mMagnificationController.getOffsetX(displayId);
+ mMagnificationController.getOffsetY(displayId);
+ mMagnificationController.getCenterX(displayId);
+ mMagnificationController.getCenterY(displayId);
+ mMagnificationController.offsetMagnifiedRegion(displayId, 50, 50, 1);
+ mMagnificationController.unregister(displayId);
}
@Test
public void testSetScale_noAnimation_shouldGoStraightToWindowManagerAndUpdateState() {
- mMagnificationController.register();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ setScale_noAnimation_shouldGoStraightToWindowManagerAndUpdateState(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void setScale_noAnimation_shouldGoStraightToWindowManagerAndUpdateState(int displayId) {
+ register(displayId);
final float scale = 2.0f;
final PointF center = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
final PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, center, scale);
assertTrue(mMagnificationController
- .setScale(scale, center.x, center.y, false, SERVICE_ID_1));
+ .setScale(displayId, scale, center.x, center.y, false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
final MagnificationSpec expectedSpec = getMagnificationSpec(scale, offsets);
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec)));
- assertThat(getCurrentMagnificationSpec(), closeTo(expectedSpec));
- assertEquals(center.x, mMagnificationController.getCenterX(), 0.0);
- assertEquals(center.y, mMagnificationController.getCenterY(), 0.0);
+ verify(mMockWindowManager).setMagnificationSpec(
+ eq(displayId), argThat(closeTo(expectedSpec)));
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedSpec));
+ assertEquals(center.x, mMagnificationController.getCenterX(displayId), 0.0);
+ assertEquals(center.y, mMagnificationController.getCenterY(displayId), 0.0);
verify(mMockValueAnimator, times(0)).start();
}
@Test
public void testSetScale_withPivotAndAnimation_stateChangesAndAnimationHappens() {
- mMagnificationController.register();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ setScale_withPivotAndAnimation_stateChangesAndAnimationHappens(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void setScale_withPivotAndAnimation_stateChangesAndAnimationHappens(int displayId) {
+ register(displayId);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
float scale = 2.0f;
PointF pivotPoint = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
assertTrue(mMagnificationController
- .setScale(scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1));
+ .setScale(displayId, scale, pivotPoint.x, pivotPoint.y, true, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
// New center should be halfway between original center and pivot
@@ -223,467 +253,645 @@ public class MagnificationControllerTest {
PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
- assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
- assertThat(getCurrentMagnificationSpec(), closeTo(endSpec));
+ assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
verify(mMockValueAnimator).start();
// Initial value
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- verify(mMockWindowManager).setMagnificationSpec(startSpec);
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
// Intermediate point
Mockito.reset(mMockWindowManager);
float fraction = 0.5f;
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- verify(mMockWindowManager).setMagnificationSpec(
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
argThat(closeTo(getInterpolatedMagSpec(startSpec, endSpec, fraction))));
// Final value
Mockito.reset(mMockWindowManager);
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec)));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
}
@Test
public void testSetCenter_whileMagnifying_noAnimation_centerMoves() {
- mMagnificationController.register();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ setCenter_whileMagnifying_noAnimation_centerMoves(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void setCenter_whileMagnifying_noAnimation_centerMoves(int displayId) {
+ register(displayId);
// First zoom in
float scale = 2.0f;
- assertTrue(mMagnificationController.setScale(scale,
+ assertTrue(mMagnificationController.setScale(displayId, scale,
INITIAL_MAGNIFICATION_BOUNDS.centerX(), INITIAL_MAGNIFICATION_BOUNDS.centerY(),
false, SERVICE_ID_1));
Mockito.reset(mMockWindowManager);
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
assertTrue(mMagnificationController
- .setCenter(newCenter.x, newCenter.y, false, SERVICE_ID_1));
+ .setCenter(displayId, newCenter.x, newCenter.y, false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
MagnificationSpec expectedSpec = getMagnificationSpec(scale, expectedOffsets);
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec)));
- assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.0);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.0);
+ verify(mMockWindowManager).setMagnificationSpec(
+ eq(displayId), argThat(closeTo(expectedSpec)));
+ assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.0);
+ assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.0);
verify(mMockValueAnimator, times(0)).start();
}
@Test
public void testSetScaleAndCenter_animated_stateChangesAndAnimationHappens() {
- mMagnificationController.register();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ setScaleAndCenter_animated_stateChangesAndAnimationHappens(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void setScaleAndCenter_animated_stateChangesAndAnimationHappens(int displayId) {
+ register(displayId);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
float scale = 2.5f;
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
MagnificationSpec endSpec = getMagnificationSpec(scale, offsets);
- assertTrue(mMagnificationController.setScaleAndCenter(scale, newCenter.x, newCenter.y,
- true, SERVICE_ID_1));
+ assertTrue(mMagnificationController.setScaleAndCenter(displayId, scale, newCenter.x,
+ newCenter.y, true, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
- assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
- assertThat(getCurrentMagnificationSpec(), closeTo(endSpec));
- verify(mMockAms).notifyMagnificationChanged(
+ assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(endSpec));
+ verify(mMockAms).notifyMagnificationChanged(displayId,
INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
verify(mMockValueAnimator).start();
// Initial value
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- verify(mMockWindowManager).setMagnificationSpec(startSpec);
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
// Intermediate point
Mockito.reset(mMockWindowManager);
float fraction = 0.33f;
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- verify(mMockWindowManager).setMagnificationSpec(
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
argThat(closeTo(getInterpolatedMagSpec(startSpec, endSpec, fraction))));
// Final value
Mockito.reset(mMockWindowManager);
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec)));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
}
@Test
public void testSetScaleAndCenter_scaleOutOfBounds_cappedAtLimits() {
- mMagnificationController.register();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ setScaleAndCenter_scaleOutOfBounds_cappedAtLimits(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void setScaleAndCenter_scaleOutOfBounds_cappedAtLimits(int displayId) {
+ register(displayId);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
PointF offsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter,
MagnificationController.MAX_SCALE);
MagnificationSpec endSpec = getMagnificationSpec(
MagnificationController.MAX_SCALE, offsets);
- assertTrue(mMagnificationController.setScaleAndCenter(
+ assertTrue(mMagnificationController.setScaleAndCenter(displayId,
MagnificationController.MAX_SCALE + 1.0f,
newCenter.x, newCenter.y, false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
- assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec)));
+ assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
Mockito.reset(mMockWindowManager);
// Verify that we can't zoom below 1x
- assertTrue(mMagnificationController.setScaleAndCenter(0.5f,
+ assertTrue(mMagnificationController.setScaleAndCenter(displayId, 0.5f,
INITIAL_MAGNIFICATION_BOUNDS_CENTER.x, INITIAL_MAGNIFICATION_BOUNDS_CENTER.y,
false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.x,
- mMagnificationController.getCenterX(), 0.5);
+ mMagnificationController.getCenterX(displayId), 0.5);
assertEquals(INITIAL_MAGNIFICATION_BOUNDS_CENTER.y,
- mMagnificationController.getCenterY(), 0.5);
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(startSpec)));
+ mMagnificationController.getCenterY(displayId), 0.5);
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec)));
}
@Test
public void testSetScaleAndCenter_centerOutOfBounds_cappedAtLimits() {
- mMagnificationController.register();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ setScaleAndCenter_centerOutOfBounds_cappedAtLimits(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void setScaleAndCenter_centerOutOfBounds_cappedAtLimits(int displayId) {
+ register(displayId);
float scale = 2.0f;
// Off the edge to the top and left
- assertTrue(mMagnificationController.setScaleAndCenter(
+ assertTrue(mMagnificationController.setScaleAndCenter(displayId,
scale, -100f, -200f, false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
- assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
- verify(mMockWindowManager).setMagnificationSpec(
+ assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
argThat(closeTo(getMagnificationSpec(scale, newOffsets))));
Mockito.reset(mMockWindowManager);
// Off the edge to the bottom and right
- assertTrue(mMagnificationController.setScaleAndCenter(scale,
+ assertTrue(mMagnificationController.setScaleAndCenter(displayId, scale,
INITIAL_MAGNIFICATION_BOUNDS.right + 1, INITIAL_MAGNIFICATION_BOUNDS.bottom + 1,
false, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
- assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.5);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.5);
- verify(mMockWindowManager).setMagnificationSpec(
+ assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
argThat(closeTo(getMagnificationSpec(scale, newOffsets))));
}
@Test
public void testMagnificationRegionChanged_serviceNotified() {
- mMagnificationController.register();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ magnificationRegionChanged_serviceNotified(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void magnificationRegionChanged_serviceNotified(int displayId) {
+ register(displayId);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
callbacks.onMagnificationRegionChanged(OTHER_REGION);
mMessageCapturingHandler.sendAllMessages();
- verify(mMockAms).notifyMagnificationChanged(OTHER_REGION, 1.0f,
+ verify(mMockAms).notifyMagnificationChanged(displayId, OTHER_REGION, 1.0f,
OTHER_MAGNIFICATION_BOUNDS.centerX(), OTHER_MAGNIFICATION_BOUNDS.centerY());
}
@Test
public void testOffsetMagnifiedRegion_whileMagnifying_offsetsMove() {
- mMagnificationController.register();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ offsetMagnifiedRegion_whileMagnifying_offsetsMove(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void offsetMagnifiedRegion_whileMagnifying_offsetsMove(int displayId) {
+ register(displayId);
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
float scale = 2.0f;
PointF startOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, startCenter, scale);
// First zoom in
assertTrue(mMagnificationController
- .setScaleAndCenter(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1));
+ .setScaleAndCenter(displayId, scale, startCenter.x, startCenter.y, false,
+ SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
Mockito.reset(mMockWindowManager);
PointF newCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
PointF newOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale);
- mMagnificationController.offsetMagnifiedRegion(
- startOffsets.x - newOffsets.x, startOffsets.y - newOffsets.y, SERVICE_ID_1);
+ mMagnificationController.offsetMagnifiedRegion(displayId,
+ startOffsets.x - newOffsets.x, startOffsets.y - newOffsets.y,
+ SERVICE_ID_1);
mMessageCapturingHandler.sendAllMessages();
MagnificationSpec expectedSpec = getMagnificationSpec(scale, newOffsets);
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedSpec)));
- assertEquals(newCenter.x, mMagnificationController.getCenterX(), 0.0);
- assertEquals(newCenter.y, mMagnificationController.getCenterY(), 0.0);
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+ argThat(closeTo(expectedSpec)));
+ assertEquals(newCenter.x, mMagnificationController.getCenterX(displayId), 0.0);
+ assertEquals(newCenter.y, mMagnificationController.getCenterY(displayId), 0.0);
verify(mMockValueAnimator, times(0)).start();
}
@Test
public void testOffsetMagnifiedRegion_whileNotMagnifying_hasNoEffect() {
- mMagnificationController.register();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ offsetMagnifiedRegion_whileNotMagnifying_hasNoEffect(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void offsetMagnifiedRegion_whileNotMagnifying_hasNoEffect(int displayId) {
+ register(displayId);
Mockito.reset(mMockWindowManager);
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
- mMagnificationController.offsetMagnifiedRegion(10, 10, SERVICE_ID_1);
- assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
- mMagnificationController.offsetMagnifiedRegion(-10, -10, SERVICE_ID_1);
- assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+ mMagnificationController.offsetMagnifiedRegion(displayId, 10, 10, SERVICE_ID_1);
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
+ mMagnificationController.offsetMagnifiedRegion(displayId, -10, -10, SERVICE_ID_1);
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
verifyNoMoreInteractions(mMockWindowManager);
}
@Test
public void testOffsetMagnifiedRegion_whileMagnifyingButAtEdge_hasNoEffect() {
- mMagnificationController.register();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ offsetMagnifiedRegion_whileMagnifyingButAtEdge_hasNoEffect(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void offsetMagnifiedRegion_whileMagnifyingButAtEdge_hasNoEffect(int displayId) {
+ register(displayId);
float scale = 2.0f;
// Upper left edges
PointF ulCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
assertTrue(mMagnificationController
- .setScaleAndCenter(scale, ulCenter.x, ulCenter.y, false, SERVICE_ID_1));
+ .setScaleAndCenter(displayId, scale, ulCenter.x, ulCenter.y, false,
+ SERVICE_ID_1));
Mockito.reset(mMockWindowManager);
- MagnificationSpec ulSpec = getCurrentMagnificationSpec();
- mMagnificationController.offsetMagnifiedRegion(-10, -10, SERVICE_ID_1);
- assertThat(getCurrentMagnificationSpec(), closeTo(ulSpec));
+ MagnificationSpec ulSpec = getCurrentMagnificationSpec(displayId);
+ mMagnificationController.offsetMagnifiedRegion(displayId, -10, -10,
+ SERVICE_ID_1);
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(ulSpec));
verifyNoMoreInteractions(mMockWindowManager);
// Lower right edges
PointF lrCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
assertTrue(mMagnificationController
- .setScaleAndCenter(scale, lrCenter.x, lrCenter.y, false, SERVICE_ID_1));
+ .setScaleAndCenter(displayId, scale, lrCenter.x, lrCenter.y, false,
+ SERVICE_ID_1));
Mockito.reset(mMockWindowManager);
- MagnificationSpec lrSpec = getCurrentMagnificationSpec();
- mMagnificationController.offsetMagnifiedRegion(10, 10, SERVICE_ID_1);
- assertThat(getCurrentMagnificationSpec(), closeTo(lrSpec));
+ MagnificationSpec lrSpec = getCurrentMagnificationSpec(displayId);
+ mMagnificationController.offsetMagnifiedRegion(displayId, 10, 10,
+ SERVICE_ID_1);
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(lrSpec));
verifyNoMoreInteractions(mMockWindowManager);
}
@Test
public void testGetIdOfLastServiceToChange_returnsCorrectValue() {
- mMagnificationController.register();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ getIdOfLastServiceToChange_returnsCorrectValue(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void getIdOfLastServiceToChange_returnsCorrectValue(int displayId) {
+ register(displayId);
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
assertTrue(mMagnificationController
- .setScale(2.0f, startCenter.x, startCenter.y, false, SERVICE_ID_1));
- assertEquals(SERVICE_ID_1, mMagnificationController.getIdOfLastServiceToMagnify());
+ .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false,
+ SERVICE_ID_1));
+ assertEquals(SERVICE_ID_1, mMagnificationController.getIdOfLastServiceToMagnify(displayId));
assertTrue(mMagnificationController
- .setScale(1.5f, startCenter.x, startCenter.y, false, SERVICE_ID_2));
- assertEquals(SERVICE_ID_2, mMagnificationController.getIdOfLastServiceToMagnify());
+ .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false,
+ SERVICE_ID_2));
+ assertEquals(SERVICE_ID_2, mMagnificationController.getIdOfLastServiceToMagnify(displayId));
}
@Test
public void testResetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled() {
- mMagnificationController.register();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ resetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void resetIfNeeded_resetsOnlyIfLastMagnifyingServiceIsDisabled(int displayId) {
+ register(displayId);
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
mMagnificationController
- .setScale(2.0f, startCenter.x, startCenter.y, false, SERVICE_ID_1);
+ .setScale(displayId, 2.0f, startCenter.x, startCenter.y, false,
+ SERVICE_ID_1);
mMagnificationController
- .setScale(1.5f, startCenter.x, startCenter.y, false, SERVICE_ID_2);
- assertFalse(mMagnificationController.resetIfNeeded(SERVICE_ID_1));
- assertTrue(mMagnificationController.isMagnifying());
- assertTrue(mMagnificationController.resetIfNeeded(SERVICE_ID_2));
- assertFalse(mMagnificationController.isMagnifying());
+ .setScale(displayId, 1.5f, startCenter.x, startCenter.y, false,
+ SERVICE_ID_2);
+ assertFalse(mMagnificationController.resetIfNeeded(displayId, SERVICE_ID_1));
+ assertTrue(mMagnificationController.isMagnifying(displayId));
+ assertTrue(mMagnificationController.resetIfNeeded(displayId, SERVICE_ID_2));
+ assertFalse(mMagnificationController.isMagnifying(displayId));
}
@Test
public void testSetUserId_resetsOnlyIfIdChanges() {
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ testSetUserId_resetsOnlyIfIdChanges(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void testSetUserId_resetsOnlyIfIdChanges(int displayId) {
final int userId1 = 1;
final int userId2 = 2;
- mMagnificationController.register();
+ register(displayId);
mMagnificationController.setUserId(userId1);
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
float scale = 2.0f;
- mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1);
+ mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false,
+ SERVICE_ID_1);
mMagnificationController.setUserId(userId1);
- assertTrue(mMagnificationController.isMagnifying());
+ assertTrue(mMagnificationController.isMagnifying(displayId));
mMagnificationController.setUserId(userId2);
- assertFalse(mMagnificationController.isMagnifying());
+ assertFalse(mMagnificationController.isMagnifying(displayId));
}
@Test
public void testResetIfNeeded_doesWhatItSays() {
- mMagnificationController.register();
- zoomIn2xToMiddle();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ testResetIfNeeded_doesWhatItSays(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void testResetIfNeeded_doesWhatItSays(int displayId) {
+ register(displayId);
+ zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
reset(mMockAms);
- assertTrue(mMagnificationController.resetIfNeeded(false));
- verify(mMockAms).notifyMagnificationChanged(
+ assertTrue(mMagnificationController.resetIfNeeded(displayId, false));
+ verify(mMockAms).notifyMagnificationChanged(eq(displayId),
eq(INITIAL_MAGNIFICATION_REGION), eq(1.0f), anyFloat(), anyFloat());
- assertFalse(mMagnificationController.isMagnifying());
- assertFalse(mMagnificationController.resetIfNeeded(false));
+ assertFalse(mMagnificationController.isMagnifying(displayId));
+ assertFalse(mMagnificationController.resetIfNeeded(displayId, false));
}
@Test
public void testTurnScreenOff_resetsMagnification() {
- mMagnificationController.register();
+ register(DISPLAY_0);
+ register(DISPLAY_1);
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext).registerReceiver(
broadcastReceiverCaptor.capture(), (IntentFilter) anyObject());
BroadcastReceiver br = broadcastReceiverCaptor.getValue();
- zoomIn2xToMiddle();
+ zoomIn2xToMiddle(DISPLAY_0);
+ zoomIn2xToMiddle(DISPLAY_1);
mMessageCapturingHandler.sendAllMessages();
br.onReceive(mMockContext, null);
mMessageCapturingHandler.sendAllMessages();
- assertFalse(mMagnificationController.isMagnifying());
+ assertFalse(mMagnificationController.isMagnifying(DISPLAY_0));
+ assertFalse(mMagnificationController.isMagnifying(DISPLAY_1));
}
@Test
public void testUserContextChange_resetsMagnification() {
- mMagnificationController.register();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
- zoomIn2xToMiddle();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ contextChange_resetsMagnification(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void contextChange_resetsMagnification(int displayId) {
+ register(displayId);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
+ zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
callbacks.onUserContextChanged();
mMessageCapturingHandler.sendAllMessages();
- assertFalse(mMagnificationController.isMagnifying());
+ assertFalse(mMagnificationController.isMagnifying(displayId));
}
@Test
public void testRotation_resetsMagnification() {
- mMagnificationController.register();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
- zoomIn2xToMiddle();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ rotation_resetsMagnification(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void rotation_resetsMagnification(int displayId) {
+ register(displayId);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
+ zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
- assertTrue(mMagnificationController.isMagnifying());
+ assertTrue(mMagnificationController.isMagnifying(displayId));
callbacks.onRotationChanged(0);
mMessageCapturingHandler.sendAllMessages();
- assertFalse(mMagnificationController.isMagnifying());
+ assertFalse(mMagnificationController.isMagnifying(displayId));
}
@Test
public void testBoundsChange_whileMagnifyingWithCompatibleSpec_noSpecChange() {
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ boundsChange_whileMagnifyingWithCompatibleSpec_noSpecChange(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void boundsChange_whileMagnifyingWithCompatibleSpec_noSpecChange(int displayId) {
// Going from a small region to a large one leads to no issues
- mMagnificationController.register();
- zoomIn2xToMiddle();
+ register(displayId);
+ zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
Mockito.reset(mMockWindowManager);
callbacks.onMagnificationRegionChanged(OTHER_REGION_COMPAT);
mMessageCapturingHandler.sendAllMessages();
- assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
verifyNoMoreInteractions(mMockWindowManager);
}
@Test
public void testBoundsChange_whileZoomingWithCompatibleSpec_noSpecChange() {
- mMagnificationController.register();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ boundsChange_whileZoomingWithCompatibleSpec_noSpecChange(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void boundsChange_whileZoomingWithCompatibleSpec_noSpecChange(int displayId) {
+ register(displayId);
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
float scale = 2.0f;
// setting animate parameter to true is differ from zoomIn2xToMiddle()
- mMagnificationController.setScale(scale, startCenter.x, startCenter.y, true, SERVICE_ID_1);
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
+ mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, true,
+ SERVICE_ID_1);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
Mockito.reset(mMockWindowManager);
callbacks.onMagnificationRegionChanged(OTHER_REGION_COMPAT);
mMessageCapturingHandler.sendAllMessages();
- assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
verifyNoMoreInteractions(mMockWindowManager);
}
@Test
public void testBoundsChange_whileMagnifyingWithIncompatibleSpec_offsetsConstrained() {
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ boundsChange_whileMagnifyingWithIncompatibleSpec_offsetsConstrained(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void boundsChange_whileMagnifyingWithIncompatibleSpec_offsetsConstrained(
+ int displayId) {
// In a large region, pan to the farthest point possible
- mMagnificationController.register();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
+ register(displayId);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
callbacks.onMagnificationRegionChanged(OTHER_REGION);
mMessageCapturingHandler.sendAllMessages();
PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER;
float scale = 2.0f;
- mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1);
+ mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false,
+ SERVICE_ID_1);
mMessageCapturingHandler.sendAllMessages();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(startSpec)));
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(startSpec)));
Mockito.reset(mMockWindowManager);
callbacks.onMagnificationRegionChanged(INITIAL_MAGNIFICATION_REGION);
mMessageCapturingHandler.sendAllMessages();
- MagnificationSpec endSpec = getCurrentMagnificationSpec();
+ MagnificationSpec endSpec = getCurrentMagnificationSpec(displayId);
assertThat(endSpec, CoreMatchers.not(closeTo(startSpec)));
PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS,
INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale);
assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets)));
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec)));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
}
@Test
public void testBoundsChange_whileZoomingWithIncompatibleSpec_jumpsToCompatibleSpec() {
- mMagnificationController.register();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ boundsChange_whileZoomingWithIncompatibleSpec_jumpsToCompatibleSpec(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void boundsChange_whileZoomingWithIncompatibleSpec_jumpsToCompatibleSpec(
+ int displayId) {
+ register(displayId);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
callbacks.onMagnificationRegionChanged(OTHER_REGION);
mMessageCapturingHandler.sendAllMessages();
PointF startCenter = OTHER_BOUNDS_LOWER_RIGHT_2X_CENTER;
float scale = 2.0f;
- mMagnificationController.setScale(scale, startCenter.x, startCenter.y, true, SERVICE_ID_1);
+ mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, true,
+ SERVICE_ID_1);
mMessageCapturingHandler.sendAllMessages();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
when(mMockValueAnimator.isRunning()).thenReturn(true);
callbacks.onMagnificationRegionChanged(INITIAL_MAGNIFICATION_REGION);
mMessageCapturingHandler.sendAllMessages();
verify(mMockValueAnimator).cancel();
- MagnificationSpec endSpec = getCurrentMagnificationSpec();
+ MagnificationSpec endSpec = getCurrentMagnificationSpec(displayId);
assertThat(endSpec, CoreMatchers.not(closeTo(startSpec)));
PointF expectedOffsets = computeOffsets(INITIAL_MAGNIFICATION_BOUNDS,
INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale);
assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets)));
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(endSpec)));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
}
@Test
public void testRequestRectOnScreen_rectAlreadyOnScreen_doesNothing() {
- mMagnificationController.register();
- zoomIn2xToMiddle();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ requestRectOnScreen_rectAlreadyOnScreen_doesNothing(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void requestRectOnScreen_rectAlreadyOnScreen_doesNothing(int displayId) {
+ register(displayId);
+ zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
Mockito.reset(mMockWindowManager);
int centerX = (int) INITIAL_MAGNIFICATION_BOUNDS_CENTER.x;
int centerY = (int) INITIAL_MAGNIFICATION_BOUNDS_CENTER.y;
callbacks.onRectangleOnScreenRequested(centerX - 1, centerY - 1, centerX + 1, centerY - 1);
mMessageCapturingHandler.sendAllMessages();
- assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
verifyNoMoreInteractions(mMockWindowManager);
}
@Test
public void testRequestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen() {
- mMagnificationController.register();
- zoomIn2xToMiddle();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ requestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void requestRectOnScreen_rectCanFitOnScreen_pansToGetRectOnScreen(int displayId) {
+ register(displayId);
+ zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
Mockito.reset(mMockWindowManager);
callbacks.onRectangleOnScreenRequested(0, 0, 1, 1);
mMessageCapturingHandler.sendAllMessages();
MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, 0);
- assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec));
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec)));
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+ argThat(closeTo(expectedEndSpec)));
}
@Test
public void testRequestRectOnScreen_garbageInput_doesNothing() {
- mMagnificationController.register();
- zoomIn2xToMiddle();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ requestRectOnScreen_garbageInput_doesNothing(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void requestRectOnScreen_garbageInput_doesNothing(int displayId) {
+ register(displayId);
+ zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
Mockito.reset(mMockWindowManager);
callbacks.onRectangleOnScreenRequested(0, 0, -50, -50);
mMessageCapturingHandler.sendAllMessages();
- assertThat(getCurrentMagnificationSpec(), closeTo(startSpec));
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(startSpec));
verifyNoMoreInteractions(mMockWindowManager);
}
@Test
public void testRequestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale() {
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ requestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void requestRectOnScreen_rectTooWide_pansToGetStartOnScreenBasedOnLocale(
+ int displayId) {
Locale.setDefault(new Locale("en", "us"));
- mMagnificationController.register();
- zoomIn2xToMiddle();
+ register(displayId);
+ zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
Mockito.reset(mMockWindowManager);
Rect wideRect = new Rect(0, 50, 100, 51);
callbacks.onRectangleOnScreenRequested(
wideRect.left, wideRect.top, wideRect.right, wideRect.bottom);
mMessageCapturingHandler.sendAllMessages();
MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, 0, startSpec.offsetY);
- assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec));
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec)));
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+ argThat(closeTo(expectedEndSpec)));
Mockito.reset(mMockWindowManager);
// Repeat with RTL
@@ -692,50 +900,66 @@ public class MagnificationControllerTest {
wideRect.left, wideRect.top, wideRect.right, wideRect.bottom);
mMessageCapturingHandler.sendAllMessages();
expectedEndSpec = getMagnificationSpec(2.0f, -100, startSpec.offsetY);
- assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec));
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec)));
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+ argThat(closeTo(expectedEndSpec)));
}
@Test
public void testRequestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen() {
- mMagnificationController.register();
- zoomIn2xToMiddle();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ requestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void requestRectOnScreen_rectTooTall_pansMinimumToGetTopOnScreen(int displayId) {
+ register(displayId);
+ zoomIn2xToMiddle(displayId);
mMessageCapturingHandler.sendAllMessages();
- MagnificationCallbacks callbacks = getMagnificationCallbacks();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
+ MagnificationCallbacks callbacks = getMagnificationCallbacks(displayId);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
Mockito.reset(mMockWindowManager);
Rect tallRect = new Rect(50, 0, 51, 100);
callbacks.onRectangleOnScreenRequested(
tallRect.left, tallRect.top, tallRect.right, tallRect.bottom);
mMessageCapturingHandler.sendAllMessages();
MagnificationSpec expectedEndSpec = getMagnificationSpec(2.0f, startSpec.offsetX, 0);
- assertThat(getCurrentMagnificationSpec(), closeTo(expectedEndSpec));
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(expectedEndSpec)));
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(expectedEndSpec));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+ argThat(closeTo(expectedEndSpec)));
}
@Test
public void testChangeMagnification_duringAnimation_animatesToNewValue() {
- mMagnificationController.register();
- MagnificationSpec startSpec = getCurrentMagnificationSpec();
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ changeMagnification_duringAnimation_animatesToNewValue(i);
+ resetMockWindowManager();
+ }
+ }
+
+ private void changeMagnification_duringAnimation_animatesToNewValue(int displayId) {
+ register(displayId);
+ MagnificationSpec startSpec = getCurrentMagnificationSpec(displayId);
float scale = 2.5f;
PointF firstCenter = INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER;
MagnificationSpec firstEndSpec = getMagnificationSpec(
scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, firstCenter, scale));
- assertTrue(mMagnificationController.setScaleAndCenter(scale, firstCenter.x, firstCenter.y,
- true, SERVICE_ID_1));
+ assertTrue(mMagnificationController.setScaleAndCenter(displayId,
+ scale, firstCenter.x, firstCenter.y, true, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
- assertEquals(firstCenter.x, mMagnificationController.getCenterX(), 0.5);
- assertEquals(firstCenter.y, mMagnificationController.getCenterY(), 0.5);
- assertThat(getCurrentMagnificationSpec(), closeTo(firstEndSpec));
+ assertEquals(firstCenter.x, mMagnificationController.getCenterX(displayId), 0.5);
+ assertEquals(firstCenter.y, mMagnificationController.getCenterY(displayId), 0.5);
+ assertThat(getCurrentMagnificationSpec(displayId), closeTo(firstEndSpec));
verify(mMockValueAnimator, times(1)).start();
// Initial value
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- verify(mMockWindowManager).setMagnificationSpec(startSpec);
- verify(mMockAms).notifyMagnificationChanged(
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId), eq(startSpec));
+ verify(mMockAms).notifyMagnificationChanged(displayId,
INITIAL_MAGNIFICATION_REGION, scale, firstCenter.x, firstCenter.y);
Mockito.reset(mMockWindowManager);
@@ -745,32 +969,34 @@ public class MagnificationControllerTest {
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
MagnificationSpec intermediateSpec1 =
getInterpolatedMagSpec(startSpec, firstEndSpec, fraction);
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(intermediateSpec1)));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+ argThat(closeTo(intermediateSpec1)));
Mockito.reset(mMockWindowManager);
PointF newCenter = INITIAL_BOUNDS_UPPER_LEFT_2X_CENTER;
MagnificationSpec newEndSpec = getMagnificationSpec(
scale, computeOffsets(INITIAL_MAGNIFICATION_BOUNDS, newCenter, scale));
- assertTrue(mMagnificationController.setCenter(
+ assertTrue(mMagnificationController.setCenter(displayId,
newCenter.x, newCenter.y, true, SERVICE_ID_1));
mMessageCapturingHandler.sendAllMessages();
// Animation should have been restarted
verify(mMockValueAnimator, times(2)).start();
- verify(mMockAms).notifyMagnificationChanged(
+ verify(mMockAms).notifyMagnificationChanged(displayId,
INITIAL_MAGNIFICATION_REGION, scale, newCenter.x, newCenter.y);
// New starting point should be where we left off
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(0.0f);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(intermediateSpec1)));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+ argThat(closeTo(intermediateSpec1)));
Mockito.reset(mMockWindowManager);
// Second intermediate point
fraction = 0.5f;
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(fraction);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- verify(mMockWindowManager).setMagnificationSpec(
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
argThat(closeTo(getInterpolatedMagSpec(intermediateSpec1, newEndSpec, fraction))));
Mockito.reset(mMockWindowManager);
@@ -778,21 +1004,54 @@ public class MagnificationControllerTest {
Mockito.reset(mMockWindowManager);
when(mMockValueAnimator.getAnimatedFraction()).thenReturn(1.0f);
mTargetAnimationListener.onAnimationUpdate(mMockValueAnimator);
- verify(mMockWindowManager).setMagnificationSpec(argThat(closeTo(newEndSpec)));
+ verify(mMockWindowManager).setMagnificationSpec(eq(displayId),
+ argThat(closeTo(newEndSpec)));
+ }
+
+ private void initMockWindowManager() {
+ for (int i = 0; i < DISPLAY_COUNT; i++) {
+ when(mMockWindowManager.setMagnificationCallbacks(eq(i), any())).thenReturn(true);
+ }
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
+ Object[] args = invocationOnMock.getArguments();
+ Region regionArg = (Region) args[1];
+ regionArg.set(INITIAL_MAGNIFICATION_REGION);
+ return null;
+ }
+ }).when(mMockWindowManager).getMagnificationRegion(anyInt(), (Region) anyObject());
+ }
+
+ private void resetMockWindowManager() {
+ Mockito.reset(mMockWindowManager);
+ initMockWindowManager();
+ }
+
+ private void register(int displayId) {
+ mMockValueAnimator = mock(ValueAnimator.class);
+ when(mMockControllerCtx.newValueAnimator()).thenReturn(mMockValueAnimator);
+ mMagnificationController.register(displayId);
+ ArgumentCaptor<ValueAnimator.AnimatorUpdateListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(ValueAnimator.AnimatorUpdateListener.class);
+ verify(mMockValueAnimator).addUpdateListener(listenerArgumentCaptor.capture());
+ mTargetAnimationListener = listenerArgumentCaptor.getValue();
+ Mockito.reset(mMockValueAnimator); // Ignore other initialization
}
- private void zoomIn2xToMiddle() {
+ private void zoomIn2xToMiddle(int displayId) {
PointF startCenter = INITIAL_MAGNIFICATION_BOUNDS_CENTER;
float scale = 2.0f;
- mMagnificationController.setScale(scale, startCenter.x, startCenter.y, false, SERVICE_ID_1);
- assertTrue(mMagnificationController.isMagnifying());
+ mMagnificationController.setScale(displayId, scale, startCenter.x, startCenter.y, false,
+ SERVICE_ID_1);
+ assertTrue(mMagnificationController.isMagnifying(displayId));
}
- private MagnificationCallbacks getMagnificationCallbacks() {
+ private MagnificationCallbacks getMagnificationCallbacks(int displayId) {
ArgumentCaptor<MagnificationCallbacks> magnificationCallbacksCaptor =
ArgumentCaptor.forClass(MagnificationCallbacks.class);
verify(mMockWindowManager)
- .setMagnificationCallbacks(magnificationCallbacksCaptor.capture());
+ .setMagnificationCallbacks(eq(displayId), magnificationCallbacksCaptor.capture());
return magnificationCallbacksCaptor.getValue();
}
@@ -823,9 +1082,10 @@ public class MagnificationControllerTest {
return spec;
}
- private MagnificationSpec getCurrentMagnificationSpec() {
- return getMagnificationSpec(mMagnificationController.getScale(),
- mMagnificationController.getOffsetX(), mMagnificationController.getOffsetY());
+ private MagnificationSpec getCurrentMagnificationSpec(int displayId) {
+ return getMagnificationSpec(mMagnificationController.getScale(displayId),
+ mMagnificationController.getOffsetX(displayId),
+ mMagnificationController.getOffsetY(displayId));
}
private MagSpecMatcher closeTo(MagnificationSpec spec) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 032074a7e398..d91ce39ea92c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -26,16 +26,19 @@ import static com.android.server.testutils.TestUtils.strictMock;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doNothing;
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 android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.content.Context;
+import android.os.Handler;
import android.os.Message;
import android.util.DebugUtils;
import android.view.InputDevice;
@@ -104,14 +107,9 @@ public class MagnificationGestureHandlerTest {
public static final float DEFAULT_X = 301;
public static final float DEFAULT_Y = 299;
+ private static final int DISPLAY_0 = 0;
+
private Context mContext;
- final AccessibilityManagerService mMockAms = mock(AccessibilityManagerService.class);
- final WindowManagerInternal mMockWindowManager = mock(WindowManagerInternal.class);
- final MessageCapturingHandler mMessageCapturingHandler =
- new MessageCapturingHandler(null);
- final ValueAnimator mMockValueAnimator = mock(ValueAnimator.class);
- MagnificationController.SettingsBridge mMockSettingsBridge =
- mock(MagnificationController.SettingsBridge.class);
MagnificationController mMagnificationController;
private OffsettableClock mClock;
@@ -123,18 +121,26 @@ public class MagnificationGestureHandlerTest {
@Before
public void setUp() {
mContext = InstrumentationRegistry.getContext();
- mMagnificationController = new MagnificationController(mContext, mMockAms, new Object(),
- mMessageCapturingHandler, mMockWindowManager, mMockValueAnimator,
- mMockSettingsBridge) {
+ final MagnificationController.ControllerContext mockController =
+ mock(MagnificationController.ControllerContext.class);
+ final WindowManagerInternal mockWindowManager = mock(WindowManagerInternal.class);
+ when(mockController.getContext()).thenReturn(mContext);
+ when(mockController.getAms()).thenReturn(mock(AccessibilityManagerService.class));
+ when(mockController.getWindowManager()).thenReturn(mockWindowManager);
+ when(mockController.getHandler()).thenReturn(new Handler(mContext.getMainLooper()));
+ when(mockController.newValueAnimator()).thenReturn(new ValueAnimator());
+ when(mockController.getAnimationDuration()).thenReturn(1000L);
+ when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true);
+ mMagnificationController = new MagnificationController(mockController, new Object()) {
@Override
- public boolean magnificationRegionContains(float x, float y) {
+ public boolean magnificationRegionContains(int displayId, float x, float y) {
return true;
}
@Override
- void setForceShowMagnifiableBounds(boolean show) {}
+ void setForceShowMagnifiableBounds(int displayId, boolean show) {}
};
- mMagnificationController.register();
+ mMagnificationController.register(DISPLAY_0);
mClock = new OffsettableClock.Stopped();
boolean detectTripleTap = true;
@@ -144,7 +150,7 @@ public class MagnificationGestureHandlerTest {
@After
public void tearDown() {
- mMagnificationController.unregister();
+ mMagnificationController.unregister(DISPLAY_0);
}
@NonNull
@@ -302,6 +308,24 @@ public class MagnificationGestureHandlerTest {
assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED);
}
+ @Test
+ public void testMultiTap_outOfDistanceSlop_shouldInIdle() {
+ // All delay motion events should be sent, if multi-tap with out of distance slop.
+ // STATE_IDLE will check if tapCount() < 2.
+ allowEventDelegation();
+ assertStaysIn(STATE_IDLE, () -> {
+ tap();
+ tap(DEFAULT_X * 2, DEFAULT_Y * 2);
+ });
+ assertStaysIn(STATE_IDLE, () -> {
+ tap();
+ tap(DEFAULT_X * 2, DEFAULT_Y * 2);
+ tap();
+ tap(DEFAULT_X * 2, DEFAULT_Y * 2);
+ tap();
+ });
+ }
+
private void assertZoomsImmediatelyOnSwipeFrom(int state) {
goFromStateIdleTo(state);
swipeAndHold();
@@ -509,7 +533,7 @@ public class MagnificationGestureHandlerTest {
}
private boolean isZoomed() {
- return mMgh.mMagnificationController.isMagnifying();
+ return mMgh.mMagnificationController.isMagnifying(DISPLAY_0);
}
private int tapCount() {
@@ -525,6 +549,11 @@ public class MagnificationGestureHandlerTest {
send(upEvent());
}
+ private void tap(float x, float y) {
+ send(downEvent(x, y));
+ send(upEvent(x, y));
+ }
+
private void swipe() {
swipeAndHold();
send(upEvent());
@@ -566,18 +595,26 @@ public class MagnificationGestureHandlerTest {
}
private MotionEvent downEvent() {
+ return downEvent(DEFAULT_X, DEFAULT_Y);
+ }
+
+ private MotionEvent downEvent(float x, float y) {
mLastDownTime = mClock.now();
return fromTouchscreen(MotionEvent.obtain(mLastDownTime, mLastDownTime,
- ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0));
+ ACTION_DOWN, x, y, 0));
}
private MotionEvent upEvent() {
- return upEvent(mLastDownTime);
+ return upEvent(DEFAULT_X, DEFAULT_Y, mLastDownTime);
+ }
+
+ private MotionEvent upEvent(float x, float y) {
+ return upEvent(x, y, mLastDownTime);
}
- private MotionEvent upEvent(long downTime) {
+ private MotionEvent upEvent(float x, float y, long downTime) {
return fromTouchscreen(MotionEvent.obtain(downTime, mClock.now(),
- MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0));
+ MotionEvent.ACTION_UP, x, y, 0));
}
private MotionEvent pointerEvent(int action, float x, float y) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 38e8ac2d8f4c..0813e6fa0252 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -206,6 +206,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mAdmin1Context.binder.callingUid = DpmMockContext.CALLER_UID;
setUpUserManager();
+
+ when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
}
private TransferOwnershipMetadataManager getMockTransferMetadataManager() {
@@ -836,6 +838,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
MockUtils.checkIntent(intent),
MockUtils.checkUserHandle(MANAGED_PROFILE_USER_ID));
}
+
/**
* Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs successfully.
*/
@@ -2618,6 +2621,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(getServices().lockPatternUtils
.isSeparateProfileChallengeEnabled(eq(PROFILE_USER))).thenReturn(false);
dpmi.reportSeparateProfileChallengeChanged(PROFILE_USER);
+ when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(true);
verifyScreenTimeoutCall(Long.MAX_VALUE, PROFILE_USER);
verifyScreenTimeoutCall(10L , UserHandle.USER_SYSTEM);
@@ -4233,6 +4237,41 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertTrue(dpm.isActivePasswordSufficient());
}
+ public void testIsActivePasswordSufficient_noLockScreen() throws Exception {
+ // If there is no lock screen, the password is considered empty no matter what, because
+ // it provides no security.
+ when(getServices().lockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ mContext.packageName = admin1.getPackageName();
+ setupDeviceOwner();
+
+ // If no password requirements are set, isActivePasswordSufficient should succeed.
+ assertTrue(dpm.isActivePasswordSufficient());
+
+ // Now set some password quality requirements.
+ dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
+
+ reset(mContext.spiedContext);
+ final int userHandle = UserHandle.getUserId(mContext.binder.callingUid);
+ PasswordMetrics passwordMetricsNoSymbols = new PasswordMetrics(
+ DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, 9,
+ 8, 2,
+ 6, 1,
+ 0, 1);
+ // This should be ignored, as there is no lock screen.
+ dpm.setActivePasswordState(passwordMetricsNoSymbols, userHandle);
+ dpm.reportPasswordChanged(userHandle);
+
+ // No broadcast should be sent.
+ verify(mContext.spiedContext, times(0)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED),
+ MockUtils.checkUserHandle(userHandle));
+
+ // The active (nonexistent) password doesn't comply with the requirements.
+ assertFalse(dpm.isActivePasswordSufficient());
+ }
+
private void setActivePasswordState(PasswordMetrics passwordMetrics)
throws Exception {
final int userHandle = UserHandle.getUserId(mContext.binder.callingUid);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 3a6cdc2ad7c9..a89198ae3708 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -147,12 +147,12 @@ public class HdmiCecLocalDeviceAudioSystemTest {
}
@Override
- void writeStringSetting(String key, String value) {
+ void writeStringSystemProperty(String key, String value) {
// do nothing
}
@Override
- boolean readBooleanSetting(String key, boolean defVal) {
+ boolean readBooleanSystemProperty(String key, boolean defVal) {
switch (key) {
case Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE:
return mMutingEnabled;
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
index 0328621ce5d1..8afc3d30efa3 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java
@@ -22,7 +22,6 @@ import static android.view.Display.INVALID_DISPLAY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -58,8 +57,7 @@ public class InputMethodManagerServiceTests {
// Make sure that there is a short-circuit for DEFAULT_DISPLAY.
assertEquals(DEFAULT_DISPLAY,
InputMethodManagerService.computeImeDisplayIdForTarget(
- DEFAULT_DISPLAY, false /* isVrImeStarted */,
- sMustNotBeCalledChecker));
+ DEFAULT_DISPLAY, sMustNotBeCalledChecker));
}
@Test
@@ -67,17 +65,7 @@ public class InputMethodManagerServiceTests {
// Make sure that there is a short-circuit for INVALID_DISPLAY.
assertEquals(DEFAULT_DISPLAY,
InputMethodManagerService.computeImeDisplayIdForTarget(
- INVALID_DISPLAY, false /* isVrImeStarted */,
- sMustNotBeCalledChecker));
- }
-
- @Test
- public void testComputeImeDisplayId_VrIme() {
- // Make sure that there is a short-circuit for VR IME.
- assertEquals(DEFAULT_DISPLAY,
- InputMethodManagerService.computeImeDisplayIdForTarget(
- SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, true /* isVrImeStarted */,
- sMustNotBeCalledChecker));
+ INVALID_DISPLAY, sMustNotBeCalledChecker));
}
@Test
@@ -86,8 +74,7 @@ public class InputMethodManagerServiceTests {
// Make sure IME displayId is DEFAULT_DISPLAY.
assertEquals(DEFAULT_DISPLAY,
InputMethodManagerService.computeImeDisplayIdForTarget(
- NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, false /* isVrImeStarted */,
- sChecker));
+ NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, sChecker));
}
@Test
@@ -96,7 +83,6 @@ public class InputMethodManagerServiceTests {
// Make sure IME displayId is the same display.
assertEquals(SYSTEM_DECORATION_SUPPORT_DISPLAY_ID,
InputMethodManagerService.computeImeDisplayIdForTarget(
- SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, false /* isVrImeStarted */,
- sChecker));
+ SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, sChecker));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 2dc3510a82e5..cf89cb8f7a15 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -37,8 +37,8 @@ import android.os.FileUtils;
import android.os.IProgressListener;
import android.os.RemoteException;
import android.os.UserManager;
-import android.os.storage.StorageManager;
import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
import android.security.KeyStore;
import android.test.AndroidTestCase;
@@ -46,6 +46,7 @@ import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.LocalServices;
+import com.android.server.wm.WindowManagerInternal;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -85,6 +86,8 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
KeyStore mKeyStore;
MockSyntheticPasswordManager mSpManager;
IAuthSecret mAuthSecretService;
+ WindowManagerInternal mMockWindowManager;
+ protected boolean mHasSecureLockScreen;
@Override
protected void setUp() throws Exception {
@@ -97,10 +100,13 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
mActivityManager = mock(IActivityManager.class);
mDevicePolicyManager = mock(DevicePolicyManager.class);
mDevicePolicyManagerInternal = mock(DevicePolicyManagerInternal.class);
+ mMockWindowManager = mock(WindowManagerInternal.class);
LocalServices.removeServiceForTest(LockSettingsInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.addService(DevicePolicyManagerInternal.class, mDevicePolicyManagerInternal);
+ LocalServices.addService(WindowManagerInternal.class, mMockWindowManager);
mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class),
@@ -114,11 +120,17 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase {
storageDir.mkdirs();
}
+ mHasSecureLockScreen = true;
mLockPatternUtils = new LockPatternUtils(mContext) {
@Override
public ILockSettings getLockSettings() {
return mService;
}
+
+ @Override
+ public boolean hasSecureLockScreen() {
+ return mHasSecureLockScreen;
+ }
};
mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService,
mUserManager);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index e12f6d3be71e..5124803ee298 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -26,13 +26,12 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSW
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.service.gatekeeper.GateKeeperResponse;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
-import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
import com.android.server.locksettings.FakeGateKeeperService.VerifyHandle;
+import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
/**
* runtest frameworks-services -c com.android.server.locksettings.LockSettingsServiceTests
@@ -54,11 +53,21 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
PASSWORD_QUALITY_ALPHABETIC);
}
+ public void testCreatePasswordFailsWithoutLockScreen() throws RemoteException {
+ testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "password",
+ CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
+ }
+
public void testCreatePatternPrimaryUser() throws RemoteException {
testCreateCredential(PRIMARY_USER_ID, "123456789", CREDENTIAL_TYPE_PATTERN,
PASSWORD_QUALITY_SOMETHING);
}
+ public void testCreatePatternFailsWithoutLockScreen() throws RemoteException {
+ testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "123456789",
+ CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING);
+ }
+
public void testChangePasswordPrimaryUser() throws RemoteException {
testChangeCredentials(PRIMARY_USER_ID, "78963214", CREDENTIAL_TYPE_PATTERN,
"asdfghjk", CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC);
@@ -198,6 +207,21 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
assertVerifyCredentials(userId, credential, type, -1);
}
+ private void testCreateCredentialFailsWithoutLockScreen(
+ int userId, String credential, int type, int quality) throws RemoteException {
+ mHasSecureLockScreen = false;
+
+ try {
+ mService.setLockCredential(credential, type, null, quality, userId);
+ fail("An exception should have been thrown.");
+ } catch (UnsupportedOperationException e) {
+ // Success - the exception was expected.
+ }
+
+ assertFalse(mService.havePassword(userId));
+ assertFalse(mService.havePattern(userId));
+ }
+
private void testChangeCredentials(int userId, String newCredential, int newType,
String oldCredential, int oldType, int quality) throws RemoteException {
final long sid = 1234;
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 a28a5a10e832..929c3b525db9 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -77,6 +78,7 @@ public class LockSettingsShellCommandTest {
final Context context = InstrumentationRegistry.getTargetContext();
mUserId = ActivityManager.getCurrentUser();
mCommand = new LockSettingsShellCommand(mLockPatternUtils);
+ when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(true);
}
@Test
@@ -103,6 +105,16 @@ public class LockSettingsShellCommandTest {
}
@Test
+ public void testChangePin_noLockScreen() throws Exception {
+ when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-pin", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).hasSecureLockScreen();
+ verifyNoMoreInteractions(mLockPatternUtils);
+ }
+
+ @Test
public void testChangePassword() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
@@ -115,6 +127,16 @@ public class LockSettingsShellCommandTest {
}
@Test
+ public void testChangePassword_noLockScreen() throws Exception {
+ when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-password", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).hasSecureLockScreen();
+ verifyNoMoreInteractions(mLockPatternUtils);
+ }
+
+ @Test
public void testChangePattern() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
@@ -126,6 +148,16 @@ public class LockSettingsShellCommandTest {
}
@Test
+ public void testChangePattern_noLockScreen() throws Exception {
+ when(mLockPatternUtils.hasSecureLockScreen()).thenReturn(false);
+ assertEquals(-1, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "set-pattern", "--old", "1234", "4321" },
+ mShellCallback, mResultReceiver));
+ verify(mLockPatternUtils).hasSecureLockScreen();
+ verifyNoMoreInteractions(mLockPatternUtils);
+ }
+
+ @Test
public void testClear() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 94e02bc4d35f..0595a5b2e9a0 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -40,10 +40,10 @@ import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationRe
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
-import java.util.ArrayList;
-
import org.mockito.ArgumentCaptor;
+import java.util.ArrayList;
+
/**
* runtest frameworks-services -c com.android.server.locksettings.SyntheticPasswordTests
@@ -448,6 +448,37 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
}
+ public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception {
+ final String password = "password";
+ final String pattern = "123654";
+ final String token = "some-high-entropy-secure-token";
+
+ mHasSecureLockScreen = false;
+ enableSyntheticPassword();
+ long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID);
+ assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
+
+ try {
+ mLocalService.setLockCredentialWithToken(password,
+ LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token.getBytes(),
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ fail("An exception should have been thrown.");
+ } catch (UnsupportedOperationException e) {
+ // Success - the exception was expected.
+ }
+ assertFalse(mService.havePassword(PRIMARY_USER_ID));
+
+ try {
+ mLocalService.setLockCredentialWithToken(pattern,
+ LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token.getBytes(),
+ PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+ fail("An exception should have been thrown.");
+ } catch (UnsupportedOperationException e) {
+ // Success - the exception was expected.
+ }
+ assertFalse(mService.havePattern(PRIMARY_USER_ID));
+ }
+
public void testgetHashFactorPrimaryUser() throws RemoteException {
final String password = "password";
mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
new file mode 100644
index 000000000000..73e96134167a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -0,0 +1,305 @@
+/*
+ * 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 org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.content.pm.PackageInstaller;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.FastXmlSerializer;
+
+import libcore.io.IoUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageInstallerSessionTest {
+ private File mTmpDir;
+ private AtomicFile mSessionsFile;
+ private static final String TAG_SESSIONS = "sessions";
+
+ @Mock
+ PackageManagerService mMockPackageManagerInternal;
+
+ @Before
+ public void setUp() throws Exception {
+ mTmpDir = IoUtils.createTemporaryDirectory("PackageInstallerSessionTest");
+ mSessionsFile = new AtomicFile(
+ new File(mTmpDir.getAbsolutePath() + "/sessions.xml"), "package-session");
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testWriteAndRestoreSessionXmlSimpleSession() {
+ PackageInstallerSession session = createSimpleSession();
+ dumpSession(session);
+ List<PackageInstallerSession> restored = restoreSessions();
+ assertEquals(1, restored.size());
+ assertSessionsEquivalent(session, restored.get(0));
+ }
+
+ @Test
+ public void testWriteAndRestoreSessionXmlStagedSession() {
+ PackageInstallerSession session = createStagedSession();
+ dumpSession(session);
+ List<PackageInstallerSession> restored = restoreSessions();
+ assertEquals(1, restored.size());
+ assertSessionsEquivalent(session, restored.get(0));
+ }
+
+ @Test
+ public void testWriteAndRestoreSessionXmlGrantedPermission() {
+ PackageInstallerSession session = createSessionWithGrantedPermissions();
+ dumpSession(session);
+ List<PackageInstallerSession> restored = restoreSessions();
+ assertEquals(1, restored.size());
+ assertSessionsEquivalent(session, restored.get(0));
+ }
+
+ @Test
+ public void testWriteAndRestoreSessionXmlMultiPackageSessions() {
+ PackageInstallerSession session = createMultiPackageParentSession(123, new int[]{234, 345});
+ PackageInstallerSession childSession1 = createMultiPackageChildSession(234, 123);
+ PackageInstallerSession childSession2 = createMultiPackageChildSession(345, 123);
+ List<PackageInstallerSession> sessionGroup =
+ Arrays.asList(session, childSession1, childSession2);
+ dumpSessions(sessionGroup);
+ List<PackageInstallerSession> restored = restoreSessions();
+ assertEquals(3, restored.size());
+ assertSessionsEquivalent(sessionGroup, restored);
+ }
+
+ private PackageInstallerSession createSimpleSession() {
+ return createSession(false, false, 123, false, PackageInstaller.SessionInfo.INVALID_ID,
+ null);
+ }
+
+ private PackageInstallerSession createStagedSession() {
+ return createSession(true, false, 123, false, PackageInstaller.SessionInfo.INVALID_ID,
+ null);
+ }
+
+ private PackageInstallerSession createSessionWithGrantedPermissions() {
+ return createSession(false, true, 123, false, PackageInstaller.SessionInfo.INVALID_ID,
+ null);
+ }
+
+ private PackageInstallerSession createMultiPackageParentSession(int sessionId,
+ int[] childSessionIds) {
+ return createSession(false, false, sessionId, true,
+ PackageInstaller.SessionInfo.INVALID_ID, childSessionIds);
+ }
+
+ private PackageInstallerSession createMultiPackageChildSession(int sessionId,
+ int parentSessionId) {
+ return createSession(false, false, sessionId, false, parentSessionId, null);
+ }
+
+ private PackageInstallerSession createSession(boolean staged, boolean withGrantedPermissions,
+ int sessionId, boolean isMultiPackage,
+ int parentSessionId, int[] childSessionIds) {
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ if (staged) {
+ params.isStaged = true;
+ }
+ if (withGrantedPermissions) {
+ params.grantedRuntimePermissions = new String[]{"permission1", "permission2"};
+ }
+ if (isMultiPackage) {
+ params.isMultiPackage = true;
+ }
+ return new PackageInstallerSession(
+ /* callback */ null,
+ /* context */null,
+ /* pm */ mMockPackageManagerInternal,
+ /* sessionProvider */ null,
+ /* looper */ BackgroundThread.getHandler().getLooper(),
+ /* stagingManager */ null,
+ /* sessionId */ sessionId,
+ /* userId */ 456,
+ /* installerPackageName */ "testInstaller",
+ /* installerUid */ -1,
+ /* sessionParams */ params,
+ /* createdMillis */ 0L,
+ /* stageDir */ mTmpDir,
+ /* stageCid */ null,
+ /* prepared */ true,
+ /* sealed */ false, // Setting to true would trigger some PM logic.
+ /* childSessionIds */ childSessionIds != null ? childSessionIds : new int[0],
+ /* parentSessionId */ parentSessionId,
+ /* isReady */ staged ? true : false,
+ /* isFailed */ false,
+ /* isApplied */false,
+ /* stagedSessionErrorCode */ PackageInstaller.SessionInfo.VERIFICATION_FAILED);
+ }
+
+ private void dumpSession(PackageInstallerSession session) {
+ dumpSessions(Arrays.asList(session));
+ }
+
+ private void dumpSessions(List<PackageInstallerSession> sessions) {
+ FileOutputStream fos = null;
+ try {
+ fos = mSessionsFile.startWrite();
+
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(fos, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.startTag(null, TAG_SESSIONS);
+ for (PackageInstallerSession session : sessions) {
+ session.write(out, mTmpDir);
+ }
+ out.endTag(null, TAG_SESSIONS);
+ out.endDocument();
+
+ mSessionsFile.finishWrite(fos);
+ Slog.d("PackageInstallerSessionTest", new String(mSessionsFile.readFully()));
+ } catch (IOException e) {
+ if (fos != null) {
+ mSessionsFile.failWrite(fos);
+ }
+ }
+ }
+
+ // This is roughly the logic used in PackageInstallerService to read the session. Note that
+ // this test stresses readFromXml method from PackageInstallerSession, and doesn't cover the
+ // PackageInstallerService portion of the parsing.
+ private List<PackageInstallerSession> restoreSessions() {
+ List<PackageInstallerSession> ret = new ArrayList<>();
+ FileInputStream fis = null;
+ try {
+ fis = mSessionsFile.openRead();
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(fis, StandardCharsets.UTF_8.name());
+
+ int type;
+ while ((type = in.next()) != END_DOCUMENT) {
+ if (type == START_TAG) {
+ final String tag = in.getName();
+ if (PackageInstallerSession.TAG_SESSION.equals(tag)) {
+ final PackageInstallerSession session;
+ try {
+ session = PackageInstallerSession.readFromXml(in, null,
+ null, mMockPackageManagerInternal,
+ BackgroundThread.getHandler().getLooper(), null,
+ mTmpDir, null);
+ ret.add(session);
+ } catch (Exception e) {
+ Slog.e("PackageInstallerSessionTest", "Exception ", e);
+ continue;
+ }
+ }
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Missing sessions are okay, probably first boot
+ } catch (IOException | XmlPullParserException e) {
+
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+ return ret;
+ }
+
+ private void assertSessionParamsEquivalent(PackageInstaller.SessionParams expected,
+ PackageInstaller.SessionParams actual) {
+ assertEquals(expected.mode, actual.mode);
+ assertEquals(expected.installFlags, actual.installFlags);
+ assertEquals(expected.installLocation, actual.installLocation);
+ assertEquals(expected.installReason, actual.installReason);
+ assertEquals(expected.sizeBytes, actual.sizeBytes);
+ assertEquals(expected.appPackageName, actual.appPackageName);
+ assertEquals(expected.appIcon, actual.appIcon);
+ assertEquals(expected.originatingUri, actual.originatingUri);
+ assertEquals(expected.originatingUid, actual.originatingUid);
+ assertEquals(expected.referrerUri, actual.referrerUri);
+ assertEquals(expected.abiOverride, actual.abiOverride);
+ assertEquals(expected.volumeUuid, actual.volumeUuid);
+ assertArrayEquals(expected.grantedRuntimePermissions, actual.grantedRuntimePermissions);
+ assertEquals(expected.installerPackageName, actual.installerPackageName);
+ assertEquals(expected.isMultiPackage, actual.isMultiPackage);
+ assertEquals(expected.isStaged, actual.isStaged);
+ }
+
+ private void assertSessionsEquivalent(List<PackageInstallerSession> expected,
+ List<PackageInstallerSession> actual) {
+ assertEquals(expected.size(), actual.size());
+ for (PackageInstallerSession expectedSession : expected) {
+ boolean foundSession = false;
+ for (PackageInstallerSession actualSession : actual) {
+ if (expectedSession.sessionId == actualSession.sessionId) {
+ // We should only encounter each expected session once.
+ assertFalse(foundSession);
+ foundSession = true;
+ assertSessionsEquivalent(expectedSession, actualSession);
+ }
+ }
+ assertTrue(foundSession);
+ }
+ }
+
+ private void assertSessionsEquivalent(PackageInstallerSession expected,
+ PackageInstallerSession actual) {
+ assertEquals(expected.sessionId, actual.sessionId);
+ assertEquals(expected.userId, actual.userId);
+ assertSessionParamsEquivalent(expected.params, actual.params);
+ assertEquals(expected.getInstallerUid(), actual.getInstallerUid());
+ assertEquals(expected.stageDir.getAbsolutePath(), actual.stageDir.getAbsolutePath());
+ assertEquals(expected.stageCid, actual.stageCid);
+ assertEquals(expected.isPrepared(), actual.isPrepared());
+ assertEquals(expected.isStaged(), actual.isStaged());
+ assertEquals(expected.isStagedSessionApplied(), actual.isStagedSessionApplied());
+ assertEquals(expected.isStagedSessionFailed(), actual.isStagedSessionFailed());
+ assertEquals(expected.isStagedSessionReady(), actual.isStagedSessionReady());
+ assertEquals(expected.getStagedSessionErrorCode(), actual.getStagedSessionErrorCode());
+ assertEquals(expected.isPrepared(), actual.isPrepared());
+ assertEquals(expected.isSealed(), actual.isSealed());
+ assertEquals(expected.isMultiPackage(), actual.isMultiPackage());
+ assertEquals(expected.hasParentSessionId(), actual.hasParentSessionId());
+ assertEquals(expected.getParentSessionId(), actual.getParentSessionId());
+ assertArrayEquals(expected.getChildSessionIds(), actual.getChildSessionIds());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
index f817e8e33b31..6da202b93065 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
@@ -16,8 +16,6 @@
package com.android.server.pm.dex;
-import static com.android.server.pm.dex.PackageDynamicCodeLoading.FILE_TYPE_DEX;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.atMost;
@@ -26,10 +24,12 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.os.UserHandle;
import android.os.storage.StorageManager;
import androidx.test.filters.SmallTest;
@@ -56,40 +56,44 @@ import org.mockito.stubbing.Stubber;
public class DexLoggerTests {
private static final String OWNING_PACKAGE_NAME = "package.name";
private static final String VOLUME_UUID = "volUuid";
- private static final String DEX_PATH = "/bar/foo.jar";
+ private static final String FILE_PATH = "/bar/foo.jar";
private static final int STORAGE_FLAGS = StorageManager.FLAG_STORAGE_DE;
private static final int OWNER_UID = 43;
private static final int OWNER_USER_ID = 44;
// Obtained via: echo -n "foo.jar" | sha256sum
- private static final String DEX_FILENAME_HASH =
+ private static final String FILENAME_HASH =
"91D7B844D7CC9673748FF057D8DC83972280FC28537D381AA42015A9CF214B9F";
- private static final byte[] CONTENT_HASH_BYTES = new byte[] {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
+ private static final byte[] CONTENT_HASH_BYTES = new byte[]{
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
};
private static final String CONTENT_HASH =
"0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20";
private static final byte[] EMPTY_BYTES = {};
- @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+ private static final String EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH =
+ "dcl:" + FILENAME_HASH;
+ private static final String EXPECTED_MESSAGE_WITH_CONTENT_HASH =
+ EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH + " " + CONTENT_HASH;
+ private static final String EXPECTED_MESSAGE_NATIVE_WITH_CONTENT_HASH =
+ "dcln:" + FILENAME_HASH + " " + CONTENT_HASH;
+
+ @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.LENIENT);
@Mock IPackageManager mPM;
@Mock Installer mInstaller;
- private PackageDynamicCodeLoading mPackageDynamicCodeLoading;
private DexLogger mDexLogger;
private final ListMultimap<Integer, String> mMessagesForUid = ArrayListMultimap.create();
private boolean mWriteTriggered = false;
- private static final String EXPECTED_MESSAGE_WITH_CONTENT_HASH =
- DEX_FILENAME_HASH + " " + CONTENT_HASH;
@Before
public void setup() throws Exception {
// Disable actually attempting to do file writes.
- mPackageDynamicCodeLoading = new PackageDynamicCodeLoading() {
+ PackageDynamicCodeLoading packageDynamicCodeLoading = new PackageDynamicCodeLoading() {
@Override
void maybeWriteAsync() {
mWriteTriggered = true;
@@ -102,13 +106,13 @@ public class DexLoggerTests {
};
// For test purposes capture log messages as well as sending to the event log.
- mDexLogger = new DexLogger(mPM, mInstaller, mPackageDynamicCodeLoading) {
+ mDexLogger = new DexLogger(mPM, mInstaller, packageDynamicCodeLoading) {
@Override
- void writeDclEvent(int uid, String message) {
- super.writeDclEvent(uid, message);
- mMessagesForUid.put(uid, message);
- }
- };
+ void writeDclEvent(String subtag, int uid, String message) {
+ super.writeDclEvent(subtag, uid, message);
+ mMessagesForUid.put(uid, subtag + ":" + message);
+ }
+ };
// Make the owning package exist in our mock PackageManager.
ApplicationInfo appInfo = new ApplicationInfo();
@@ -124,9 +128,9 @@ public class DexLoggerTests {
@Test
public void testOneLoader_ownFile_withFileHash() throws Exception {
- whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+ whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
- recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
@@ -139,13 +143,13 @@ public class DexLoggerTests {
@Test
public void testOneLoader_ownFile_noFileHash() throws Exception {
- whenFileIsHashed(DEX_PATH, doReturn(EMPTY_BYTES));
+ whenFileIsHashed(FILE_PATH, doReturn(EMPTY_BYTES));
- recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
- assertThat(mMessagesForUid).containsEntry(OWNER_UID, DEX_FILENAME_HASH);
+ assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH);
// File should be removed from the DCL list, since we can't hash it.
assertThat(mWriteTriggered).isTrue();
@@ -154,13 +158,14 @@ public class DexLoggerTests {
@Test
public void testOneLoader_ownFile_hashingFails() throws Exception {
- whenFileIsHashed(DEX_PATH, doThrow(new InstallerException("Intentional failure for test")));
+ whenFileIsHashed(FILE_PATH,
+ doThrow(new InstallerException("Intentional failure for test")));
- recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
- assertThat(mMessagesForUid).containsEntry(OWNER_UID, DEX_FILENAME_HASH);
+ assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH);
// File should be removed from the DCL list, since we can't hash it.
assertThat(mWriteTriggered).isTrue();
@@ -178,11 +183,23 @@ public class DexLoggerTests {
}
@Test
+ public void testOneLoader_pathTraversal() throws Exception {
+ String filePath = "/bar/../secret/foo.jar";
+ whenFileIsHashed(filePath, doReturn(CONTENT_HASH_BYTES));
+ setPackageUid(OWNING_PACKAGE_NAME, -1);
+
+ recordLoad(OWNING_PACKAGE_NAME, filePath);
+ mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+
+ assertThat(mMessagesForUid).isEmpty();
+ }
+
+ @Test
public void testOneLoader_differentOwner() throws Exception {
- whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+ whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
setPackageUid("other.package.name", 1001);
- recordLoad("other.package.name", DEX_PATH);
+ recordLoad("other.package.name", FILE_PATH);
mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
assertThat(mMessagesForUid.keys()).containsExactly(1001);
@@ -192,10 +209,10 @@ public class DexLoggerTests {
@Test
public void testOneLoader_differentOwner_uninstalled() throws Exception {
- whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+ whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
setPackageUid("other.package.name", -1);
- recordLoad("other.package.name", DEX_PATH);
+ recordLoad("other.package.name", FILE_PATH);
mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
assertThat(mMessagesForUid).isEmpty();
@@ -203,22 +220,38 @@ public class DexLoggerTests {
}
@Test
+ public void testNativeCodeLoad() throws Exception {
+ whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
+
+ recordLoadNative(FILE_PATH);
+ mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+
+ assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
+ assertThat(mMessagesForUid)
+ .containsEntry(OWNER_UID, EXPECTED_MESSAGE_NATIVE_WITH_CONTENT_HASH);
+
+ assertThat(mWriteTriggered).isFalse();
+ assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading())
+ .containsExactly(OWNING_PACKAGE_NAME);
+ }
+
+ @Test
public void testMultipleLoadersAndFiles() throws Exception {
String otherDexPath = "/bar/nosuchdir/foo.jar";
- whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+ whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES));
whenFileIsHashed(otherDexPath, doReturn(EMPTY_BYTES));
setPackageUid("other.package.name1", 1001);
setPackageUid("other.package.name2", 1002);
- recordLoad("other.package.name1", DEX_PATH);
+ recordLoad("other.package.name1", FILE_PATH);
recordLoad("other.package.name1", otherDexPath);
- recordLoad("other.package.name2", DEX_PATH);
- recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ recordLoad("other.package.name2", FILE_PATH);
+ recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
assertThat(mMessagesForUid.keys()).containsExactly(1001, 1001, 1002, OWNER_UID);
assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
- assertThat(mMessagesForUid).containsEntry(1001, DEX_FILENAME_HASH);
+ assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH);
assertThat(mMessagesForUid).containsEntry(1002, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
@@ -233,7 +266,7 @@ public class DexLoggerTests {
@Test
public void testUnknownOwner() {
reset(mPM);
- recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
mDexLogger.logDynamicCodeLoading("other.package.name");
assertThat(mMessagesForUid).isEmpty();
@@ -244,7 +277,7 @@ public class DexLoggerTests {
@Test
public void testUninstalledPackage() {
reset(mPM);
- recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+ recordLoad(OWNING_PACKAGE_NAME, FILE_PATH);
mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
assertThat(mMessagesForUid).isEmpty();
@@ -262,7 +295,16 @@ public class DexLoggerTests {
}
private void recordLoad(String loadingPackageName, String dexPath) {
- mPackageDynamicCodeLoading.record(
- OWNING_PACKAGE_NAME, dexPath, FILE_TYPE_DEX, OWNER_USER_ID, loadingPackageName);
+ mDexLogger.recordDex(OWNER_USER_ID, dexPath, OWNING_PACKAGE_NAME, loadingPackageName);
+ mWriteTriggered = false;
+ }
+
+ private void recordLoadNative(String nativePath) throws Exception {
+ int loadingUid = UserHandle.getUid(OWNER_USER_ID, OWNER_UID);
+ String[] packageNames = { OWNING_PACKAGE_NAME };
+ when(mPM.getPackagesForUid(loadingUid)).thenReturn(packageNames);
+
+ mDexLogger.recordNative(loadingUid, nativePath);
+ mWriteTriggered = false;
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 94b21af65799..c0f9b80b2e08 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -185,6 +185,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
+
+ private static final int NOTIFICATION_LOCATION_UNKNOWN = 0;
+
@Mock
private NotificationListeners mListeners;
@Mock private NotificationAssistants mAssistants;
@@ -2528,11 +2531,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
- mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true,
+ NOTIFICATION_LOCATION_UNKNOWN);
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((true)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
- mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false);
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false,
+ NOTIFICATION_LOCATION_UNKNOWN);
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(true), eq((false)));
assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
}
@@ -2542,11 +2547,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
- mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true,
+ NOTIFICATION_LOCATION_UNKNOWN);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.sbn), eq(false), eq((true)));
- mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false);
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false,
+ NOTIFICATION_LOCATION_UNKNOWN);
assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
verify(mAssistants).notifyAssistantExpansionChangedLocked(
eq(r.sbn), eq(false), eq((false)));
@@ -3793,7 +3800,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
- mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true,
+ NOTIFICATION_LOCATION_UNKNOWN);
NotificationVisibility[] notificationVisibility = new NotificationVisibility[] {
NotificationVisibility.obtain(r.getKey(), 0, 0, true)
};
@@ -3808,7 +3816,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
- mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true);
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true,
+ NOTIFICATION_LOCATION_UNKNOWN);
assertEquals(0, mService.countLogSmartSuggestionsVisible);
}
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 319ffed3778c..8be63fc43adb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -31,7 +31,6 @@ import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
@@ -76,9 +75,6 @@ public class ActivityRecordTests extends ActivityTestsBase {
mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
mTask = mStack.getChildAt(0);
mActivity = mTask.getTopActivity();
-
- doReturn(false).when(mService).isBooting();
- doReturn(true).when(mService).isBooted();
}
@Test
@@ -121,23 +117,22 @@ public class ActivityRecordTests extends ActivityTestsBase {
mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
- // The activity is in the focused stack so it should be resumed.
+ // The activity is in the focused stack so it should not move to paused.
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
- assertTrue(mActivity.isState(RESUMED));
+ assertTrue(mActivity.isState(STOPPED));
assertFalse(pauseFound.value);
- // Make the activity non focusable
- mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped");
- doReturn(false).when(mActivity).isFocusable();
+ // Clear focused stack
+ final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+ when(display.getFocusedStack()).thenReturn(null);
- // If the activity is not focusable, it should move to paused.
+ // In the unfocused stack, the activity should move to paused.
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
assertTrue(mActivity.isState(PAUSING));
assertTrue(pauseFound.value);
// Make sure that the state does not change for current non-stopping states.
mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped");
- doReturn(true).when(mActivity).isFocusable();
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
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 ea8f33f0c630..68df87e3e27d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -55,7 +55,6 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.Looper;
-import android.os.PowerManager;
import android.os.Process;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
@@ -426,7 +425,6 @@ class ActivityTestsBase {
doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
// allow background activity starts by default
doReturn(true).when(this).isBackgroundActivityStartsEnabled();
- doNothing().when(this).updateCpuStats();
}
void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
@@ -582,8 +580,6 @@ class ActivityTestsBase {
doNothing().when(this).acquireLaunchWakelock();
doReturn(mKeyguardController).when(this).getKeyguardController();
- mLaunchingActivity = mock(PowerManager.WakeLock.class);
-
initialize();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index d0b9225715c4..ea5ab7bf0621 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -70,9 +70,9 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase {
mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
- eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
+ eq(mToken.mSurfaceAnimator.mLeash));
verify(mTransaction).reparent(eq(mToken.mSurfaceAnimator.mLeash),
- eq(mToken.mAnimationBoundsLayer.getHandle()));
+ eq(mToken.mAnimationBoundsLayer));
}
@Test
@@ -111,7 +111,7 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase {
mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
- eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
+ eq(mToken.mSurfaceAnimator.mLeash));
assertThat(mToken.mAnimationBoundsLayer).isNull();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index ad80cd6ddfb7..9b84215a8f3b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -90,7 +90,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase {
final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
OnAnimationFinishedCallback.class);
assertAnimating(mAnimatable);
- verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash.getHandle()));
+ verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash));
verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
callbackCaptor.getValue().onAnimationFinished(mSpec);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index b996bfbf2101..c595868db484 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -19,6 +19,10 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.TaskPositioner.MIN_ASPECT;
import static com.android.server.wm.WindowManagerService.dipToPixel;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
@@ -38,13 +42,12 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.Mockito;
/**
* Tests for the {@link TaskPositioner} class.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:TaskPositionerTests
+ * atest WmTests:TaskPositionerTests
*/
@SmallTest
public class TaskPositionerTests extends WindowTestsBase {
@@ -73,18 +76,17 @@ public class TaskPositionerTests extends WindowTestsBase {
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm);
- mPositioner = new TaskPositioner(mWm, Mockito.mock(IActivityTaskManager.class));
+ mPositioner = new TaskPositioner(mWm, mock(IActivityTaskManager.class));
mPositioner.register(mDisplayContent);
- mWindow = Mockito.spy(createWindow(null, TYPE_BASE_APPLICATION, "window"));
- final Task task = Mockito.spy(mWindow.getTask());
- Mockito.when(mWindow.getTask()).thenReturn(task);
-
- Mockito.doAnswer(invocation -> {
+ mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
+ final Task task = mWindow.getTask();
+ spyOn(task);
+ doAnswer(invocation -> {
final Rect rect = (Rect) invocation.getArguments()[0];
rect.set(mDimBounds);
- return (Void) null;
- }).when(task).getDimBounds(Mockito.any(Rect.class));
+ return null;
+ }).when(task).getDimBounds(any(Rect.class));
mWindow.getStack().setWindowingMode(WINDOWING_MODE_FREEFORM);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
index 946ffb60c759..d29e3fa6f546 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
@@ -53,7 +53,7 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase {
public void setUp() {
final UserManager um = UserManager.get(getInstrumentation().getTargetContext());
mTestUserId = um.getUserHandle();
- mPersister = new TaskSnapshotPersister(userId -> FILES_DIR);
+ mPersister = new TaskSnapshotPersister(mWm, userId -> FILES_DIR);
mLoader = new TaskSnapshotLoader(mPersister);
mPersister.start();
}
diff --git a/startop/OWNERS b/startop/OWNERS
index bfe96d3ed15c..762cd8e48be4 100644
--- a/startop/OWNERS
+++ b/startop/OWNERS
@@ -3,3 +3,4 @@ chriswailes@google.com
eholk@google.com
iam@google.com
sehr@google.com
+mathieuc@google.com
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index a39e885204b7..7d1f8ce75919 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -19,6 +19,7 @@ package android.telecom;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.telecom.Connection.VideoProvider;
@@ -64,6 +65,10 @@ public abstract class Conference extends Conferenceable {
public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
public void onExtrasChanged(Conference c, Bundle extras) {}
public void onExtrasRemoved(Conference c, List<String> keys) {}
+ public void onConferenceStateChanged(Conference c, boolean isConference) {}
+ public void onAddressChanged(Conference c, Uri newAddress, int presentation) {}
+ public void onCallerDisplayNameChanged(
+ Conference c, String callerDisplayName, int presentation) {}
}
private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
@@ -946,6 +951,62 @@ public abstract class Conference extends Conferenceable {
public void onExtrasChanged(Bundle extras) {}
/**
+ * Set whether Telecom should treat this {@link Conference} as a conference call or if it
+ * should treat it as a single-party call.
+ * This method is used as part of a workaround regarding IMS conference calls and user
+ * expectation. In IMS, once a conference is formed, the UE is connected to an IMS conference
+ * server. If all participants of the conference drop out of the conference except for one, the
+ * UE is still connected to the IMS conference server. At this point, the user logically
+ * assumes they're no longer in a conference, yet the underlying network actually is.
+ * To help provide a better user experiece, IMS conference calls can pretend to actually be a
+ * single-party call when the participant count drops to 1. Although the dialer/phone app
+ * could perform this trickery, it makes sense to do this in Telephony since a fix there will
+ * ensure that bluetooth head units, auto and wearable apps all behave consistently.
+ *
+ * @param isConference {@code true} if this {@link Conference} should be treated like a
+ * conference call, {@code false} if it should be treated like a single-party call.
+ * @hide
+ */
+ public void setConferenceState(boolean isConference) {
+ for (Listener l : mListeners) {
+ l.onConferenceStateChanged(this, isConference);
+ }
+ }
+
+ /**
+ * Sets the address of this {@link Conference}. Used when {@link #setConferenceState(boolean)}
+ * is called to mark a conference temporarily as NOT a conference.
+ *
+ * @param address The new address.
+ * @param presentation The presentation requirements for the address.
+ * See {@link TelecomManager} for valid values.
+ * @hide
+ */
+ public final void setAddress(Uri address, int presentation) {
+ Log.d(this, "setAddress %s", address);
+ for (Listener l : mListeners) {
+ l.onAddressChanged(this, address, presentation);
+ }
+ }
+
+ /**
+ * Sets the caller display name (CNAP) of this {@link Conference}. Used when
+ * {@link #setConferenceState(boolean)} is called to mark a conference temporarily as NOT a
+ * conference.
+ *
+ * @param callerDisplayName The new display name.
+ * @param presentation The presentation requirements for the handle.
+ * See {@link TelecomManager} for valid values.
+ * @hide
+ */
+ public final void setCallerDisplayName(String callerDisplayName, int presentation) {
+ Log.d(this, "setCallerDisplayName %s", callerDisplayName);
+ for (Listener l : mListeners) {
+ l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
+ }
+ }
+
+ /**
* Handles a change to extras received from Telecom.
*
* @param extras The new extras.
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 4d5f5e140a87..9bafbe09998e 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1254,6 +1254,31 @@ public abstract class ConnectionService extends Service {
mAdapter.removeExtras(id, keys);
}
}
+
+ @Override
+ public void onConferenceStateChanged(Conference c, boolean isConference) {
+ String id = mIdByConference.get(c);
+ if (id != null) {
+ mAdapter.setConferenceState(id, isConference);
+ }
+ }
+
+ @Override
+ public void onAddressChanged(Conference c, Uri newAddress, int presentation) {
+ String id = mIdByConference.get(c);
+ if (id != null) {
+ mAdapter.setAddress(id, newAddress, presentation);
+ }
+ }
+
+ @Override
+ public void onCallerDisplayNameChanged(Conference c, String callerDisplayName,
+ int presentation) {
+ String id = mIdByConference.get(c);
+ if (id != null) {
+ mAdapter.setCallerDisplayName(id, callerDisplayName, presentation);
+ }
+ }
};
private final Connection.Listener mConnectionListener = new Connection.Listener() {
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 520e7eda6f69..6c3f4f34ff2d 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -653,4 +653,22 @@ final class ConnectionServiceAdapter implements DeathRecipient {
}
}
}
+
+ /**
+ * Sets whether a conference is treated as a conference or a single party call.
+ * See {@link Conference#setConferenceState(boolean)} for more information.
+ *
+ * @param callId The ID of the telecom call.
+ * @param isConference {@code true} if this call should be treated as a conference,
+ * {@code false} otherwise.
+ */
+ void setConferenceState(String callId, boolean isConference) {
+ Log.v(this, "setConferenceState: %s %b", callId, isConference);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setConferenceState(callId, isConference, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 78d65e643abc..f99b218bd9b9 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -74,6 +74,7 @@ final class ConnectionServiceAdapterServant {
private static final int MSG_ON_RTT_UPGRADE_REQUEST = 33;
private static final int MSG_SET_PHONE_ACCOUNT_CHANGED = 34;
private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35;
+ private static final int MSG_SET_CONFERENCE_STATE = 36;
private final IConnectionServiceAdapter mDelegate;
@@ -333,6 +334,14 @@ final class ConnectionServiceAdapterServant {
case MSG_CONNECTION_SERVICE_FOCUS_RELEASED:
mDelegate.onConnectionServiceFocusReleased(null /*Session.Info*/);
break;
+ case MSG_SET_CONFERENCE_STATE:
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setConferenceState((String) args.arg1, (Boolean) args.arg2,
+ (Session.Info) args.arg3);
+ } finally {
+ args.recycle();
+ }
}
}
};
@@ -615,6 +624,16 @@ final class ConnectionServiceAdapterServant {
public void resetConnectionTime(String callId, Session.Info sessionInfo) {
// Do nothing
}
+
+ @Override
+ public void setConferenceState(String callId, boolean isConference,
+ Session.Info sessionInfo) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = isConference;
+ args.arg3 = sessionInfo;
+ mHandler.obtainMessage(MSG_SET_CONFERENCE_STATE, args).sendToTarget();
+ }
};
public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 9821dcbce85e..744544eb01f1 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -471,6 +471,12 @@ final class RemoteConnectionService {
public void resetConnectionTime(String callId, Session.Info sessionInfo) {
// Do nothing
}
+
+ @Override
+ public void setConferenceState(String callId, boolean isConference,
+ Session.Info sessionInfo) {
+ // Do nothing
+ }
};
private final ConnectionServiceAdapterServant mServant =
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 6c4b1af8c2a1..0fe5e080d1f8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -793,15 +793,17 @@ public class TelecomManager {
* <p>
* Apps must be prepared for this method to return {@code null}, indicating that there currently
* exists no user-chosen default {@code PhoneAccount}.
+ * <p>
+ * The default dialer has access to use this method.
*
* @return The user outgoing phone account selected by the user.
- * @hide
*/
- @UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
try {
if (isServiceConnected()) {
- return getTelecomService().getUserSelectedOutgoingPhoneAccount();
+ return getTelecomService().getUserSelectedOutgoingPhoneAccount(
+ mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getUserSelectedOutgoingPhoneAccount", e);
@@ -810,10 +812,14 @@ public class TelecomManager {
}
/**
- * Sets the user-chosen default for making outgoing phone calls.
+ * Sets the user-chosen default {@link PhoneAccountHandle} for making outgoing phone calls.
+ *
+ * @param accountHandle The {@link PhoneAccountHandle} which will be used by default for making
+ * outgoing voice calls.
* @hide
*/
- @UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 0157a5863363..76ac88e9fbaa 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -123,4 +123,6 @@ oneway interface IConnectionServiceAdapter {
void onConnectionServiceFocusReleased(in Session.Info sessionInfo);
void resetConnectionTime(String callIdi, in Session.Info sessionInfo);
+
+ void setConferenceState(String callId, boolean isConference, in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 954a7098f6be..e1d5c17d5e3a 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -45,7 +45,7 @@ interface ITelecomService {
/**
* @see TelecomServiceImpl#getUserSelectedOutgoingPhoneAccount
*/
- PhoneAccountHandle getUserSelectedOutgoingPhoneAccount();
+ PhoneAccountHandle getUserSelectedOutgoingPhoneAccount(String callingPackage);
/**
* @see TelecomServiceImpl#setUserSelectedOutgoingPhoneAccount
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index 26ec6ded8224..ca264f738e1d 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -42,7 +42,7 @@ public final class DataFailCause {
// This series of errors as specified by the standards
// specified in ril.h
- /** Operator determined barring. */
+ /** Operator determined barring. (no retry) */
public static final int OPERATOR_BARRED = 0x08;
/** NAS signalling. */
public static final int NAS_SIGNALLING = 0x0E;
@@ -91,6 +91,11 @@ public final class DataFailCause {
public static final int FILTER_SYTAX_ERROR = 0x2D;
/** Packet Data Protocol (PDP) without active traffic flow template (TFT). */
public static final int PDP_WITHOUT_ACTIVE_TFT = 0x2E;
+ /**
+ * UE requested to modify QoS parameters or the bearer control mode, which is not compatible
+ * with the selected bearer control mode.
+ */
+ public static final int ACTIVATION_REJECTED_BCM_VIOLATION = 0x30;
/** Packet Data Protocol (PDP) type IPv4 only allowed. */
public static final int ONLY_IPV4_ALLOWED = 0x32; /* no retry */
/** Packet Data Protocol (PDP) type IPv6 only allowed. */
@@ -103,6 +108,27 @@ public final class DataFailCause {
public static final int PDN_CONN_DOES_NOT_EXIST = 0x36;
/** Multiple connections to a same PDN is not allowed. */
public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37;
+ /**
+ * Network has already initiated the activation, modification, or deactivation of bearer
+ * resources that was requested by the UE.
+ */
+ public static final int COLLISION_WITH_NETWORK_INITIATED_REQUEST = 0x38;
+ /**
+ * Network supports IPv4v6 PDP type only. Non-IP type is not allowed. In LTE mode of operation,
+ * this is a PDN throttling cause code, meaning the UE may throttle further requests to the
+ * same APN.
+ */
+ public static final int ONLY_IPV4V6_ALLOWED = 0x39;
+ /**
+ * Network supports non-IP PDP type only. IPv4, IPv6 and IPv4v6 is not allowed. In LTE mode of
+ * operation, this is a PDN throttling cause code, meaning the UE can throttle further requests
+ * to the same APN.
+ */
+ public static final int ONLY_NON_IP_ALLOWED = 0x3A;
+ /** QCI (QoS Class Identifier) indicated in the UE request cannot be supported. */
+ public static final int UNSUPPORTED_QCI_VALUE = 0x3B;
+ /** Procedure requested by the UE was rejected because the bearer handling is not supported. */
+ public static final int BEARER_HANDLING_NOT_SUPPORTED = 0x3C;
/** Max number of Packet Data Protocol (PDP) context reached. */
public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 0x41;
/** Unsupported APN in current public land mobile network (PLMN). */
@@ -146,6 +172,742 @@ public final class DataFailCause {
public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 0x79;
/** Authentication failure on emergency call. */
public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 0x7A;
+ /** Not receiving a DNS address that was mandatory. */
+ public static final int INVALID_DNS_ADDR = 0x7B;
+ /** Not receiving either a PCSCF or a DNS address, one of them being mandatory. */
+ public static final int INVALID_PCSCF_OR_DNS_ADDRESS = 0x7C;
+ /** Emergency call bring up on a different ePDG. */
+ public static final int CALL_PREEMPT_BY_EMERGENCY_APN = 0x7F;
+ /** UE performs a detach or disconnect PDN action based on TE requirements. */
+ public static final int UE_INITIATED_DETACH_OR_DISCONNECT = 0x80;
+
+ /** Reason unspecified for foreign agent rejected MIP (Mobile IP) registration. */
+ public static final int MIP_FA_REASON_UNSPECIFIED = 0x7D0;
+ /** Foreign agent administratively prohibited MIP (Mobile IP) registration. */
+ public static final int MIP_FA_ADMIN_PROHIBITED = 0x7D1;
+ /** Foreign agent rejected MIP (Mobile IP) registration because of insufficient resources. */
+ public static final int MIP_FA_INSUFFICIENT_RESOURCES = 0x7D2;
+ /**
+ * Foreign agent rejected MIP (Mobile IP) registration because of MN-AAA authenticator was
+ * wrong.
+ */
+ public static final int MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE = 0x7D3;
+ /**
+ * Foreign agent rejected MIP (Mobile IP) registration because of home agent authentication
+ * failure.
+ */
+ public static final int MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE = 0x7D4;
+ /**
+ * Foreign agent rejected MIP (Mobile IP) registration because of requested lifetime was too
+ * long.
+ */
+ public static final int MIP_FA_REQUESTED_LIFETIME_TOO_LONG = 0x7D5;
+ /** Foreign agent rejected MIP (Mobile IP) registration because of malformed request. */
+ public static final int MIP_FA_MALFORMED_REQUEST = 0x7D6;
+ /** Foreign agent rejected MIP (Mobile IP) registration because of malformed reply. */
+ public static final int MIP_FA_MALFORMED_REPLY = 0x7D7;
+ /**
+ * Foreign agent rejected MIP (Mobile IP) registration because of requested encapsulation was
+ * unavailable.
+ */
+ public static final int MIP_FA_ENCAPSULATION_UNAVAILABLE = 0x7D8;
+ /**
+ * Foreign agent rejected MIP (Mobile IP) registration of VJ Header Compression was
+ * unavailable.
+ */
+ public static final int MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE = 0x7D9;
+ /**
+ * Foreign agent rejected MIP (Mobile IP) registration because of reverse tunnel was
+ * unavailable.
+ */
+ public static final int MIP_FA_REVERSE_TUNNEL_UNAVAILABLE = 0x7DA;
+ /**
+ * Foreign agent rejected MIP (Mobile IP) registration because of reverse tunnel was mandatory
+ * but not requested by device.
+ */
+ public static final int MIP_FA_REVERSE_TUNNEL_IS_MANDATORY = 0x7DB;
+ /**
+ * Foreign agent rejected MIP (Mobile IP) registration because of delivery style was not
+ * supported.
+ */
+ public static final int MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED = 0x7DC;
+ /**
+ * Foreign agent rejected MIP (Mobile IP) registration because of missing NAI (Network Access
+ * Identifier).
+ */
+ public static final int MIP_FA_MISSING_NAI = 0x7DD;
+ /** Foreign agent rejected MIP (Mobile IP) registration because of missing Home Agent. */
+ public static final int MIP_FA_MISSING_HOME_AGENT = 0x7DE;
+ /** Foreign agent rejected MIP (Mobile IP) registration because of missing Home Address. */
+ public static final int MIP_FA_MISSING_HOME_ADDRESS = 0x7DF;
+ /** Foreign agent rejected MIP (Mobile IP) registration because of unknown challenge. */
+ public static final int MIP_FA_UNKNOWN_CHALLENGE = 0x7E0;
+ /** Foreign agent rejected MIP (Mobile IP) registration because of missing challenge. */
+ public static final int MIP_FA_MISSING_CHALLENGE = 0x7E1;
+ /** Foreign agent rejected MIP (Mobile IP) registration because of stale challenge. */
+ public static final int MIP_FA_STALE_CHALLENGE = 0x7E2;
+ /** Reason unspecified for home agent rejected MIP (Mobile IP) registration. */
+ public static final int MIP_HA_REASON_UNSPECIFIED = 0x7E3;
+ /** Home agent administratively prohibited MIP (Mobile IP) registration. */
+ public static final int MIP_HA_ADMIN_PROHIBITED = 0x7E4;
+ /** Home agent rejected MIP (Mobile IP) registration because of insufficient resources. */
+ public static final int MIP_HA_INSUFFICIENT_RESOURCES = 0x7E5;
+ /**
+ * Home agent rejected MIP (Mobile IP) registration because of MN-HA authenticator was
+ * wrong.
+ */
+ public static final int MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE = 0x7E6;
+ /**
+ * Home agent rejected MIP (Mobile IP) registration because of foreign agent authentication
+ * failure.
+ */
+ public static final int MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE = 0x7E7;
+ /** Home agent rejected MIP (Mobile IP) registration because of registration id mismatch. */
+ public static final int MIP_HA_REGISTRATION_ID_MISMATCH = 0x7E8;
+ /** Home agent rejected MIP (Mobile IP) registration because of malformed request. */
+ public static final int MIP_HA_MALFORMED_REQUEST = 0x7E9;
+ /** Home agent rejected MIP (Mobile IP) registration because of unknown home agent address. */
+ public static final int MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS = 0x7EA;
+ /**
+ * Home agent rejected MIP (Mobile IP) registration because of reverse tunnel was
+ * unavailable.
+ */
+ public static final int MIP_HA_REVERSE_TUNNEL_UNAVAILABLE = 0x7EB;
+ /**
+ * Home agent rejected MIP (Mobile IP) registration because of reverse tunnel is mandatory but
+ * not requested by device.
+ */
+ public static final int MIP_HA_REVERSE_TUNNEL_IS_MANDATORY = 0x7EC;
+ /** Home agent rejected MIP (Mobile IP) registration because of encapsulation unavailable. */
+ public static final int MIP_HA_ENCAPSULATION_UNAVAILABLE = 0x7ED;
+ /** Tearing down is in progress. */
+ public static final int CLOSE_IN_PROGRESS = 0x7EE;
+ /** Brought down by the network. */
+ public static final int NETWORK_INITIATED_TERMINATION = 0x7EF;
+ /** Another application in modem preempts the data call. */
+ public static final int MODEM_APP_PREEMPTED = 0x7F0;
+ /**
+ * IPV4 PDN is in throttled state due to network providing only IPV6 address during the
+ * previous VSNCP bringup (subs_limited_to_v6).
+ */
+ public static final int PDN_IPV4_CALL_DISALLOWED = 0x7F1;
+ /** IPV4 PDN is in throttled state due to previous VSNCP bringup failure(s). */
+ public static final int PDN_IPV4_CALL_THROTTLED = 0x7F2;
+ /**
+ * IPV6 PDN is in throttled state due to network providing only IPV4 address during the
+ * previous VSNCP bringup (subs_limited_to_v4).
+ */
+ public static final int PDN_IPV6_CALL_DISALLOWED = 0x7F3;
+ /** IPV6 PDN is in throttled state due to previous VSNCP bringup failure(s). */
+ public static final int PDN_IPV6_CALL_THROTTLED = 0x7F4;
+ /** Modem restart. */
+ public static final int MODEM_RESTART = 0x7F5;
+ /** PDP PPP calls are not supported. */
+ public static final int PDP_PPP_NOT_SUPPORTED = 0x7F6;
+ /** RAT on which the data call is attempted/connected is no longer the preferred RAT. */
+ public static final int UNPREFERRED_RAT = 0x7F7;
+ /** Physical link is in the process of cleanup. */
+ public static final int PHYSICAL_LINK_CLOSE_IN_PROGRESS = 0x7F8;
+ /** Interface bring up is attempted for an APN that is yet to be handed over to target RAT. */
+ public static final int APN_PENDING_HANDOVER = 0x7F9;
+ /** APN bearer type in the profile does not match preferred network mode. */
+ public static final int PROFILE_BEARER_INCOMPATIBLE = 0x7FA;
+ /** Card was refreshed or removed. */
+ public static final int SIM_CARD_CHANGED = 0x7FB;
+ /** Device is going into lower power mode or powering down. */
+ public static final int LOW_POWER_MODE_OR_POWERING_DOWN = 0x7FC;
+ /** APN has been disabled. */
+ public static final int APN_DISABLED = 0x7FD;
+ /** Maximum PPP inactivity timer expired. */
+ public static final int MAX_PPP_INACTIVITY_TIMER_EXPIRED = 0x7FE;
+ /** IPv6 address transfer failed. */
+ public static final int IPV6_ADDRESS_TRANSFER_FAILED = 0x7FF;
+ /** Target RAT swap failed. */
+ public static final int TRAT_SWAP_FAILED = 0x800;
+ /** Device falls back from eHRPD to HRPD. */
+ public static final int EHRPD_TO_HRPD_FALLBACK = 0x801;
+ /**
+ * UE is in MIP-only configuration but the MIP configuration fails on call bring up due to
+ * incorrect provisioning.
+ */
+ public static final int MIP_CONFIG_FAILURE = 0x802;
+ /**
+ * PDN inactivity timer expired due to no data transmission in a configurable duration of time.
+ */
+ public static final int PDN_INACTIVITY_TIMER_EXPIRED = 0x803;
+ /**
+ * IPv4 data call bring up is rejected because the UE already maintains the allotted maximum
+ * number of IPv4 data connections.
+ */
+ public static final int MAX_IPV4_CONNECTIONS = 0x804;
+ /**
+ * IPv6 data call bring up is rejected because the UE already maintains the allotted maximum
+ * number of IPv6 data connections.
+ */
+ public static final int MAX_IPV6_CONNECTIONS = 0x805;
+ /**
+ * New PDN bring up is rejected during interface selection because the UE has already allotted
+ * the available interfaces for other PDNs.
+ */
+ public static final int APN_MISMATCH = 0x806;
+ /**
+ * New call bring up is rejected since the existing data call IP type doesn't match the
+ * requested IP.
+ */
+ public static final int IP_VERSION_MISMATCH = 0x807;
+ /** Dial up networking (DUN) call bring up is rejected since UE is in eHRPD RAT. */
+ public static final int DUN_CALL_DISALLOWED = 0x808;
+ /*** Rejected/Brought down since UE is transition between EPC and NONEPC RAT. */
+ public static final int INTERNAL_EPC_NONEPC_TRANSITION = 0x809;
+ /** The current interface is being in use. */
+ public static final int INTERFACE_IN_USE = 0x80A;
+ /** PDN connection to the APN is disallowed on the roaming network. */
+ public static final int APN_DISALLOWED_ON_ROAMING = 0x80B;
+ /** APN-related parameters are changed. */
+ public static final int APN_PARAMETERS_CHANGED = 0x80C;
+ /** PDN is attempted to be brought up with NULL APN but NULL APN is not supported. */
+ public static final int NULL_APN_DISALLOWED = 0x80D;
+ /**
+ * Thermal level increases and causes calls to be torn down when normal mode of operation is
+ * not allowed.
+ */
+ public static final int THERMAL_MITIGATION = 0x80E;
+ /**
+ * PDN Connection to a given APN is disallowed because data is disabled from the device user
+ * interface settings.
+ */
+ public static final int DATA_SETTINGS_DISABLED = 0x80F;
+ /**
+ * PDN Connection to a given APN is disallowed because data roaming is disabled from the device
+ * user interface settings and the UE is roaming.
+ */
+ public static final int DATA_ROAMING_SETTINGS_DISABLED = 0x810;
+ /** DDS (Default data subscription) switch occurs. */
+ public static final int DDS_SWITCHED = 0x811;
+ /** PDN being brought up with an APN that is part of forbidden APN Name list. */
+ public static final int FORBIDDEN_APN_NAME = 0x812;
+ /** Default data subscription switch is in progress. */
+ public static final int DDS_SWITCH_IN_PROGRESS = 0x813;
+ /** Roaming is disallowed during call bring up. */
+ public static final int CALL_DISALLOWED_IN_ROAMING = 0x814;
+ /**
+ * UE is unable to bring up a non-IP data call because the device is not camped on a NB1 cell.
+ */
+ public static final int NON_IP_NOT_SUPPORTED = 0x815;
+ /** Non-IP PDN is in throttled state due to previous VSNCP bringup failure(s). */
+ public static final int PDN_NON_IP_CALL_THROTTLED = 0x816;
+ /** Non-IP PDN is in disallowed state due to the network providing only an IP address. */
+ public static final int PDN_NON_IP_CALL_DISALLOWED = 0x817;
+ /** Device in CDMA locked state. */
+ public static final int CDMA_LOCK = 0x818;
+ /** Received an intercept order from the base station. */
+ public static final int CDMA_INTERCEPT = 0x819;
+ /** Receiving a reorder from the base station. */
+ public static final int CDMA_REORDER = 0x81A;
+ /** Receiving a release from the base station with a SO (Service Option) Reject reason. */
+ public static final int CDMA_RELEASE_DUE_TO_SO_REJECTION = 0x81B;
+ /** Receiving an incoming call from the base station. */
+ public static final int CDMA_INCOMING_CALL = 0x81C;
+ /** Received an alert stop from the base station due to incoming only. */
+ public static final int CDMA_ALERT_STOP = 0x81D;
+ /**
+ * Channel acquisition failures. This indicates that device has failed acquiring all the
+ * channels in the PRL.
+ */
+ public static final int CHANNEL_ACQUISITION_FAILURE = 0x81E;
+ /** Maximum access probes transmitted. */
+ public static final int MAX_ACCESS_PROBE = 0x81F;
+ /** Concurrent service is not supported by base station. */
+ public static final int CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION = 0x820;
+ /** There was no response received from the base station. */
+ public static final int NO_RESPONSE_FROM_BASE_STATION = 0x821;
+ /** The base station rejecting the call. */
+ public static final int REJECTED_BY_BASE_STATION = 0x822;
+ /** The concurrent services requested were not compatible. */
+ public static final int CONCURRENT_SERVICES_INCOMPATIBLE = 0x823;
+ /** Device does not have CDMA service. */
+ public static final int NO_CDMA_SERVICE = 0x824;
+ /** RUIM not being present. */
+ public static final int RUIM_NOT_PRESENT = 0x825;
+ /** Receiving a retry order from the base station. */
+ public static final int CDMA_RETRY_ORDER = 0x826;
+ /** Access blocked by the base station. */
+ public static final int ACCESS_BLOCK = 0x827;
+ /** Access blocked by the base station for all mobile devices. */
+ public static final int ACCESS_BLOCK_ALL = 0x828;
+ /** Maximum access probes for the IS-707B call. */
+ public static final int IS707B_MAX_ACCESS_PROBES = 0x829;
+ /** Put device in thermal emergency. */
+ public static final int THERMAL_EMERGENCY = 0x82A;
+ /** In favor of a voice call or SMS when concurrent voice and data are not supported. */
+ public static final int CONCURRENT_SERVICES_NOT_ALLOWED = 0x82B;
+ /** The other clients rejected incoming call. */
+ public static final int INCOMING_CALL_REJECTED = 0x82C;
+ /** No service on the gateway. */
+ public static final int NO_SERVICE_ON_GATEWAY = 0x82D;
+ /** GPRS context is not available. */
+ public static final int NO_GPRS_CONTEXT = 0x82E;
+ /**
+ * Network refuses service to the MS because either an identity of the MS is not acceptable to
+ * the network or the MS does not pass the authentication check.
+ */
+ public static final int ILLEGAL_MS = 0x82F;
+ /** ME could not be authenticated and the ME used is not acceptable to the network. */
+ public static final int ILLEGAL_ME = 0x830;
+ /** Not allowed to operate either GPRS or non-GPRS services. */
+ public static final int GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED = 0x831;
+ /** MS is not allowed to operate GPRS services. */
+ public static final int GPRS_SERVICES_NOT_ALLOWED = 0x832;
+ /** No matching identity or context could be found in the network. */
+ public static final int MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK = 0x833;
+ /**
+ * Mobile reachable timer has expired, or the GMM context data related to the subscription does
+ * not exist in the SGSN.
+ */
+ public static final int IMPLICITLY_DETACHED = 0x834;
+ /**
+ * UE requests GPRS service, or the network initiates a detach request in a PLMN which does not
+ * offer roaming for GPRS services to that MS.
+ */
+ public static final int PLMN_NOT_ALLOWED = 0x835;
+ /**
+ * MS requests service, or the network initiates a detach request, in a location area where the
+ * HPLMN determines that the MS, by subscription, is not allowed to operate.
+ */
+ public static final int LOCATION_AREA_NOT_ALLOWED = 0x836;
+ /**
+ * UE requests GPRS service or the network initiates a detach request in a PLMN that does not
+ * offer roaming for GPRS services.
+ */
+ public static final int GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN = 0x837;
+ /** PDP context already exists. */
+ public static final int PDP_DUPLICATE = 0x838;
+ /** RAT change on the UE. */
+ public static final int UE_RAT_CHANGE = 0x839;
+ /** Network cannot serve a request from the MS due to congestion. */
+ public static final int CONGESTION = 0x83A;
+ /**
+ * MS requests an establishment of the radio access bearers for all active PDP contexts by
+ * sending a service request message indicating data to the network, but the SGSN does not have
+ * any active PDP context.
+ */
+ public static final int NO_PDP_CONTEXT_ACTIVATED = 0x83B;
+ /** Access class blocking restrictions for the current camped cell. */
+ public static final int ACCESS_CLASS_DSAC_REJECTION = 0x83C;
+ /** SM attempts PDP activation for a maximum of four attempts. */
+ public static final int PDP_ACTIVATE_MAX_RETRY_FAILED = 0x83D;
+ /** Radio access bearer failure. */
+ public static final int RADIO_ACCESS_BEARER_FAILURE = 0x83E;
+ /** Invalid EPS bearer identity in the request. */
+ public static final int ESM_UNKNOWN_EPS_BEARER_CONTEXT = 0x83F;
+ /** Data radio bearer is released by the RRC. */
+ public static final int DRB_RELEASED_BY_RRC = 0x840;
+ /** Indicate the connection was released. */
+ public static final int CONNECTION_RELEASED = 0x841;
+ /** UE is detached. */
+ public static final int EMM_DETACHED = 0x842;
+ /** Attach procedure is rejected by the network. */
+ public static final int EMM_ATTACH_FAILED = 0x843;
+ /** Attach procedure is started for EMC purposes. */
+ public static final int EMM_ATTACH_STARTED = 0x844;
+ /** Service request procedure failure. */
+ public static final int LTE_NAS_SERVICE_REQUEST_FAILED = 0x845;
+ /** Active dedicated bearer was requested using the same default bearer ID. */
+ public static final int DUPLICATE_BEARER_ID = 0x846;
+ /** Collision scenarios for the UE and network-initiated procedures. */
+ public static final int ESM_COLLISION_SCENARIOS = 0x847;
+ /** Bearer must be deactivated to synchronize with the network. */
+ public static final int ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK = 0x848;
+ /** Active dedicated bearer was requested for an existing default bearer. */
+ public static final int ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER = 0x849;
+ /** Bad OTA message is received from the network. */
+ public static final int ESM_BAD_OTA_MESSAGE = 0x84A;
+ /** Download server rejected the call. */
+ public static final int ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL = 0x84B;
+ /** PDN was disconnected by the downlaod server due to IRAT. */
+ public static final int ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT = 0x84C;
+ /** Dedicated bearer will be deactivated regardless of the network response. */
+ public static final int DS_EXPLICIT_DEACTIVATION = 0x84D;
+ /** No specific local cause is mentioned, usually a valid OTA cause. */
+ public static final int ESM_LOCAL_CAUSE_NONE = 0x84E;
+ /** Throttling is not needed for this service request failure. */
+ public static final int LTE_THROTTLING_NOT_REQUIRED = 0x84F;
+ /** Access control list check failure at the lower layer. */
+ public static final int ACCESS_CONTROL_LIST_CHECK_FAILURE = 0x850;
+ /** Service is not allowed on the requested PLMN. */
+ public static final int SERVICE_NOT_ALLOWED_ON_PLMN = 0x851;
+ /** T3417 timer expiration of the service request procedure. */
+ public static final int EMM_T3417_EXPIRED = 0x852;
+ /** Extended service request fails due to expiration of the T3417 EXT timer. */
+ public static final int EMM_T3417_EXT_EXPIRED = 0x853;
+ /** Transmission failure of radio resource control (RRC) uplink data. */
+ public static final int RRC_UPLINK_DATA_TRANSMISSION_FAILURE = 0x854;
+ /** Radio resource control (RRC) uplink data delivery failed due to a handover. */
+ public static final int RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER = 0x855;
+ /** Radio resource control (RRC) uplink data delivery failed due to a connection release. */
+ public static final int RRC_UPLINK_CONNECTION_RELEASE = 0x856;
+ /** Radio resource control (RRC) uplink data delivery failed due to a radio link failure. */
+ public static final int RRC_UPLINK_RADIO_LINK_FAILURE = 0x857;
+ /**
+ * Radio resource control (RRC) is not connected but the non-access stratum (NAS) sends an
+ * uplink data request.
+ */
+ public static final int RRC_UPLINK_ERROR_REQUEST_FROM_NAS = 0x858;
+ /** Radio resource control (RRC) connection failure at access stratum. */
+ public static final int RRC_CONNECTION_ACCESS_STRATUM_FAILURE = 0x859;
+ /**
+ * Radio resource control (RRC) connection establishment is aborted due to another procedure.
+ */
+ public static final int RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS = 0x85A;
+ /** Radio resource control (RRC) connection establishment failed due to access barrred. */
+ public static final int RRC_CONNECTION_ACCESS_BARRED = 0x85B;
+ /**
+ * Radio resource control (RRC) connection establishment failed due to cell reselection at
+ * access stratum.
+ */
+ public static final int RRC_CONNECTION_CELL_RESELECTION = 0x85C;
+ /**
+ * Connection establishment failed due to configuration failure at the radio resource control
+ * (RRC).
+ */
+ public static final int RRC_CONNECTION_CONFIG_FAILURE = 0x85D;
+ /** Radio resource control (RRC) connection could not be established in the time limit. */
+ public static final int RRC_CONNECTION_TIMER_EXPIRED = 0x85E;
+ /**
+ * Connection establishment failed due to a link failure at the radio resource control (RRC).
+ */
+ public static final int RRC_CONNECTION_LINK_FAILURE = 0x85F;
+ /**
+ * Connection establishment failed as the radio resource control (RRC) is not camped on any
+ * cell.
+ */
+ public static final int RRC_CONNECTION_CELL_NOT_CAMPED = 0x860;
+ /**
+ * Connection establishment failed due to a service interval failure at the radio resource
+ * control (RRC).
+ */
+ public static final int RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE = 0x861;
+ /**
+ * Radio resource control (RRC) connection establishment failed due to the network rejecting
+ * the UE connection request.
+ */
+ public static final int RRC_CONNECTION_REJECT_BY_NETWORK = 0x862;
+ /** Normal radio resource control (RRC) connection release. */
+ public static final int RRC_CONNECTION_NORMAL_RELEASE = 0x863;
+ /**
+ * Radio resource control (RRC) connection release failed due to radio link failure conditions.
+ */
+ public static final int RRC_CONNECTION_RADIO_LINK_FAILURE = 0x864;
+ /** Radio resource control (RRC) connection re-establishment failure. */
+ public static final int RRC_CONNECTION_REESTABLISHMENT_FAILURE = 0x865;
+ /** UE is out of service during the call register. */
+ public static final int RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER = 0x866;
+ /**
+ * Connection has been released by the radio resource control (RRC) due to an abort request.
+ */
+ public static final int RRC_CONNECTION_ABORT_REQUEST = 0x867;
+ /**
+ * Radio resource control (RRC) connection released due to a system information block read
+ * error.
+ */
+ public static final int RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR = 0x868;
+ /** Network-initiated detach with reattach. */
+ public static final int NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH = 0x869;
+ /** Network-initiated detach without reattach. */
+ public static final int NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH = 0x86A;
+ /** ESM procedure maximum attempt timeout failure. */
+ public static final int ESM_PROCEDURE_TIME_OUT = 0x86B;
+ /**
+ * No PDP exists with the given connection ID while modifying or deactivating or activation for
+ * an already active PDP.
+ */
+ public static final int INVALID_CONNECTION_ID = 0x86C;
+ /** Maximum NSAPIs have been exceeded during PDP activation. */
+ public static final int MAXIMIUM_NSAPIS_EXCEEDED = 0x86D;
+ /** Primary context for NSAPI does not exist. */
+ public static final int INVALID_PRIMARY_NSAPI = 0x86E;
+ /** Unable to encode the OTA message for MT PDP or deactivate PDP. */
+ public static final int CANNOT_ENCODE_OTA_MESSAGE = 0x86F;
+ /**
+ * Radio access bearer is not established by the lower layers during activation, modification,
+ * or deactivation.
+ */
+ public static final int RADIO_ACCESS_BEARER_SETUP_FAILURE = 0x870;
+ /** Expiration of the PDP establish timer with a maximum of five retries. */
+ public static final int PDP_ESTABLISH_TIMEOUT_EXPIRED = 0x871;
+ /** Expiration of the PDP modify timer with a maximum of four retries. */
+ public static final int PDP_MODIFY_TIMEOUT_EXPIRED = 0x872;
+ /** Expiration of the PDP deactivate timer with a maximum of four retries. */
+ public static final int PDP_INACTIVE_TIMEOUT_EXPIRED = 0x873;
+ /** PDP activation failed due to RRC_ABORT or a forbidden PLMN. */
+ public static final int PDP_LOWERLAYER_ERROR = 0x874;
+ /** MO PDP modify collision when the MT PDP is already in progress. */
+ public static final int PDP_MODIFY_COLLISION = 0x875;
+ /** Maximum size of the L2 message was exceeded. */
+ public static final int MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED = 0x876;
+ /** Non-access stratum (NAS) request was rejected by the network. */
+ public static final int NAS_REQUEST_REJECTED_BY_NETWORK = 0x877;
+ /**
+ * Radio resource control (RRC) connection establishment failure due to an error in the request
+ * message.
+ */
+ public static final int RRC_CONNECTION_INVALID_REQUEST = 0x878;
+ /**
+ * Radio resource control (RRC) connection establishment failure due to a change in the
+ * tracking area ID.
+ */
+ public static final int RRC_CONNECTION_TRACKING_AREA_ID_CHANGED = 0x879;
+ /**
+ * Radio resource control (RRC) connection establishment failure due to the RF was unavailable.
+ */
+ public static final int RRC_CONNECTION_RF_UNAVAILABLE = 0x87A;
+ /**
+ * Radio resource control (RRC) connection was aborted before deactivating the LTE stack due to
+ * a successful LTE to WCDMA/GSM/TD-SCDMA IRAT change.
+ */
+ public static final int RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE = 0x87B;
+ /**
+ * If the UE has an LTE radio link failure before security is established, the radio resource
+ * control (RRC) connection must be released and the UE must return to idle.
+ */
+ public static final int RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE = 0x87C;
+ /**
+ * Radio resource control (RRC) connection was aborted by the non-access stratum (NAS) after an
+ * IRAT to LTE IRAT handover.
+ */
+ public static final int RRC_CONNECTION_ABORTED_AFTER_HANDOVER = 0x87D;
+ /**
+ * Radio resource control (RRC) connection was aborted before deactivating the LTE stack after
+ * a successful LTE to GSM/EDGE IRAT cell change order procedure.
+ */
+ public static final int RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE = 0x87E;
+ /**
+ * Radio resource control (RRC) connection was aborted in the middle of a LTE to GSM IRAT cell
+ * change order procedure.
+ */
+ public static final int RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE = 0x87F;
+ /** IMSI present in the UE is unknown in the home subscriber server. */
+ public static final int IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER = 0x880;
+ /** IMEI of the UE is not accepted by the network. */
+ public static final int IMEI_NOT_ACCEPTED = 0x881;
+ /** EPS and non-EPS services are not allowed by the network. */
+ public static final int EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED = 0x882;
+ /** EPS services are not allowed in the PLMN. */
+ public static final int EPS_SERVICES_NOT_ALLOWED_IN_PLMN = 0x883;
+ /** Mobile switching center is temporarily unreachable. */
+ public static final int MSC_TEMPORARILY_NOT_REACHABLE = 0x884;
+ /** CS domain is not available. */
+ public static final int CS_DOMAIN_NOT_AVAILABLE = 0x885;
+ /** ESM level failure. */
+ public static final int ESM_FAILURE = 0x886;
+ /** MAC level failure. */
+ public static final int MAC_FAILURE = 0x887;
+ /** Synchronization failure. */
+ public static final int SYNCHRONIZATION_FAILURE = 0x888;
+ /** UE security capabilities mismatch. */
+ public static final int UE_SECURITY_CAPABILITIES_MISMATCH = 0x889;
+ /** Unspecified security mode reject. */
+ public static final int SECURITY_MODE_REJECTED = 0x88A;
+ /** Unacceptable non-EPS authentication. */
+ public static final int UNACCEPTABLE_NON_EPS_AUTHENTICATION = 0x88B;
+ /** CS fallback call establishment is not allowed. */
+ public static final int CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED = 0x88C;
+ /** No EPS bearer context was activated. */
+ public static final int NO_EPS_BEARER_CONTEXT_ACTIVATED = 0x88D;
+ /** Invalid EMM state. */
+ public static final int INVALID_EMM_STATE = 0x88E;
+ /** Non-Access Spectrum layer failure. */
+ public static final int NAS_LAYER_FAILURE = 0x88F;
+ /** Multiple PDP call feature is disabled. */
+ public static final int MULTIPLE_PDP_CALL_NOT_ALLOWED = 0x890;
+ /** Data call has been brought down because EMBMS is not enabled at the RRC layer. */
+ public static final int EMBMS_NOT_ENABLED = 0x891;
+ /** Data call was unsuccessfully transferred during the IRAT handover. */
+ public static final int IRAT_HANDOVER_FAILED = 0x892;
+ /** EMBMS data call has been successfully brought down. */
+ public static final int EMBMS_REGULAR_DEACTIVATION = 0x893;
+ /** Test loop-back data call has been successfully brought down. */
+ public static final int TEST_LOOPBACK_REGULAR_DEACTIVATION = 0x894;
+ /** Lower layer registration failure. */
+ public static final int LOWER_LAYER_REGISTRATION_FAILURE = 0x895;
+ /**
+ * Network initiates a detach on LTE with error cause ""data plan has been replenished or has
+ * expired.
+ */
+ public static final int DATA_PLAN_EXPIRED = 0x896;
+ /** UMTS interface is brought down due to handover from UMTS to iWLAN. */
+ public static final int UMTS_HANDOVER_TO_IWLAN = 0x897;
+ /** Received a connection deny due to general or network busy on EVDO network. */
+ public static final int EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY = 0x898;
+ /** Received a connection deny due to billing or authentication failure on EVDO network. */
+ public static final int EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE = 0x899;
+ /** HDR system has been changed due to redirection or the PRL was not preferred. */
+ public static final int EVDO_HDR_CHANGED = 0x89A;
+ /** Device exited HDR due to redirection or the PRL was not preferred. */
+ public static final int EVDO_HDR_EXITED = 0x89B;
+ /** Device does not have an HDR session. */
+ public static final int EVDO_HDR_NO_SESSION = 0x89C;
+ /** It is ending an HDR call origination in favor of a GPS fix. */
+ public static final int EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL = 0x89D;
+ /** Connection setup on the HDR system was time out. */
+ public static final int EVDO_HDR_CONNECTION_SETUP_TIMEOUT = 0x89E;
+ /** Device failed to acquire a co-located HDR for origination. */
+ public static final int FAILED_TO_ACQUIRE_COLOCATED_HDR = 0x89F;
+ /** OTASP commit is in progress. */
+ public static final int OTASP_COMMIT_IN_PROGRESS = 0x8A0;
+ /** Device has no hybrid HDR service. */
+ public static final int NO_HYBRID_HDR_SERVICE = 0x8A1;
+ /** HDR module could not be obtained because of the RF locked. */
+ public static final int HDR_NO_LOCK_GRANTED = 0x8A2;
+ /** DBM or SMS is in progress. */
+ public static final int DBM_OR_SMS_IN_PROGRESS = 0x8A3;
+ /** HDR module released the call due to fade. */
+ public static final int HDR_FADE = 0x8A4;
+ /** HDR system access failure. */
+ public static final int HDR_ACCESS_FAILURE = 0x8A5;
+ /**
+ * P_rev supported by 1 base station is less than 6, which is not supported for a 1X data call.
+ * The UE must be in the footprint of BS which has p_rev >= 6 to support this SO33 call.
+ */
+ public static final int UNSUPPORTED_1X_PREV = 0x8A6;
+ /** Client ended the data call. */
+ public static final int LOCAL_END = 0x8A7;
+ /** Device has no service. */
+ public static final int NO_SERVICE = 0x8A8;
+ /** Device lost the system due to fade. */
+ public static final int FADE = 0x8A9;
+ /** Receiving a release from the base station with no reason. */
+ public static final int NORMAL_RELEASE = 0x8AA;
+ /** Access attempt is already in progress. */
+ public static final int ACCESS_ATTEMPT_ALREADY_IN_PROGRESS = 0x8AB;
+ /** Device is in the process of redirecting or handing off to a different target system. */
+ public static final int REDIRECTION_OR_HANDOFF_IN_PROGRESS = 0x8AC;
+ /** Device is operating in Emergency mode. */
+ public static final int EMERGENCY_MODE = 0x8AD;
+ /** Device is in use (e.g., voice call). */
+ public static final int PHONE_IN_USE = 0x8AE;
+ /**
+ * Device operational mode is different from the mode requested in the traffic channel bring up.
+ */
+ public static final int INVALID_MODE = 0x8AF;
+ /** SIM was marked by the network as invalid for the circuit and/or packet service domain. */
+ public static final int INVALID_SIM_STATE = 0x8B0;
+ /** There is no co-located HDR. */
+ public static final int NO_COLLOCATED_HDR = 0x8B1;
+ /** UE is entering power save mode. */
+ public static final int UE_IS_ENTERING_POWERSAVE_MODE = 0x8B2;
+ /** Dual switch from single standby to dual standby is in progress. */
+ public static final int DUAL_SWITCH = 0x8B3;
+ /**
+ * Data call bring up fails in the PPP setup due to a timeout.
+ * (e.g., an LCP conf ack was not received from the network)
+ */
+ public static final int PPP_TIMEOUT = 0x8B4;
+ /**
+ * Data call bring up fails in the PPP setup due to an authorization failure.
+ * (e.g., authorization is required, but not negotiated with the network during an LCP phase)
+ */
+ public static final int PPP_AUTH_FAILURE = 0x8B5;
+ /** Data call bring up fails in the PPP setup due to an option mismatch. */
+ public static final int PPP_OPTION_MISMATCH = 0x8B6;
+ /** Data call bring up fails in the PPP setup due to a PAP failure. */
+ public static final int PPP_PAP_FAILURE = 0x8B7;
+ /** Data call bring up fails in the PPP setup due to a CHAP failure. */
+ public static final int PPP_CHAP_FAILURE = 0x8B8;
+ /**
+ * Data call bring up fails in the PPP setup because the PPP is in the process of cleaning the
+ * previous PPP session.
+ */
+ public static final int PPP_CLOSE_IN_PROGRESS = 0x8B9;
+ /**
+ * IPv6 interface bring up fails because the network provided only the IPv4 address for the
+ * upcoming PDN permanent client can reattempt a IPv6 call bring up after the IPv4 interface is
+ * also brought down. However, there is no guarantee that the network will provide a IPv6
+ * address.
+ */
+ public static final int LIMITED_TO_IPV4 = 0x8BA;
+ /**
+ * IPv4 interface bring up fails because the network provided only the IPv6 address for the
+ * upcoming PDN permanent client can reattempt a IPv4 call bring up after the IPv6 interface is
+ * also brought down. However there is no guarantee that the network will provide a IPv4
+ * address.
+ */
+ public static final int LIMITED_TO_IPV6 = 0x8BB;
+ /** Data call bring up fails in the VSNCP phase due to a VSNCP timeout error. */
+ public static final int VSNCP_TIMEOUT = 0x8BC;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a general error. It's used when there is
+ * no other specific error code available to report the failure.
+ */
+ public static final int VSNCP_GEN_ERROR = 0x8BD;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request because the requested APN is unauthorized.
+ */
+ public static final int VSNCP_APN_UNATHORIZED = 0x8BE;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request because the PDN limit has been exceeded.
+ */
+ public static final int VSNCP_PDN_LIMIT_EXCEEDED = 0x8BF;
+ /**
+ * Data call bring up fails in the VSNCP phase due to the network rejected the VSNCP
+ * configuration request due to no PDN gateway address.
+ */
+ public static final int VSNCP_NO_PDN_GATEWAY_ADDRESS = 0x8C0;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request because the PDN gateway is unreachable.
+ */
+ public static final int VSNCP_PDN_GATEWAY_UNREACHABLE = 0x8C1;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request due to a PDN gateway reject.
+ */
+ public static final int VSNCP_PDN_GATEWAY_REJECT = 0x8C2;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request with the reason of insufficient parameter.
+ */
+ public static final int VSNCP_INSUFFICIENT_PARAMETERS = 0x8C3;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request with the reason of resource unavailable.
+ */
+ public static final int VSNCP_RESOURCE_UNAVAILABLE = 0x8C4;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request with the reason of administratively prohibited at the HSGW.
+ */
+ public static final int VSNCP_ADMINISTRATIVELY_PROHIBITED = 0x8C5;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of PDN ID in use, or
+ * all existing PDNs are brought down with this end reason because one of the PDN bring up was
+ * rejected by the network with the reason of PDN ID in use.
+ */
+ public static final int VSNCP_PDN_ID_IN_USE = 0x8C6;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request for the reason of subscriber limitation.
+ */
+ public static final int VSNCP_SUBSCRIBER_LIMITATION = 0x8C7;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request because the PDN exists for this APN.
+ */
+ public static final int VSNCP_PDN_EXISTS_FOR_THIS_APN = 0x8C8;
+ /**
+ * Data call bring up fails in the VSNCP phase due to a network rejection of the VSNCP
+ * configuration request with reconnect to this PDN not allowed, or an active data call is
+ * terminated by the network because reconnection to this PDN is not allowed. Upon receiving
+ * this error code from the network, the modem infinitely throttles the PDN until the next
+ * power cycle.
+ */
+ public static final int VSNCP_RECONNECT_NOT_ALLOWED = 0x8C9;
+ /** Device failure to obtain the prefix from the network. */
+ public static final int IPV6_PREFIX_UNAVAILABLE = 0x8CA;
+ /** System preference change back to SRAT during handoff */
+ public static final int HANDOFF_PREFERENCE_CHANGED = 0x8CB;
// OEM sepecific error codes. To be used by OEMs when they don't
// want to reveal error code which would be replaced by ERROR_UNSPECIFIED
@@ -199,6 +961,13 @@ public final class DataFailCause {
/** @hide */
public static final int RESET_BY_FRAMEWORK = 0x10005;
+ /**
+ * Data handover failed.
+ *
+ * @hide
+ */
+ public static final int HANDOVER_FAILED = 0x10006;
+
/** @hide */
@IntDef(value = {
NONE,
@@ -226,12 +995,18 @@ public final class DataFailCause {
FILTER_SEMANTIC_ERROR,
FILTER_SYTAX_ERROR,
PDP_WITHOUT_ACTIVE_TFT,
+ ACTIVATION_REJECTED_BCM_VIOLATION,
ONLY_IPV4_ALLOWED,
ONLY_IPV6_ALLOWED,
ONLY_SINGLE_BEARER_ALLOWED,
ESM_INFO_NOT_RECEIVED,
PDN_CONN_DOES_NOT_EXIST,
MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
+ COLLISION_WITH_NETWORK_INITIATED_REQUEST,
+ ONLY_IPV4V6_ALLOWED,
+ ONLY_NON_IP_ALLOWED,
+ UNSUPPORTED_QCI_VALUE,
+ BEARER_HANDLING_NOT_SUPPORTED,
ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED,
UNSUPPORTED_APN_IN_CURRENT_PLMN,
INVALID_TRANSACTION_ID,
@@ -242,7 +1017,7 @@ public final class DataFailCause {
UNKNOWN_INFO_ELEMENT,
CONDITIONAL_IE_ERROR,
MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE,
- PROTOCOL_ERRORS, /* no retry */
+ PROTOCOL_ERRORS,
APN_TYPE_CONFLICT,
INVALID_PCSCF_ADDR,
INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN,
@@ -254,6 +1029,262 @@ public final class DataFailCause {
IFACE_AND_POL_FAMILY_MISMATCH,
EMM_ACCESS_BARRED_INFINITE_RETRY,
AUTH_FAILURE_ON_EMERGENCY_CALL,
+ INVALID_DNS_ADDR,
+ INVALID_PCSCF_OR_DNS_ADDRESS,
+ CALL_PREEMPT_BY_EMERGENCY_APN,
+ UE_INITIATED_DETACH_OR_DISCONNECT,
+ MIP_FA_REASON_UNSPECIFIED,
+ MIP_FA_ADMIN_PROHIBITED,
+ MIP_FA_INSUFFICIENT_RESOURCES,
+ MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE,
+ MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE,
+ MIP_FA_REQUESTED_LIFETIME_TOO_LONG,
+ MIP_FA_MALFORMED_REQUEST,
+ MIP_FA_MALFORMED_REPLY,
+ MIP_FA_ENCAPSULATION_UNAVAILABLE,
+ MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE,
+ MIP_FA_REVERSE_TUNNEL_UNAVAILABLE,
+ MIP_FA_REVERSE_TUNNEL_IS_MANDATORY,
+ MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED,
+ MIP_FA_MISSING_NAI,
+ MIP_FA_MISSING_HOME_AGENT,
+ MIP_FA_MISSING_HOME_ADDRESS,
+ MIP_FA_UNKNOWN_CHALLENGE,
+ MIP_FA_MISSING_CHALLENGE,
+ MIP_FA_STALE_CHALLENGE,
+ MIP_HA_REASON_UNSPECIFIED,
+ MIP_HA_ADMIN_PROHIBITED,
+ MIP_HA_INSUFFICIENT_RESOURCES,
+ MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE,
+ MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE,
+ MIP_HA_REGISTRATION_ID_MISMATCH,
+ MIP_HA_MALFORMED_REQUEST,
+ MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS,
+ MIP_HA_REVERSE_TUNNEL_UNAVAILABLE,
+ MIP_HA_REVERSE_TUNNEL_IS_MANDATORY,
+ MIP_HA_ENCAPSULATION_UNAVAILABLE,
+ CLOSE_IN_PROGRESS,
+ NETWORK_INITIATED_TERMINATION,
+ MODEM_APP_PREEMPTED,
+ PDN_IPV4_CALL_DISALLOWED,
+ PDN_IPV4_CALL_THROTTLED,
+ PDN_IPV6_CALL_DISALLOWED,
+ PDN_IPV6_CALL_THROTTLED,
+ MODEM_RESTART,
+ PDP_PPP_NOT_SUPPORTED,
+ UNPREFERRED_RAT,
+ PHYSICAL_LINK_CLOSE_IN_PROGRESS,
+ APN_PENDING_HANDOVER,
+ PROFILE_BEARER_INCOMPATIBLE,
+ SIM_CARD_CHANGED,
+ LOW_POWER_MODE_OR_POWERING_DOWN,
+ APN_DISABLED,
+ MAX_PPP_INACTIVITY_TIMER_EXPIRED,
+ IPV6_ADDRESS_TRANSFER_FAILED,
+ TRAT_SWAP_FAILED,
+ EHRPD_TO_HRPD_FALLBACK,
+ MIP_CONFIG_FAILURE,
+ PDN_INACTIVITY_TIMER_EXPIRED,
+ MAX_IPV4_CONNECTIONS,
+ MAX_IPV6_CONNECTIONS,
+ APN_MISMATCH,
+ IP_VERSION_MISMATCH,
+ DUN_CALL_DISALLOWED,
+ INTERNAL_EPC_NONEPC_TRANSITION,
+ INTERFACE_IN_USE,
+ APN_DISALLOWED_ON_ROAMING,
+ APN_PARAMETERS_CHANGED,
+ NULL_APN_DISALLOWED,
+ THERMAL_MITIGATION,
+ DATA_SETTINGS_DISABLED,
+ DATA_ROAMING_SETTINGS_DISABLED,
+ DDS_SWITCHED,
+ FORBIDDEN_APN_NAME,
+ DDS_SWITCH_IN_PROGRESS,
+ CALL_DISALLOWED_IN_ROAMING,
+ NON_IP_NOT_SUPPORTED,
+ PDN_NON_IP_CALL_THROTTLED,
+ PDN_NON_IP_CALL_DISALLOWED,
+ CDMA_LOCK,
+ CDMA_INTERCEPT,
+ CDMA_REORDER,
+ CDMA_RELEASE_DUE_TO_SO_REJECTION,
+ CDMA_INCOMING_CALL,
+ CDMA_ALERT_STOP,
+ CHANNEL_ACQUISITION_FAILURE,
+ MAX_ACCESS_PROBE,
+ CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION,
+ NO_RESPONSE_FROM_BASE_STATION,
+ REJECTED_BY_BASE_STATION,
+ CONCURRENT_SERVICES_INCOMPATIBLE,
+ NO_CDMA_SERVICE,
+ RUIM_NOT_PRESENT,
+ CDMA_RETRY_ORDER,
+ ACCESS_BLOCK,
+ ACCESS_BLOCK_ALL,
+ IS707B_MAX_ACCESS_PROBES,
+ THERMAL_EMERGENCY,
+ CONCURRENT_SERVICES_NOT_ALLOWED,
+ INCOMING_CALL_REJECTED,
+ NO_SERVICE_ON_GATEWAY,
+ NO_GPRS_CONTEXT,
+ ILLEGAL_MS,
+ ILLEGAL_ME,
+ GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED,
+ GPRS_SERVICES_NOT_ALLOWED,
+ MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK,
+ IMPLICITLY_DETACHED,
+ PLMN_NOT_ALLOWED,
+ LOCATION_AREA_NOT_ALLOWED,
+ GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN,
+ PDP_DUPLICATE,
+ UE_RAT_CHANGE,
+ CONGESTION,
+ NO_PDP_CONTEXT_ACTIVATED,
+ ACCESS_CLASS_DSAC_REJECTION,
+ PDP_ACTIVATE_MAX_RETRY_FAILED,
+ RADIO_ACCESS_BEARER_FAILURE,
+ ESM_UNKNOWN_EPS_BEARER_CONTEXT,
+ DRB_RELEASED_BY_RRC,
+ CONNECTION_RELEASED,
+ EMM_DETACHED,
+ EMM_ATTACH_FAILED,
+ EMM_ATTACH_STARTED,
+ LTE_NAS_SERVICE_REQUEST_FAILED,
+ DUPLICATE_BEARER_ID,
+ ESM_COLLISION_SCENARIOS,
+ ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK,
+ ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER,
+ ESM_BAD_OTA_MESSAGE,
+ ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL,
+ ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT,
+ DS_EXPLICIT_DEACTIVATION,
+ ESM_LOCAL_CAUSE_NONE,
+ LTE_THROTTLING_NOT_REQUIRED,
+ ACCESS_CONTROL_LIST_CHECK_FAILURE,
+ SERVICE_NOT_ALLOWED_ON_PLMN,
+ EMM_T3417_EXPIRED,
+ EMM_T3417_EXT_EXPIRED,
+ RRC_UPLINK_DATA_TRANSMISSION_FAILURE,
+ RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER,
+ RRC_UPLINK_CONNECTION_RELEASE,
+ RRC_UPLINK_RADIO_LINK_FAILURE,
+ RRC_UPLINK_ERROR_REQUEST_FROM_NAS,
+ RRC_CONNECTION_ACCESS_STRATUM_FAILURE,
+ RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS,
+ RRC_CONNECTION_ACCESS_BARRED,
+ RRC_CONNECTION_CELL_RESELECTION,
+ RRC_CONNECTION_CONFIG_FAILURE,
+ RRC_CONNECTION_TIMER_EXPIRED,
+ RRC_CONNECTION_LINK_FAILURE,
+ RRC_CONNECTION_CELL_NOT_CAMPED,
+ RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE,
+ RRC_CONNECTION_REJECT_BY_NETWORK,
+ RRC_CONNECTION_NORMAL_RELEASE,
+ RRC_CONNECTION_RADIO_LINK_FAILURE,
+ RRC_CONNECTION_REESTABLISHMENT_FAILURE,
+ RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER,
+ RRC_CONNECTION_ABORT_REQUEST,
+ RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR,
+ NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH,
+ NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH,
+ ESM_PROCEDURE_TIME_OUT,
+ INVALID_CONNECTION_ID,
+ MAXIMIUM_NSAPIS_EXCEEDED,
+ INVALID_PRIMARY_NSAPI,
+ CANNOT_ENCODE_OTA_MESSAGE,
+ RADIO_ACCESS_BEARER_SETUP_FAILURE,
+ PDP_ESTABLISH_TIMEOUT_EXPIRED,
+ PDP_MODIFY_TIMEOUT_EXPIRED,
+ PDP_INACTIVE_TIMEOUT_EXPIRED,
+ PDP_LOWERLAYER_ERROR,
+ PDP_MODIFY_COLLISION,
+ MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED,
+ NAS_REQUEST_REJECTED_BY_NETWORK,
+ RRC_CONNECTION_INVALID_REQUEST,
+ RRC_CONNECTION_TRACKING_AREA_ID_CHANGED,
+ RRC_CONNECTION_RF_UNAVAILABLE,
+ RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE,
+ RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE,
+ RRC_CONNECTION_ABORTED_AFTER_HANDOVER,
+ RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE,
+ RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE,
+ IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER,
+ IMEI_NOT_ACCEPTED,
+ EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED,
+ EPS_SERVICES_NOT_ALLOWED_IN_PLMN,
+ MSC_TEMPORARILY_NOT_REACHABLE,
+ CS_DOMAIN_NOT_AVAILABLE,
+ ESM_FAILURE,
+ MAC_FAILURE,
+ SYNCHRONIZATION_FAILURE,
+ UE_SECURITY_CAPABILITIES_MISMATCH,
+ SECURITY_MODE_REJECTED,
+ UNACCEPTABLE_NON_EPS_AUTHENTICATION,
+ CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED,
+ NO_EPS_BEARER_CONTEXT_ACTIVATED,
+ INVALID_EMM_STATE,
+ NAS_LAYER_FAILURE,
+ MULTIPLE_PDP_CALL_NOT_ALLOWED,
+ EMBMS_NOT_ENABLED,
+ IRAT_HANDOVER_FAILED,
+ EMBMS_REGULAR_DEACTIVATION,
+ TEST_LOOPBACK_REGULAR_DEACTIVATION,
+ LOWER_LAYER_REGISTRATION_FAILURE,
+ DATA_PLAN_EXPIRED,
+ UMTS_HANDOVER_TO_IWLAN,
+ EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY,
+ EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE,
+ EVDO_HDR_CHANGED,
+ EVDO_HDR_EXITED,
+ EVDO_HDR_NO_SESSION,
+ EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL,
+ EVDO_HDR_CONNECTION_SETUP_TIMEOUT,
+ FAILED_TO_ACQUIRE_COLOCATED_HDR,
+ OTASP_COMMIT_IN_PROGRESS,
+ NO_HYBRID_HDR_SERVICE,
+ HDR_NO_LOCK_GRANTED,
+ DBM_OR_SMS_IN_PROGRESS,
+ HDR_FADE,
+ HDR_ACCESS_FAILURE,
+ UNSUPPORTED_1X_PREV,
+ LOCAL_END,
+ NO_SERVICE,
+ FADE,
+ NORMAL_RELEASE,
+ ACCESS_ATTEMPT_ALREADY_IN_PROGRESS,
+ REDIRECTION_OR_HANDOFF_IN_PROGRESS,
+ EMERGENCY_MODE,
+ PHONE_IN_USE,
+ INVALID_MODE,
+ INVALID_SIM_STATE,
+ NO_COLLOCATED_HDR,
+ UE_IS_ENTERING_POWERSAVE_MODE,
+ DUAL_SWITCH,
+ PPP_TIMEOUT,
+ PPP_AUTH_FAILURE,
+ PPP_OPTION_MISMATCH,
+ PPP_PAP_FAILURE,
+ PPP_CHAP_FAILURE,
+ PPP_CLOSE_IN_PROGRESS,
+ LIMITED_TO_IPV4,
+ LIMITED_TO_IPV6,
+ VSNCP_TIMEOUT,
+ VSNCP_GEN_ERROR,
+ VSNCP_APN_UNATHORIZED,
+ VSNCP_PDN_LIMIT_EXCEEDED,
+ VSNCP_NO_PDN_GATEWAY_ADDRESS,
+ VSNCP_PDN_GATEWAY_UNREACHABLE,
+ VSNCP_PDN_GATEWAY_REJECT,
+ VSNCP_INSUFFICIENT_PARAMETERS,
+ VSNCP_RESOURCE_UNAVAILABLE,
+ VSNCP_ADMINISTRATIVELY_PROHIBITED,
+ VSNCP_PDN_ID_IN_USE,
+ VSNCP_SUBSCRIBER_LIMITATION,
+ VSNCP_PDN_EXISTS_FOR_THIS_APN,
+ VSNCP_RECONNECT_NOT_ALLOWED,
+ IPV6_PREFIX_UNAVAILABLE,
+ HANDOFF_PREFERENCE_CHANGED,
OEM_DCFAILCAUSE_1,
OEM_DCFAILCAUSE_2,
OEM_DCFAILCAUSE_3,
@@ -317,6 +1348,7 @@ public final class DataFailCause {
sFailCauseMap.put(FILTER_SEMANTIC_ERROR, "FILTER_SEMANTIC_ERROR");
sFailCauseMap.put(FILTER_SYTAX_ERROR, "FILTER_SYTAX_ERROR");
sFailCauseMap.put(PDP_WITHOUT_ACTIVE_TFT, "PDP_WITHOUT_ACTIVE_TFT");
+ sFailCauseMap.put(ACTIVATION_REJECTED_BCM_VIOLATION, "ACTIVATION_REJECTED_BCM_VIOLATION");
sFailCauseMap.put(ONLY_IPV4_ALLOWED, "ONLY_IPV4_ALLOWED");
sFailCauseMap.put(ONLY_IPV6_ALLOWED, "ONLY_IPV6_ALLOWED");
sFailCauseMap.put(ONLY_SINGLE_BEARER_ALLOWED, "ONLY_SINGLE_BEARER_ALLOWED");
@@ -324,6 +1356,12 @@ public final class DataFailCause {
sFailCauseMap.put(PDN_CONN_DOES_NOT_EXIST, "PDN_CONN_DOES_NOT_EXIST");
sFailCauseMap.put(MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
"MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED");
+ sFailCauseMap.put(COLLISION_WITH_NETWORK_INITIATED_REQUEST,
+ "COLLISION_WITH_NETWORK_INITIATED_REQUEST");
+ sFailCauseMap.put(ONLY_IPV4V6_ALLOWED, "ONLY_IPV4V6_ALLOWED");
+ sFailCauseMap.put(ONLY_NON_IP_ALLOWED, "ONLY_NON_IP_ALLOWED");
+ sFailCauseMap.put(UNSUPPORTED_QCI_VALUE, "UNSUPPORTED_QCI_VALUE");
+ sFailCauseMap.put(BEARER_HANDLING_NOT_SUPPORTED, "BEARER_HANDLING_NOT_SUPPORTED");
sFailCauseMap.put(ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED,
"ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED");
sFailCauseMap.put(UNSUPPORTED_APN_IN_CURRENT_PLMN,
@@ -353,6 +1391,301 @@ public final class DataFailCause {
"EMM_ACCESS_BARRED_INFINITE_RETRY");
sFailCauseMap.put(AUTH_FAILURE_ON_EMERGENCY_CALL,
"AUTH_FAILURE_ON_EMERGENCY_CALL");
+ sFailCauseMap.put(INVALID_DNS_ADDR, "INVALID_DNS_ADDR");
+ sFailCauseMap.put(INVALID_PCSCF_OR_DNS_ADDRESS, "INVALID_PCSCF_OR_DNS_ADDRESS");
+ sFailCauseMap.put(CALL_PREEMPT_BY_EMERGENCY_APN, "CALL_PREEMPT_BY_EMERGENCY_APN");
+ sFailCauseMap.put(UE_INITIATED_DETACH_OR_DISCONNECT, "UE_INITIATED_DETACH_OR_DISCONNECT");
+ sFailCauseMap.put(MIP_FA_REASON_UNSPECIFIED, "MIP_FA_REASON_UNSPECIFIED");
+ sFailCauseMap.put(MIP_FA_ADMIN_PROHIBITED, "MIP_FA_ADMIN_PROHIBITED");
+ sFailCauseMap.put(MIP_FA_INSUFFICIENT_RESOURCES, "MIP_FA_INSUFFICIENT_RESOURCES");
+ sFailCauseMap.put(MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE,
+ "MIP_FA_MOBILE_NODE_AUTHENTICATION_FAILURE");
+ sFailCauseMap.put(MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE,
+ "MIP_FA_HOME_AGENT_AUTHENTICATION_FAILURE");
+ sFailCauseMap.put(MIP_FA_REQUESTED_LIFETIME_TOO_LONG, "MIP_FA_REQUESTED_LIFETIME_TOO_LONG");
+ sFailCauseMap.put(MIP_FA_MALFORMED_REQUEST, "MIP_FA_MALFORMED_REQUEST");
+ sFailCauseMap.put(MIP_FA_MALFORMED_REPLY, "MIP_FA_MALFORMED_REPLY");
+ sFailCauseMap.put(MIP_FA_ENCAPSULATION_UNAVAILABLE, "MIP_FA_ENCAPSULATION_UNAVAILABLE");
+ sFailCauseMap.put(MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE,
+ "MIP_FA_VJ_HEADER_COMPRESSION_UNAVAILABLE");
+ sFailCauseMap.put(MIP_FA_REVERSE_TUNNEL_UNAVAILABLE, "MIP_FA_REVERSE_TUNNEL_UNAVAILABLE");
+ sFailCauseMap.put(MIP_FA_REVERSE_TUNNEL_IS_MANDATORY, "MIP_FA_REVERSE_TUNNEL_IS_MANDATORY");
+ sFailCauseMap.put(MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED,
+ "MIP_FA_DELIVERY_STYLE_NOT_SUPPORTED");
+ sFailCauseMap.put(MIP_FA_MISSING_NAI, "MIP_FA_MISSING_NAI");
+ sFailCauseMap.put(MIP_FA_MISSING_HOME_AGENT, "MIP_FA_MISSING_HOME_AGENT");
+ sFailCauseMap.put(MIP_FA_MISSING_HOME_ADDRESS, "MIP_FA_MISSING_HOME_ADDRESS");
+ sFailCauseMap.put(MIP_FA_UNKNOWN_CHALLENGE, "MIP_FA_UNKNOWN_CHALLENGE");
+ sFailCauseMap.put(MIP_FA_MISSING_CHALLENGE, "MIP_FA_MISSING_CHALLENGE");
+ sFailCauseMap.put(MIP_FA_STALE_CHALLENGE, "MIP_FA_STALE_CHALLENGE");
+ sFailCauseMap.put(MIP_HA_REASON_UNSPECIFIED, "MIP_HA_REASON_UNSPECIFIED");
+ sFailCauseMap.put(MIP_HA_ADMIN_PROHIBITED, "MIP_HA_ADMIN_PROHIBITED");
+ sFailCauseMap.put(MIP_HA_INSUFFICIENT_RESOURCES, "MIP_HA_INSUFFICIENT_RESOURCES");
+ sFailCauseMap.put(MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE,
+ "MIP_HA_MOBILE_NODE_AUTHENTICATION_FAILURE");
+ sFailCauseMap.put(MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE,
+ "MIP_HA_FOREIGN_AGENT_AUTHENTICATION_FAILURE");
+ sFailCauseMap.put(MIP_HA_REGISTRATION_ID_MISMATCH, "MIP_HA_REGISTRATION_ID_MISMATCH");
+ sFailCauseMap.put(MIP_HA_MALFORMED_REQUEST, "MIP_HA_MALFORMED_REQUEST");
+ sFailCauseMap.put(MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS, "MIP_HA_UNKNOWN_HOME_AGENT_ADDRESS");
+ sFailCauseMap.put(MIP_HA_REVERSE_TUNNEL_UNAVAILABLE, "MIP_HA_REVERSE_TUNNEL_UNAVAILABLE");
+ sFailCauseMap.put(MIP_HA_REVERSE_TUNNEL_IS_MANDATORY, "MIP_HA_REVERSE_TUNNEL_IS_MANDATORY");
+ sFailCauseMap.put(MIP_HA_ENCAPSULATION_UNAVAILABLE, "MIP_HA_ENCAPSULATION_UNAVAILABLE");
+ sFailCauseMap.put(CLOSE_IN_PROGRESS, "CLOSE_IN_PROGRESS");
+ sFailCauseMap.put(NETWORK_INITIATED_TERMINATION, "NETWORK_INITIATED_TERMINATION");
+ sFailCauseMap.put(MODEM_APP_PREEMPTED, "MODEM_APP_PREEMPTED");
+ sFailCauseMap.put(PDN_IPV4_CALL_DISALLOWED, "PDN_IPV4_CALL_DISALLOWED");
+ sFailCauseMap.put(PDN_IPV4_CALL_THROTTLED, "PDN_IPV4_CALL_THROTTLED");
+ sFailCauseMap.put(PDN_IPV6_CALL_DISALLOWED, "PDN_IPV6_CALL_DISALLOWED");
+ sFailCauseMap.put(PDN_IPV6_CALL_THROTTLED, "PDN_IPV6_CALL_THROTTLED");
+ sFailCauseMap.put(MODEM_RESTART, "MODEM_RESTART");
+ sFailCauseMap.put(PDP_PPP_NOT_SUPPORTED, "PDP_PPP_NOT_SUPPORTED");
+ sFailCauseMap.put(UNPREFERRED_RAT, "UNPREFERRED_RAT");
+ sFailCauseMap.put(PHYSICAL_LINK_CLOSE_IN_PROGRESS, "PHYSICAL_LINK_CLOSE_IN_PROGRESS");
+ sFailCauseMap.put(APN_PENDING_HANDOVER, "APN_PENDING_HANDOVER");
+ sFailCauseMap.put(PROFILE_BEARER_INCOMPATIBLE, "PROFILE_BEARER_INCOMPATIBLE");
+ sFailCauseMap.put(SIM_CARD_CHANGED, "SIM_CARD_CHANGED");
+ sFailCauseMap.put(LOW_POWER_MODE_OR_POWERING_DOWN, "LOW_POWER_MODE_OR_POWERING_DOWN");
+ sFailCauseMap.put(APN_DISABLED, "APN_DISABLED");
+ sFailCauseMap.put(MAX_PPP_INACTIVITY_TIMER_EXPIRED, "MAX_PPP_INACTIVITY_TIMER_EXPIRED");
+ sFailCauseMap.put(IPV6_ADDRESS_TRANSFER_FAILED, "IPV6_ADDRESS_TRANSFER_FAILED");
+ sFailCauseMap.put(TRAT_SWAP_FAILED, "TRAT_SWAP_FAILED");
+ sFailCauseMap.put(EHRPD_TO_HRPD_FALLBACK, "EHRPD_TO_HRPD_FALLBACK");
+ sFailCauseMap.put(MIP_CONFIG_FAILURE, "MIP_CONFIG_FAILURE");
+ sFailCauseMap.put(PDN_INACTIVITY_TIMER_EXPIRED, "PDN_INACTIVITY_TIMER_EXPIRED");
+ sFailCauseMap.put(MAX_IPV4_CONNECTIONS, "MAX_IPV4_CONNECTIONS");
+ sFailCauseMap.put(MAX_IPV6_CONNECTIONS, "MAX_IPV6_CONNECTIONS");
+ sFailCauseMap.put(APN_MISMATCH, "APN_MISMATCH");
+ sFailCauseMap.put(IP_VERSION_MISMATCH, "IP_VERSION_MISMATCH");
+ sFailCauseMap.put(DUN_CALL_DISALLOWED, "DUN_CALL_DISALLOWED");
+ sFailCauseMap.put(INTERNAL_EPC_NONEPC_TRANSITION, "INTERNAL_EPC_NONEPC_TRANSITION");
+ sFailCauseMap.put(INTERFACE_IN_USE, "INTERFACE_IN_USE");
+ sFailCauseMap.put(APN_DISALLOWED_ON_ROAMING, "APN_DISALLOWED_ON_ROAMING");
+ sFailCauseMap.put(APN_PARAMETERS_CHANGED, "APN_PARAMETERS_CHANGED");
+ sFailCauseMap.put(NULL_APN_DISALLOWED, "NULL_APN_DISALLOWED");
+ sFailCauseMap.put(THERMAL_MITIGATION, "THERMAL_MITIGATION");
+ sFailCauseMap.put(DATA_SETTINGS_DISABLED, "DATA_SETTINGS_DISABLED");
+ sFailCauseMap.put(DATA_ROAMING_SETTINGS_DISABLED, "DATA_ROAMING_SETTINGS_DISABLED");
+ sFailCauseMap.put(DDS_SWITCHED, "DDS_SWITCHED");
+ sFailCauseMap.put(FORBIDDEN_APN_NAME, "FORBIDDEN_APN_NAME");
+ sFailCauseMap.put(DDS_SWITCH_IN_PROGRESS, "DDS_SWITCH_IN_PROGRESS");
+ sFailCauseMap.put(CALL_DISALLOWED_IN_ROAMING, "CALL_DISALLOWED_IN_ROAMING");
+ sFailCauseMap.put(NON_IP_NOT_SUPPORTED, "NON_IP_NOT_SUPPORTED");
+ sFailCauseMap.put(PDN_NON_IP_CALL_THROTTLED, "PDN_NON_IP_CALL_THROTTLED");
+ sFailCauseMap.put(PDN_NON_IP_CALL_DISALLOWED, "PDN_NON_IP_CALL_DISALLOWED");
+ sFailCauseMap.put(CDMA_LOCK, "CDMA_LOCK");
+ sFailCauseMap.put(CDMA_INTERCEPT, "CDMA_INTERCEPT");
+ sFailCauseMap.put(CDMA_REORDER, "CDMA_REORDER");
+ sFailCauseMap.put(CDMA_RELEASE_DUE_TO_SO_REJECTION, "CDMA_RELEASE_DUE_TO_SO_REJECTION");
+ sFailCauseMap.put(CDMA_INCOMING_CALL, "CDMA_INCOMING_CALL");
+ sFailCauseMap.put(CDMA_ALERT_STOP, "CDMA_ALERT_STOP");
+ sFailCauseMap.put(CHANNEL_ACQUISITION_FAILURE, "CHANNEL_ACQUISITION_FAILURE");
+ sFailCauseMap.put(MAX_ACCESS_PROBE, "MAX_ACCESS_PROBE");
+ sFailCauseMap.put(CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION,
+ "CONCURRENT_SERVICE_NOT_SUPPORTED_BY_BASE_STATION");
+ sFailCauseMap.put(NO_RESPONSE_FROM_BASE_STATION, "NO_RESPONSE_FROM_BASE_STATION");
+ sFailCauseMap.put(REJECTED_BY_BASE_STATION, "REJECTED_BY_BASE_STATION");
+ sFailCauseMap.put(CONCURRENT_SERVICES_INCOMPATIBLE, "CONCURRENT_SERVICES_INCOMPATIBLE");
+ sFailCauseMap.put(NO_CDMA_SERVICE, "NO_CDMA_SERVICE");
+ sFailCauseMap.put(RUIM_NOT_PRESENT, "RUIM_NOT_PRESENT");
+ sFailCauseMap.put(CDMA_RETRY_ORDER, "CDMA_RETRY_ORDER");
+ sFailCauseMap.put(ACCESS_BLOCK, "ACCESS_BLOCK");
+ sFailCauseMap.put(ACCESS_BLOCK_ALL, "ACCESS_BLOCK_ALL");
+ sFailCauseMap.put(IS707B_MAX_ACCESS_PROBES, "IS707B_MAX_ACCESS_PROBES");
+ sFailCauseMap.put(THERMAL_EMERGENCY, "THERMAL_EMERGENCY");
+ sFailCauseMap.put(CONCURRENT_SERVICES_NOT_ALLOWED, "CONCURRENT_SERVICES_NOT_ALLOWED");
+ sFailCauseMap.put(INCOMING_CALL_REJECTED, "INCOMING_CALL_REJECTED");
+ sFailCauseMap.put(NO_SERVICE_ON_GATEWAY, "NO_SERVICE_ON_GATEWAY");
+ sFailCauseMap.put(NO_GPRS_CONTEXT, "NO_GPRS_CONTEXT");
+ sFailCauseMap.put(ILLEGAL_MS, "ILLEGAL_MS");
+ sFailCauseMap.put(ILLEGAL_ME, "ILLEGAL_ME");
+ sFailCauseMap.put(GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED,
+ "GPRS_SERVICES_AND_NON_GPRS_SERVICES_NOT_ALLOWED");
+ sFailCauseMap.put(GPRS_SERVICES_NOT_ALLOWED, "GPRS_SERVICES_NOT_ALLOWED");
+ sFailCauseMap.put(MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK,
+ "MS_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK");
+ sFailCauseMap.put(IMPLICITLY_DETACHED, "IMPLICITLY_DETACHED");
+ sFailCauseMap.put(PLMN_NOT_ALLOWED, "PLMN_NOT_ALLOWED");
+ sFailCauseMap.put(LOCATION_AREA_NOT_ALLOWED, "LOCATION_AREA_NOT_ALLOWED");
+ sFailCauseMap.put(GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN,
+ "GPRS_SERVICES_NOT_ALLOWED_IN_THIS_PLMN");
+ sFailCauseMap.put(PDP_DUPLICATE, "PDP_DUPLICATE");
+ sFailCauseMap.put(UE_RAT_CHANGE, "UE_RAT_CHANGE");
+ sFailCauseMap.put(CONGESTION, "CONGESTION");
+ sFailCauseMap.put(NO_PDP_CONTEXT_ACTIVATED, "NO_PDP_CONTEXT_ACTIVATED");
+ sFailCauseMap.put(ACCESS_CLASS_DSAC_REJECTION, "ACCESS_CLASS_DSAC_REJECTION");
+ sFailCauseMap.put(PDP_ACTIVATE_MAX_RETRY_FAILED, "PDP_ACTIVATE_MAX_RETRY_FAILED");
+ sFailCauseMap.put(RADIO_ACCESS_BEARER_FAILURE, "RADIO_ACCESS_BEARER_FAILURE");
+ sFailCauseMap.put(ESM_UNKNOWN_EPS_BEARER_CONTEXT, "ESM_UNKNOWN_EPS_BEARER_CONTEXT");
+ sFailCauseMap.put(DRB_RELEASED_BY_RRC, "DRB_RELEASED_BY_RRC");
+ sFailCauseMap.put(CONNECTION_RELEASED, "CONNECTION_RELEASED");
+ sFailCauseMap.put(EMM_DETACHED, "EMM_DETACHED");
+ sFailCauseMap.put(EMM_ATTACH_FAILED, "EMM_ATTACH_FAILED");
+ sFailCauseMap.put(EMM_ATTACH_STARTED, "EMM_ATTACH_STARTED");
+ sFailCauseMap.put(LTE_NAS_SERVICE_REQUEST_FAILED, "LTE_NAS_SERVICE_REQUEST_FAILED");
+ sFailCauseMap.put(DUPLICATE_BEARER_ID, "DUPLICATE_BEARER_ID");
+ sFailCauseMap.put(ESM_COLLISION_SCENARIOS, "ESM_COLLISION_SCENARIOS");
+ sFailCauseMap.put(ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK,
+ "ESM_BEARER_DEACTIVATED_TO_SYNC_WITH_NETWORK");
+ sFailCauseMap.put(ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER,
+ "ESM_NW_ACTIVATED_DED_BEARER_WITH_ID_OF_DEF_BEARER");
+ sFailCauseMap.put(ESM_BAD_OTA_MESSAGE, "ESM_BAD_OTA_MESSAGE");
+ sFailCauseMap.put(ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL,
+ "ESM_DOWNLOAD_SERVER_REJECTED_THE_CALL");
+ sFailCauseMap.put(ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT,
+ "ESM_CONTEXT_TRANSFERRED_DUE_TO_IRAT");
+ sFailCauseMap.put(DS_EXPLICIT_DEACTIVATION, "DS_EXPLICIT_DEACTIVATION");
+ sFailCauseMap.put(ESM_LOCAL_CAUSE_NONE, "ESM_LOCAL_CAUSE_NONE");
+ sFailCauseMap.put(LTE_THROTTLING_NOT_REQUIRED, "LTE_THROTTLING_NOT_REQUIRED");
+ sFailCauseMap.put(ACCESS_CONTROL_LIST_CHECK_FAILURE,
+ "ACCESS_CONTROL_LIST_CHECK_FAILURE");
+ sFailCauseMap.put(SERVICE_NOT_ALLOWED_ON_PLMN, "SERVICE_NOT_ALLOWED_ON_PLMN");
+ sFailCauseMap.put(EMM_T3417_EXPIRED, "EMM_T3417_EXPIRED");
+ sFailCauseMap.put(EMM_T3417_EXT_EXPIRED, "EMM_T3417_EXT_EXPIRED");
+ sFailCauseMap.put(RRC_UPLINK_DATA_TRANSMISSION_FAILURE,
+ "RRC_UPLINK_DATA_TRANSMISSION_FAILURE");
+ sFailCauseMap.put(RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER,
+ "RRC_UPLINK_DELIVERY_FAILED_DUE_TO_HANDOVER");
+ sFailCauseMap.put(RRC_UPLINK_CONNECTION_RELEASE, "RRC_UPLINK_CONNECTION_RELEASE");
+ sFailCauseMap.put(RRC_UPLINK_RADIO_LINK_FAILURE, "RRC_UPLINK_RADIO_LINK_FAILURE");
+ sFailCauseMap.put(RRC_UPLINK_ERROR_REQUEST_FROM_NAS, "RRC_UPLINK_ERROR_REQUEST_FROM_NAS");
+ sFailCauseMap.put(RRC_CONNECTION_ACCESS_STRATUM_FAILURE,
+ "RRC_CONNECTION_ACCESS_STRATUM_FAILURE");
+ sFailCauseMap.put(RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS,
+ "RRC_CONNECTION_ANOTHER_PROCEDURE_IN_PROGRESS");
+ sFailCauseMap.put(RRC_CONNECTION_ACCESS_BARRED, "RRC_CONNECTION_ACCESS_BARRED");
+ sFailCauseMap.put(RRC_CONNECTION_CELL_RESELECTION, "RRC_CONNECTION_CELL_RESELECTION");
+ sFailCauseMap.put(RRC_CONNECTION_CONFIG_FAILURE, "RRC_CONNECTION_CONFIG_FAILURE");
+ sFailCauseMap.put(RRC_CONNECTION_TIMER_EXPIRED, "RRC_CONNECTION_TIMER_EXPIRED");
+ sFailCauseMap.put(RRC_CONNECTION_LINK_FAILURE, "RRC_CONNECTION_LINK_FAILURE");
+ sFailCauseMap.put(RRC_CONNECTION_CELL_NOT_CAMPED, "RRC_CONNECTION_CELL_NOT_CAMPED");
+ sFailCauseMap.put(RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE,
+ "RRC_CONNECTION_SYSTEM_INTERVAL_FAILURE");
+ sFailCauseMap.put(RRC_CONNECTION_REJECT_BY_NETWORK, "RRC_CONNECTION_REJECT_BY_NETWORK");
+ sFailCauseMap.put(RRC_CONNECTION_NORMAL_RELEASE, "RRC_CONNECTION_NORMAL_RELEASE");
+ sFailCauseMap.put(RRC_CONNECTION_RADIO_LINK_FAILURE, "RRC_CONNECTION_RADIO_LINK_FAILURE");
+ sFailCauseMap.put(RRC_CONNECTION_REESTABLISHMENT_FAILURE,
+ "RRC_CONNECTION_REESTABLISHMENT_FAILURE");
+ sFailCauseMap.put(RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER,
+ "RRC_CONNECTION_OUT_OF_SERVICE_DURING_CELL_REGISTER");
+ sFailCauseMap.put(RRC_CONNECTION_ABORT_REQUEST, "RRC_CONNECTION_ABORT_REQUEST");
+ sFailCauseMap.put(RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR,
+ "RRC_CONNECTION_SYSTEM_INFORMATION_BLOCK_READ_ERROR");
+ sFailCauseMap.put(NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH,
+ "NETWORK_INITIATED_DETACH_WITH_AUTO_REATTACH");
+ sFailCauseMap.put(NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH,
+ "NETWORK_INITIATED_DETACH_NO_AUTO_REATTACH");
+ sFailCauseMap.put(ESM_PROCEDURE_TIME_OUT, "ESM_PROCEDURE_TIME_OUT");
+ sFailCauseMap.put(INVALID_CONNECTION_ID, "INVALID_CONNECTION_ID");
+ sFailCauseMap.put(MAXIMIUM_NSAPIS_EXCEEDED, "MAXIMIUM_NSAPIS_EXCEEDED");
+ sFailCauseMap.put(INVALID_PRIMARY_NSAPI, "INVALID_PRIMARY_NSAPI");
+ sFailCauseMap.put(CANNOT_ENCODE_OTA_MESSAGE, "CANNOT_ENCODE_OTA_MESSAGE");
+ sFailCauseMap.put(RADIO_ACCESS_BEARER_SETUP_FAILURE, "RADIO_ACCESS_BEARER_SETUP_FAILURE");
+ sFailCauseMap.put(PDP_ESTABLISH_TIMEOUT_EXPIRED, "PDP_ESTABLISH_TIMEOUT_EXPIRED");
+ sFailCauseMap.put(PDP_MODIFY_TIMEOUT_EXPIRED, "PDP_MODIFY_TIMEOUT_EXPIRED");
+ sFailCauseMap.put(PDP_INACTIVE_TIMEOUT_EXPIRED, "PDP_INACTIVE_TIMEOUT_EXPIRED");
+ sFailCauseMap.put(PDP_LOWERLAYER_ERROR, "PDP_LOWERLAYER_ERROR");
+ sFailCauseMap.put(PDP_MODIFY_COLLISION, "PDP_MODIFY_COLLISION");
+ sFailCauseMap.put(MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED,
+ "MAXINUM_SIZE_OF_L2_MESSAGE_EXCEEDED");
+ sFailCauseMap.put(NAS_REQUEST_REJECTED_BY_NETWORK, "NAS_REQUEST_REJECTED_BY_NETWORK");
+ sFailCauseMap.put(RRC_CONNECTION_INVALID_REQUEST, "RRC_CONNECTION_INVALID_REQUEST");
+ sFailCauseMap.put(RRC_CONNECTION_TRACKING_AREA_ID_CHANGED,
+ "RRC_CONNECTION_TRACKING_AREA_ID_CHANGED");
+ sFailCauseMap.put(RRC_CONNECTION_RF_UNAVAILABLE, "RRC_CONNECTION_RF_UNAVAILABLE");
+ sFailCauseMap.put(RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE,
+ "RRC_CONNECTION_ABORTED_DUE_TO_IRAT_CHANGE");
+ sFailCauseMap.put(RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE,
+ "RRC_CONNECTION_RELEASED_SECURITY_NOT_ACTIVE");
+ sFailCauseMap.put(RRC_CONNECTION_ABORTED_AFTER_HANDOVER,
+ "RRC_CONNECTION_ABORTED_AFTER_HANDOVER");
+ sFailCauseMap.put(RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE,
+ "RRC_CONNECTION_ABORTED_AFTER_IRAT_CELL_CHANGE");
+ sFailCauseMap.put(RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE,
+ "RRC_CONNECTION_ABORTED_DURING_IRAT_CELL_CHANGE");
+ sFailCauseMap.put(IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER,
+ "IMSI_UNKNOWN_IN_HOME_SUBSCRIBER_SERVER");
+ sFailCauseMap.put(IMEI_NOT_ACCEPTED, "IMEI_NOT_ACCEPTED");
+ sFailCauseMap.put(EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED,
+ "EPS_SERVICES_AND_NON_EPS_SERVICES_NOT_ALLOWED");
+ sFailCauseMap.put(EPS_SERVICES_NOT_ALLOWED_IN_PLMN, "EPS_SERVICES_NOT_ALLOWED_IN_PLMN");
+ sFailCauseMap.put(MSC_TEMPORARILY_NOT_REACHABLE, "MSC_TEMPORARILY_NOT_REACHABLE");
+ sFailCauseMap.put(CS_DOMAIN_NOT_AVAILABLE, "CS_DOMAIN_NOT_AVAILABLE");
+ sFailCauseMap.put(ESM_FAILURE, "ESM_FAILURE");
+ sFailCauseMap.put(MAC_FAILURE, "MAC_FAILURE");
+ sFailCauseMap.put(SYNCHRONIZATION_FAILURE, "SYNCHRONIZATION_FAILURE");
+ sFailCauseMap.put(UE_SECURITY_CAPABILITIES_MISMATCH, "UE_SECURITY_CAPABILITIES_MISMATCH");
+ sFailCauseMap.put(SECURITY_MODE_REJECTED, "SECURITY_MODE_REJECTED");
+ sFailCauseMap.put(UNACCEPTABLE_NON_EPS_AUTHENTICATION,
+ "UNACCEPTABLE_NON_EPS_AUTHENTICATION");
+ sFailCauseMap.put(CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED,
+ "CS_FALLBACK_CALL_ESTABLISHMENT_NOT_ALLOWED");
+ sFailCauseMap.put(NO_EPS_BEARER_CONTEXT_ACTIVATED, "NO_EPS_BEARER_CONTEXT_ACTIVATED");
+ sFailCauseMap.put(INVALID_EMM_STATE, "INVALID_EMM_STATE");
+ sFailCauseMap.put(NAS_LAYER_FAILURE, "NAS_LAYER_FAILURE");
+ sFailCauseMap.put(MULTIPLE_PDP_CALL_NOT_ALLOWED, "MULTIPLE_PDP_CALL_NOT_ALLOWED");
+ sFailCauseMap.put(EMBMS_NOT_ENABLED, "EMBMS_NOT_ENABLED");
+ sFailCauseMap.put(IRAT_HANDOVER_FAILED, "IRAT_HANDOVER_FAILED");
+ sFailCauseMap.put(EMBMS_REGULAR_DEACTIVATION, "EMBMS_REGULAR_DEACTIVATION");
+ sFailCauseMap.put(TEST_LOOPBACK_REGULAR_DEACTIVATION, "TEST_LOOPBACK_REGULAR_DEACTIVATION");
+ sFailCauseMap.put(LOWER_LAYER_REGISTRATION_FAILURE, "LOWER_LAYER_REGISTRATION_FAILURE");
+ sFailCauseMap.put(DATA_PLAN_EXPIRED, "DATA_PLAN_EXPIRED");
+ sFailCauseMap.put(UMTS_HANDOVER_TO_IWLAN, "UMTS_HANDOVER_TO_IWLAN");
+ sFailCauseMap.put(EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY,
+ "EVDO_CONNECTION_DENY_BY_GENERAL_OR_NETWORK_BUSY");
+ sFailCauseMap.put(EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE,
+ "EVDO_CONNECTION_DENY_BY_BILLING_OR_AUTHENTICATION_FAILURE");
+ sFailCauseMap.put(EVDO_HDR_CHANGED, "EVDO_HDR_CHANGED");
+ sFailCauseMap.put(EVDO_HDR_EXITED, "EVDO_HDR_EXITED");
+ sFailCauseMap.put(EVDO_HDR_NO_SESSION, "EVDO_HDR_NO_SESSION");
+ sFailCauseMap.put(EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL,
+ "EVDO_USING_GPS_FIX_INSTEAD_OF_HDR_CALL");
+ sFailCauseMap.put(EVDO_HDR_CONNECTION_SETUP_TIMEOUT, "EVDO_HDR_CONNECTION_SETUP_TIMEOUT");
+ sFailCauseMap.put(FAILED_TO_ACQUIRE_COLOCATED_HDR, "FAILED_TO_ACQUIRE_COLOCATED_HDR");
+ sFailCauseMap.put(OTASP_COMMIT_IN_PROGRESS, "OTASP_COMMIT_IN_PROGRESS");
+ sFailCauseMap.put(NO_HYBRID_HDR_SERVICE, "NO_HYBRID_HDR_SERVICE");
+ sFailCauseMap.put(HDR_NO_LOCK_GRANTED, "HDR_NO_LOCK_GRANTED");
+ sFailCauseMap.put(DBM_OR_SMS_IN_PROGRESS, "DBM_OR_SMS_IN_PROGRESS");
+ sFailCauseMap.put(HDR_FADE, "HDR_FADE");
+ sFailCauseMap.put(HDR_ACCESS_FAILURE, "HDR_ACCESS_FAILURE");
+ sFailCauseMap.put(UNSUPPORTED_1X_PREV, "UNSUPPORTED_1X_PREV");
+ sFailCauseMap.put(LOCAL_END, "LOCAL_END");
+ sFailCauseMap.put(NO_SERVICE, "NO_SERVICE");
+ sFailCauseMap.put(FADE, "FADE");
+ sFailCauseMap.put(NORMAL_RELEASE, "NORMAL_RELEASE");
+ sFailCauseMap.put(ACCESS_ATTEMPT_ALREADY_IN_PROGRESS, "ACCESS_ATTEMPT_ALREADY_IN_PROGRESS");
+ sFailCauseMap.put(REDIRECTION_OR_HANDOFF_IN_PROGRESS, "REDIRECTION_OR_HANDOFF_IN_PROGRESS");
+ sFailCauseMap.put(EMERGENCY_MODE, "EMERGENCY_MODE");
+ sFailCauseMap.put(PHONE_IN_USE, "PHONE_IN_USE");
+ sFailCauseMap.put(INVALID_MODE, "INVALID_MODE");
+ sFailCauseMap.put(INVALID_SIM_STATE, "INVALID_SIM_STATE");
+ sFailCauseMap.put(NO_COLLOCATED_HDR, "NO_COLLOCATED_HDR");
+ sFailCauseMap.put(UE_IS_ENTERING_POWERSAVE_MODE, "UE_IS_ENTERING_POWERSAVE_MODE");
+ sFailCauseMap.put(DUAL_SWITCH, "DUAL_SWITCH");
+ sFailCauseMap.put(PPP_TIMEOUT, "PPP_TIMEOUT");
+ sFailCauseMap.put(PPP_AUTH_FAILURE, "PPP_AUTH_FAILURE");
+ sFailCauseMap.put(PPP_OPTION_MISMATCH, "PPP_OPTION_MISMATCH");
+ sFailCauseMap.put(PPP_PAP_FAILURE, "PPP_PAP_FAILURE");
+ sFailCauseMap.put(PPP_CHAP_FAILURE, "PPP_CHAP_FAILURE");
+ sFailCauseMap.put(PPP_CLOSE_IN_PROGRESS, "PPP_CLOSE_IN_PROGRESS");
+ sFailCauseMap.put(LIMITED_TO_IPV4, "LIMITED_TO_IPV4");
+ sFailCauseMap.put(LIMITED_TO_IPV6, "LIMITED_TO_IPV6");
+ sFailCauseMap.put(VSNCP_TIMEOUT, "VSNCP_TIMEOUT");
+ sFailCauseMap.put(VSNCP_GEN_ERROR, "VSNCP_GEN_ERROR");
+ sFailCauseMap.put(VSNCP_APN_UNATHORIZED, "VSNCP_APN_UNATHORIZED");
+ sFailCauseMap.put(VSNCP_PDN_LIMIT_EXCEEDED, "VSNCP_PDN_LIMIT_EXCEEDED");
+ sFailCauseMap.put(VSNCP_NO_PDN_GATEWAY_ADDRESS, "VSNCP_NO_PDN_GATEWAY_ADDRESS");
+ sFailCauseMap.put(VSNCP_PDN_GATEWAY_UNREACHABLE, "VSNCP_PDN_GATEWAY_UNREACHABLE");
+ sFailCauseMap.put(VSNCP_PDN_GATEWAY_REJECT, "VSNCP_PDN_GATEWAY_REJECT");
+ sFailCauseMap.put(VSNCP_INSUFFICIENT_PARAMETERS, "VSNCP_INSUFFICIENT_PARAMETERS");
+ sFailCauseMap.put(VSNCP_RESOURCE_UNAVAILABLE, "VSNCP_RESOURCE_UNAVAILABLE");
+ sFailCauseMap.put(VSNCP_ADMINISTRATIVELY_PROHIBITED, "VSNCP_ADMINISTRATIVELY_PROHIBITED");
+ sFailCauseMap.put(VSNCP_PDN_ID_IN_USE, "VSNCP_PDN_ID_IN_USE");
+ sFailCauseMap.put(VSNCP_SUBSCRIBER_LIMITATION, "VSNCP_SUBSCRIBER_LIMITATION");
+ sFailCauseMap.put(VSNCP_PDN_EXISTS_FOR_THIS_APN, "VSNCP_PDN_EXISTS_FOR_THIS_APN");
+ sFailCauseMap.put(VSNCP_RECONNECT_NOT_ALLOWED, "VSNCP_RECONNECT_NOT_ALLOWED");
+ sFailCauseMap.put(IPV6_PREFIX_UNAVAILABLE, "IPV6_PREFIX_UNAVAILABLE");
+ sFailCauseMap.put(HANDOFF_PREFERENCE_CHANGED, "HANDOFF_PREFERENCE_CHANGED");
sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 8d148c36f3e7..0e695309fce2 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -140,15 +140,19 @@ public class ApnSetting implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
public @interface AuthType {}
- // Possible values for protocol.
- /** Protocol type for IP. */
+ // Possible values for protocol which is defined in TS 27.007 section 10.1.1.
+ /** Internet protocol. */
public static final int PROTOCOL_IP = 0;
- /** Protocol type for IPV6. */
+ /** Internet protocol, version 6. */
public static final int PROTOCOL_IPV6 = 1;
- /** Protocol type for IPV4V6. */
+ /** Virtual PDP type introduced to handle dual IP stack UE capability. */
public static final int PROTOCOL_IPV4V6 = 2;
- /** Protocol type for PPP. */
+ /** Point to point protocol. */
public static final int PROTOCOL_PPP = 3;
+ /** Transfer of Non-IP data to external packet data network. */
+ public static final int PROTOCOL_NON_IP = 4;
+ /** Transfer of Unstructured data to the Data Network via N6. */
+ public static final int PROTOCOL_UNSTRUCTURED = 5;
/** @hide */
@IntDef(prefix = { "PROTOCOL_" }, value = {
@@ -156,6 +160,8 @@ public class ApnSetting implements Parcelable {
PROTOCOL_IPV6,
PROTOCOL_IPV4V6,
PROTOCOL_PPP,
+ PROTOCOL_NON_IP,
+ PROTOCOL_UNSTRUCTURED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtocolType {}
@@ -217,11 +223,15 @@ public class ApnSetting implements Parcelable {
PROTOCOL_STRING_MAP.put("IPV6", PROTOCOL_IPV6);
PROTOCOL_STRING_MAP.put("IPV4V6", PROTOCOL_IPV4V6);
PROTOCOL_STRING_MAP.put("PPP", PROTOCOL_PPP);
+ PROTOCOL_STRING_MAP.put("NON-IP", PROTOCOL_NON_IP);
+ PROTOCOL_STRING_MAP.put("UNSTRUCTURED", PROTOCOL_UNSTRUCTURED);
PROTOCOL_INT_MAP = new ArrayMap<Integer, String>();
PROTOCOL_INT_MAP.put(PROTOCOL_IP, "IP");
PROTOCOL_INT_MAP.put(PROTOCOL_IPV6, "IPV6");
PROTOCOL_INT_MAP.put(PROTOCOL_IPV4V6, "IPV4V6");
PROTOCOL_INT_MAP.put(PROTOCOL_PPP, "PPP");
+ PROTOCOL_INT_MAP.put(PROTOCOL_NON_IP, "NON-IP");
+ PROTOCOL_INT_MAP.put(PROTOCOL_UNSTRUCTURED, "UNSTRUCTURED");
MVNO_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
MVNO_TYPE_STRING_MAP.put("spn", MVNO_TYPE_SPN);
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index 25f51333350b..294c79ba57a2 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -52,8 +52,7 @@ public final class DataCallResponse implements Parcelable {
* @param status Data call fail cause. 0 indicates no error.
* @param suggestedRetryTime The suggested data retry time in milliseconds.
* @param cid The unique id of the data connection.
- * @param active Data connection active status. 0 = inactive, 1 = active/physical link down,
- * 2 = active/physical link up.
+ * @param active Data connection active status. 0 = inactive, 1 = dormant, 2 = active.
* @param type The connection protocol, should be one of the PDP_type values in TS 27.007
* section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
* @param ifname The network interface name.
@@ -124,7 +123,7 @@ public final class DataCallResponse implements Parcelable {
public int getCallId() { return mCid; }
/**
- * @return 0 = inactive, 1 = active/physical link down, 2 = active/physical link up.
+ * @return 0 = inactive, 1 = dormant, 2 = active.
*/
public int getActive() { return mActive; }
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 0e5c71d6ef90..0fa1b41d4b16 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -129,6 +129,66 @@ public class EuiccManager {
"android.telephony.euicc.action.RESOLVE_ERROR";
/**
+ * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+ * enable or disable a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID} and
+ * {@link #EXTRA_ENABLE_SUBSCRIPTION}.
+ *
+ * <p>Unlike {@link #switchToSubscription(int, PendingIntent)}, using this action allows the
+ * underlying eUICC service (i.e. the LPA app) to control the UI experience during this
+ * operation. The action is received by the Telephony framework, which in turn selects and
+ * launches an appropriate LPA activity to present UI to the user. For example, the activity may
+ * show a confirmation dialog, a progress dialog, or an error dialog when necessary.
+ *
+ * <p>The launched activity will immediately finish with
+ * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED =
+ "android.telephony.euicc.action.TOGGLE_SUBSCRIPTION_PRIVILEGED";
+
+ /**
+ * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+ * delete a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID}.
+ *
+ * <p>Unlike {@link #deleteSubscription(int, PendingIntent)}, using this action allows the
+ * underlying eUICC service (i.e. the LPA app) to control the UI experience during this
+ * operation. The action is received by the Telephony framework, which in turn selects and
+ * launches an appropriate LPA activity to present UI to the user. For example, the activity may
+ * show a confirmation dialog, a progress dialog, or an error dialog when necessary.
+ *
+ * <p>The launched activity will immediately finish with
+ * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_DELETE_SUBSCRIPTION_PRIVILEGED =
+ "android.telephony.euicc.action.DELETE_SUBSCRIPTION_PRIVILEGED";
+
+ /**
+ * Intent action sent by system apps (such as the Settings app) to the Telephony framework to
+ * rename a subscription. Must be accompanied with {@link #EXTRA_SUBSCRIPTION_ID} and
+ * {@link #EXTRA_SUBSCRIPTION_NICKNAME}.
+ *
+ * <p>Unlike {@link #updateSubscriptionNickname(int, String, PendingIntent)}, using this action
+ * allows the the underlying eUICC service (i.e. the LPA app) to control the UI experience
+ * during this operation. The action is received by the Telephony framework, which in turn
+ * selects and launches an appropriate LPA activity to present UI to the user. For example, the
+ * activity may show a confirmation dialog, a progress dialog, or an error dialog when
+ * necessary.
+ *
+ * <p>The launched activity will immediately finish with
+ * {@link android.app.Activity#RESULT_CANCELED} if {@link #isEnabled} is false.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_RENAME_SUBSCRIPTION_PRIVILEGED =
+ "android.telephony.euicc.action.RENAME_SUBSCRIPTION_PRIVILEGED";
+
+ /**
* Result code for an operation indicating that the operation succeeded.
*/
public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0;
@@ -219,6 +279,37 @@ public class EuiccManager {
"android.telephony.euicc.extra.FORCE_PROVISION";
/**
+ * Key for an extra set on privileged actions {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED},
+ * {@link #ACTION_DELETE_SUBSCRIPTION_PRIVILEGED}, and
+ * {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing the ID of the targeted subscription.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SUBSCRIPTION_ID =
+ "android.telephony.euicc.extra.SUBSCRIPTION_ID";
+
+ /**
+ * Key for an extra set on {@link #ACTION_TOGGLE_SUBSCRIPTION_PRIVILEGED} providing a boolean
+ * value of whether to enable or disable the targeted subscription.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_ENABLE_SUBSCRIPTION =
+ "android.telephony.euicc.extra.ENABLE_SUBSCRIPTION";
+
+ /**
+ * Key for an extra set on {@link #ACTION_RENAME_SUBSCRIPTION_PRIVILEGED} providing a new
+ * nickname for the targeted subscription.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_SUBSCRIPTION_NICKNAME =
+ "android.telephony.euicc.extra.SUBSCRIPTION_NICKNAME";
+
+ /**
* Optional meta-data attribute for a carrier app providing an icon to use to represent the
* carrier. If not provided, the app's launcher icon will be used as a fallback.
*/
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index a181bc387d36..1110790c373f 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -32,7 +32,6 @@ package android.test.mock {
public class MockContext extends android.content.Context {
ctor public MockContext();
- method public boolean bindIsolatedService(android.content.Intent, android.content.ServiceConnection, int, String);
method public boolean bindService(android.content.Intent, android.content.ServiceConnection, int);
method public int checkCallingOrSelfPermission(String);
method public int checkCallingOrSelfUriPermission(android.net.Uri, int);
@@ -81,7 +80,6 @@ package android.test.mock {
method public java.io.File getNoBackupFilesDir();
method public java.io.File getObbDir();
method public java.io.File[] getObbDirs();
- method public String getOpPackageName();
method public String getPackageCodePath();
method public android.content.pm.PackageManager getPackageManager();
method public String getPackageName();
@@ -137,7 +135,6 @@ package android.test.mock {
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
- method public void updateServiceGroup(android.content.ServiceConnection, int, int);
}
@Deprecated public class MockCursor implements android.database.Cursor {
diff --git a/tests/DexLoggerIntegrationTests/Android.mk b/tests/DexLoggerIntegrationTests/Android.mk
index ee2ec0a80b03..979d13ac9405 100644
--- a/tests/DexLoggerIntegrationTests/Android.mk
+++ b/tests/DexLoggerIntegrationTests/Android.mk
@@ -29,6 +29,35 @@ include $(BUILD_JAVA_LIBRARY)
dexloggertest_jar := $(LOCAL_BUILT_MODULE)
+# Also build a native library that the test app can dynamically load
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE := DexLoggerNativeTestLibrary
+LOCAL_MULTILIB := first
+LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE)
+LOCAL_SDK_VERSION := 28
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
+
+dexloggertest_so := $(LOCAL_BUILT_MODULE)
+
+# And a standalone native executable that we can exec.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE := DexLoggerNativeExecutable
+LOCAL_SRC_FILES := src/cpp/test_executable.cpp
+
+include $(BUILD_EXECUTABLE)
+
+dexloggertest_executable := $(LOCAL_BUILT_MODULE)
+
# Build the test app itself
include $(CLEAR_VARS)
@@ -37,14 +66,18 @@ LOCAL_MODULE_TAGS := tests
LOCAL_PACKAGE_NAME := DexLoggerIntegrationTests
LOCAL_SDK_VERSION := current
LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := platform
+LOCAL_CERTIFICATE := shared
LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm)
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
truth-prebuilt \
-# This gets us the javalib.jar built by DexLoggerTestLibrary above.
-LOCAL_JAVA_RESOURCE_FILES := $(dexloggertest_jar)
+# This gets us the javalib.jar built by DexLoggerTestLibrary above as well as the various
+# native binaries.
+LOCAL_JAVA_RESOURCE_FILES := \
+ $(dexloggertest_jar) \
+ $(dexloggertest_so) \
+ $(dexloggertest_executable)
include $(BUILD_PACKAGE)
diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
index 75ee0896c23a..d68769b378b9 100644
--- a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
+++ b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
@@ -17,6 +17,7 @@
package com.android.server.pm.dex;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import android.app.UiAutomation;
import android.content.Context;
@@ -25,6 +26,7 @@ import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.util.EventLog;
+import android.util.EventLog.Event;
import dalvik.system.DexClassLoader;
@@ -65,14 +67,13 @@ public final class DexLoggerIntegrationTests {
// Event log tag used for SNET related events
private static final int SNET_TAG = 0x534e4554;
- // Subtag used to distinguish dynamic code loading events
- private static final String DCL_SUBTAG = "dcl";
+ // Subtags used to distinguish dynamic code loading events
+ private static final String DCL_DEX_SUBTAG = "dcl";
+ private static final String DCL_NATIVE_SUBTAG = "dcln";
- // All the tags we care about
- private static final int[] TAG_LIST = new int[] { SNET_TAG };
-
- // This is {@code DynamicCodeLoggingService#JOB_ID}
- private static final int DYNAMIC_CODE_LOGGING_JOB_ID = 2030028;
+ // These are job IDs from DynamicCodeLoggingService
+ private static final int IDLE_LOGGING_JOB_ID = 2030028;
+ private static final int AUDIT_WATCHING_JOB_ID = 203142925;
private static Context sContext;
private static int sMyUid;
@@ -89,15 +90,20 @@ public final class DexLoggerIntegrationTests {
// Without this the first test passes and others don't - we don't see new events in the
// log. The exact reason is unclear.
EventLog.writeEvent(SNET_TAG, "Dummy event");
+
+ // Audit log messages are throttled by the kernel (at the request of logd) to 5 per
+ // second, so running the tests too quickly in sequence means we lose some and get
+ // spurious failures. Sigh.
+ SystemClock.sleep(1000);
}
@Test
- public void testDexLoggerGeneratesEvents() throws Exception {
- File privateCopyFile = fileForJar("copied.jar");
+ public void testDexLoggerGeneratesEvents_standardClassLoader() throws Exception {
+ File privateCopyFile = privateFile("copied.jar");
// Obtained via "echo -n copied.jar | sha256sum"
String expectedNameHash =
"1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C";
- String expectedContentHash = copyAndHashJar(privateCopyFile);
+ String expectedContentHash = copyAndHashResource("/javalib.jar", privateCopyFile);
// Feed the jar to a class loader and make sure it contains what we expect.
ClassLoader parentClassLoader = sContext.getClass().getClassLoader();
@@ -107,18 +113,18 @@ public final class DexLoggerIntegrationTests {
// And make sure we log events about it
long previousEventNanos = mostRecentEventTimeNanos();
- runDexLogger();
+ runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
- assertDclLoggedSince(previousEventNanos, expectedNameHash, expectedContentHash);
+ assertDclLoggedSince(previousEventNanos, DCL_DEX_SUBTAG,
+ expectedNameHash, expectedContentHash);
}
@Test
-
public void testDexLoggerGeneratesEvents_unknownClassLoader() throws Exception {
- File privateCopyFile = fileForJar("copied2.jar");
+ File privateCopyFile = privateFile("copied2.jar");
String expectedNameHash =
"202158B6A3169D78F1722487205A6B036B3F2F5653FDCFB4E74710611AC7EB93";
- String expectedContentHash = copyAndHashJar(privateCopyFile);
+ String expectedContentHash = copyAndHashResource("/javalib.jar", privateCopyFile);
// This time make sure an unknown class loader is an ancestor of the class loader we use.
ClassLoader knownClassLoader = sContext.getClass().getClassLoader();
@@ -129,22 +135,185 @@ public final class DexLoggerIntegrationTests {
// And make sure we log events about it
long previousEventNanos = mostRecentEventTimeNanos();
- runDexLogger();
+ runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+ assertDclLoggedSince(previousEventNanos, DCL_DEX_SUBTAG,
+ expectedNameHash, expectedContentHash);
+ }
+
+ @Test
+ public void testDexLoggerGeneratesEvents_nativeLibrary() throws Exception {
+ File privateCopyFile = privateFile("copied.so");
+ String expectedNameHash =
+ "996223BAD4B4FE75C57A3DEC61DB9C0B38E0A7AD479FC95F33494F4BC55A0F0E";
+ String expectedContentHash =
+ copyAndHashResource("/DexLoggerNativeTestLibrary.so", privateCopyFile);
+
+ System.load(privateCopyFile.toString());
+
+ // Run the job to scan generated audit log entries
+ runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+ // And then make sure we log events about it
+ long previousEventNanos = mostRecentEventTimeNanos();
+ runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
- assertDclLoggedSince(previousEventNanos, expectedNameHash, expectedContentHash);
+ assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
+ expectedNameHash, expectedContentHash);
}
- private static File fileForJar(String name) {
- return new File(sContext.getDir("jars", Context.MODE_PRIVATE), name);
+ @Test
+ public void testDexLoggerGeneratesEvents_nativeLibrary_escapedName() throws Exception {
+ // A file name with a space will be escaped in the audit log; verify we un-escape it
+ // correctly.
+ File privateCopyFile = privateFile("second copy.so");
+ String expectedNameHash =
+ "8C39990C560B4F36F83E208E279F678746FE23A790E4C50F92686584EA2041CA";
+ String expectedContentHash =
+ copyAndHashResource("/DexLoggerNativeTestLibrary.so", privateCopyFile);
+
+ System.load(privateCopyFile.toString());
+
+ // Run the job to scan generated audit log entries
+ runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+ // And then make sure we log events about it
+ long previousEventNanos = mostRecentEventTimeNanos();
+ runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+ assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
+ expectedNameHash, expectedContentHash);
}
- private static String copyAndHashJar(File copyTo) throws Exception {
+ @Test
+ public void testDexLoggerGeneratesEvents_nativeExecutable() throws Exception {
+ File privateCopyFile = privateFile("test_executable");
+ String expectedNameHash =
+ "3FBEC3F925A132D18F347F11AE9A5BB8DE1238828F8B4E064AA86EB68BD46DCF";
+ String expectedContentHash =
+ copyAndHashResource("/DexLoggerNativeExecutable", privateCopyFile);
+ assertThat(privateCopyFile.setExecutable(true)).isTrue();
+
+ Process process = Runtime.getRuntime().exec(privateCopyFile.toString());
+ int exitCode = process.waitFor();
+ assertThat(exitCode).isEqualTo(0);
+
+ // Run the job to scan generated audit log entries
+ runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+ // And then make sure we log events about it
+ long previousEventNanos = mostRecentEventTimeNanos();
+ runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+ assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
+ expectedNameHash, expectedContentHash);
+ }
+
+ @Test
+ public void testDexLoggerGeneratesEvents_spoofed_validFile() throws Exception {
+ File privateCopyFile = privateFile("spoofed");
+
+ String expectedContentHash =
+ copyAndHashResource("/DexLoggerNativeExecutable", privateCopyFile);
+
+ EventLog.writeEvent(EventLog.getTagCode("auditd"),
+ "type=1400 avc: granted { execute_no_trans } "
+ + "path=\"" + privateCopyFile + "\" "
+ + "scontext=u:r:untrusted_app_27: "
+ + "tcontext=u:object_r:app_data_file: "
+ + "tclass=file ");
+
+ String expectedNameHash =
+ "1CF36F503A02877BB775DC23C1C5A47A95F2684B6A1A83B11795B856D88861E3";
+
+ // Run the job to scan generated audit log entries
+ runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+ // And then make sure we log events about it
+ long previousEventNanos = mostRecentEventTimeNanos();
+ runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+ assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG,
+ expectedNameHash, expectedContentHash);
+ }
+
+ @Test
+ public void testDexLoggerGeneratesEvents_spoofed_pathTraversal() throws Exception {
+ File privateDir = privateFile("x").getParentFile();
+
+ // Transform /a/b/c -> /a/b/c/../../.. so we get back to the root
+ File pathTraversalToRoot = privateDir;
+ File root = new File("/");
+ while (!privateDir.equals(root)) {
+ pathTraversalToRoot = new File(pathTraversalToRoot, "..");
+ privateDir = privateDir.getParentFile();
+ }
+
+ File spoofedFile = new File(pathTraversalToRoot, "dev/urandom");
+
+ assertWithMessage("Expected " + spoofedFile + " to be readable")
+ .that(spoofedFile.canRead()).isTrue();
+
+ EventLog.writeEvent(EventLog.getTagCode("auditd"),
+ "type=1400 avc: granted { execute_no_trans } "
+ + "path=\"" + spoofedFile + "\" "
+ + "scontext=u:r:untrusted_app_27: "
+ + "tcontext=u:object_r:app_data_file: "
+ + "tclass=file ");
+
+ String expectedNameHash =
+ "65528FE876BD676B0DFCC9A8ACA8988E026766F99EEC1E1FB48F46B2F635E225";
+
+ // Run the job to scan generated audit log entries
+ runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+ // And then trigger generating DCL events
+ long previousEventNanos = mostRecentEventTimeNanos();
+ runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+ assertNoDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG, expectedNameHash);
+ }
+
+ @Test
+ public void testDexLoggerGeneratesEvents_spoofed_otherAppFile() throws Exception {
+ File ourPath = sContext.getDatabasePath("android_pay");
+ File targetPath = new File(ourPath.toString()
+ .replace("com.android.frameworks.dexloggertest", "com.google.android.gms"));
+
+ assertWithMessage("Expected " + targetPath + " to not be readable")
+ .that(targetPath.canRead()).isFalse();
+
+ EventLog.writeEvent(EventLog.getTagCode("auditd"),
+ "type=1400 avc: granted { execute_no_trans } "
+ + "path=\"" + targetPath + "\" "
+ + "scontext=u:r:untrusted_app_27: "
+ + "tcontext=u:object_r:app_data_file: "
+ + "tclass=file ");
+
+ String expectedNameHash =
+ "CBE04E8AB9E7199FC19CBAAF9C774B88E56B3B19E823F2251693380AD6F515E6";
+
+ // Run the job to scan generated audit log entries
+ runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID);
+
+ // And then trigger generating DCL events
+ long previousEventNanos = mostRecentEventTimeNanos();
+ runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID);
+
+ assertNoDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG, expectedNameHash);
+ }
+
+ private static File privateFile(String name) {
+ return new File(sContext.getDir("dcl", Context.MODE_PRIVATE), name);
+ }
+
+ private static String copyAndHashResource(String resourcePath, File copyTo) throws Exception {
MessageDigest hasher = MessageDigest.getInstance("SHA-256");
// Copy the jar from our Java resources to a private data directory
Class<?> thisClass = DexLoggerIntegrationTests.class;
- try (InputStream input = thisClass.getResourceAsStream("/javalib.jar");
- OutputStream output = new FileOutputStream(copyTo)) {
+ try (InputStream input = thisClass.getResourceAsStream(resourcePath);
+ OutputStream output = new FileOutputStream(copyTo)) {
byte[] buffer = new byte[1024];
while (true) {
int numRead = input.read(buffer);
@@ -166,24 +335,18 @@ public final class DexLoggerIntegrationTests {
return formatter.toString();
}
- private static long mostRecentEventTimeNanos() throws Exception {
- List<EventLog.Event> events = new ArrayList<>();
-
- EventLog.readEvents(TAG_LIST, events);
- return events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos();
- }
-
- private static void runDexLogger() throws Exception {
- // This forces {@code DynamicCodeLoggingService} to start now.
- runCommand("cmd jobscheduler run -f android " + DYNAMIC_CODE_LOGGING_JOB_ID);
+ private static void runDynamicCodeLoggingJob(int jobId) throws Exception {
+ // This forces the DynamicCodeLoggingService job to start now.
+ runCommand("cmd jobscheduler run -f android " + jobId);
// Wait for the job to have run.
long startTime = SystemClock.elapsedRealtime();
while (true) {
String response = runCommand(
- "cmd jobscheduler get-job-state android " + DYNAMIC_CODE_LOGGING_JOB_ID);
+ "cmd jobscheduler get-job-state android " + jobId);
if (!response.contains("pending") && !response.contains("active")) {
break;
}
+ // Don't wait forever - if it's taken > 10s then something is very wrong.
if (SystemClock.elapsedRealtime() - startTime > TimeUnit.SECONDS.toMillis(10)) {
throw new AssertionError("Job has not completed: " + response);
}
@@ -208,37 +371,68 @@ public final class DexLoggerIntegrationTests {
return response.toString("UTF-8");
}
- private static void assertDclLoggedSince(long previousEventNanos, String expectedNameHash,
- String expectedContentHash) throws Exception {
- List<EventLog.Event> events = new ArrayList<>();
- EventLog.readEvents(TAG_LIST, events);
- int found = 0;
- for (EventLog.Event event : events) {
+ private static long mostRecentEventTimeNanos() throws Exception {
+ List<Event> events = readSnetEvents();
+ return events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos();
+ }
+
+ private static void assertDclLoggedSince(long previousEventNanos, String expectedSubTag,
+ String expectedNameHash, String expectedContentHash) throws Exception {
+ List<String> messages =
+ findMatchingEvents(previousEventNanos, expectedSubTag, expectedNameHash);
+
+ assertWithMessage("Expected exactly one matching log entry").that(messages).hasSize(1);
+ assertThat(messages.get(0)).endsWith(expectedContentHash);
+ }
+
+ private static void assertNoDclLoggedSince(long previousEventNanos, String expectedSubTag,
+ String expectedNameHash) throws Exception {
+ List<String> messages =
+ findMatchingEvents(previousEventNanos, expectedSubTag, expectedNameHash);
+
+ assertWithMessage("Expected no matching log entries").that(messages).isEmpty();
+ }
+
+ private static List<String> findMatchingEvents(long previousEventNanos, String expectedSubTag,
+ String expectedNameHash) throws Exception {
+ List<String> messages = new ArrayList<>();
+
+ for (Event event : readSnetEvents()) {
if (event.getTimeNanos() <= previousEventNanos) {
continue;
}
- Object[] data = (Object[]) event.getData();
+
+ Object data = event.getData();
+ if (!(data instanceof Object[])) {
+ continue;
+ }
+ Object[] fields = (Object[]) data;
// We only care about DCL events that we generated.
- String subTag = (String) data[0];
- if (!DCL_SUBTAG.equals(subTag)) {
+ String subTag = (String) fields[0];
+ if (!expectedSubTag.equals(subTag)) {
continue;
}
- int uid = (int) data[1];
+ int uid = (int) fields[1];
if (uid != sMyUid) {
continue;
}
- String message = (String) data[2];
+ String message = (String) fields[2];
if (!message.startsWith(expectedNameHash)) {
continue;
}
- assertThat(message).endsWith(expectedContentHash);
- ++found;
+ messages.add(message);
+ //assertThat(message).endsWith(expectedContentHash);
}
+ return messages;
+ }
- assertThat(found).isEqualTo(1);
+ private static List<Event> readSnetEvents() throws Exception {
+ List<Event> events = new ArrayList<>();
+ EventLog.readEvents(new int[] { SNET_TAG }, events);
+ return events;
}
/**
diff --git a/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/Alarm.aidl b/tests/DexLoggerIntegrationTests/src/cpp/com_android_dcl_Jni.cpp
index 62a8c4866f07..060888310b51 100644
--- a/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/Alarm.aidl
+++ b/tests/DexLoggerIntegrationTests/src/cpp/com_android_dcl_Jni.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.testing.alarmservice;
+#include "jni.h"
-interface Alarm {
- int prepare();
- int setAlarmAndWait(long timeoutMills);
- int done();
-} \ No newline at end of file
+extern "C" jint JNI_OnLoad(JavaVM* /* vm */, void* /* reserved */)
+{
+ return JNI_VERSION_1_6;
+}
diff --git a/tests/DexLoggerIntegrationTests/src/cpp/test_executable.cpp b/tests/DexLoggerIntegrationTests/src/cpp/test_executable.cpp
new file mode 100644
index 000000000000..ad025e696dec
--- /dev/null
+++ b/tests/DexLoggerIntegrationTests/src/cpp/test_executable.cpp
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+int main() {
+ // This program just has to run, it doesn't need to do anything. So we don't.
+ return 0;
+}
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index c2e735e184b0..ec6f4b55d3ea 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -38,7 +38,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -470,7 +469,7 @@ public class RollbackTest {
* Test that app user data is rolled back.
* TODO: Stop ignoring this test once user data rollback is supported.
*/
- @Ignore @Test
+ @Test
public void testUserDataRollback() throws Exception {
try {
RollbackTestUtils.adoptShellPermissionIdentity(
@@ -479,9 +478,9 @@ public class RollbackTest {
Manifest.permission.MANAGE_ROLLBACKS);
RollbackTestUtils.uninstall(TEST_APP_A);
- RollbackTestUtils.install("RollbackTestAppV1.apk", false);
+ RollbackTestUtils.install("RollbackTestAppAv1.apk", false);
processUserData(TEST_APP_A);
- RollbackTestUtils.install("RollbackTestAppV2.apk", true);
+ RollbackTestUtils.install("RollbackTestAppAv2.apk", true);
processUserData(TEST_APP_A);
RollbackManager rm = RollbackTestUtils.getRollbackManager();
diff --git a/tests/net/java/android/net/DnsPacketTest.java b/tests/net/java/android/net/DnsPacketTest.java
new file mode 100644
index 000000000000..032e52666970
--- /dev/null
+++ b/tests/net/java/android/net/DnsPacketTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DnsPacketTest {
+ private void assertHeaderParses(DnsPacket.DnsHeader header, int id, int flag,
+ int qCount, int aCount, int nsCount, int arCount) {
+ assertEquals(header.id, id);
+ assertEquals(header.flags, flag);
+ assertEquals(header.getSectionCount(DnsPacket.QDSECTION), qCount);
+ assertEquals(header.getSectionCount(DnsPacket.ANSECTION), aCount);
+ assertEquals(header.getSectionCount(DnsPacket.NSSECTION), nsCount);
+ assertEquals(header.getSectionCount(DnsPacket.ARSECTION), arCount);
+ }
+
+ private void assertSectionParses(DnsPacket.DnsSection section, String dname,
+ int dtype, int dclass, int ttl, byte[] rr) {
+ assertEquals(section.dName, dname);
+ assertEquals(section.nsType, dtype);
+ assertEquals(section.nsClass, dclass);
+ assertEquals(section.ttl, ttl);
+ assertTrue(Arrays.equals(section.getRR(), rr));
+ }
+
+ class TestDnsPacket extends DnsPacket {
+ TestDnsPacket(byte[] data) throws ParseException {
+ super(data);
+ }
+
+ public DnsHeader getHeader() {
+ return mHeader;
+ }
+ public List<DnsSection> getSectionList(int secType) {
+ return mSections[secType];
+ }
+ }
+
+ @Test
+ public void testNullDisallowed() {
+ try {
+ new TestDnsPacket(null);
+ fail("Exception not thrown for null byte array");
+ } catch (DnsPacket.ParseException e) {
+ }
+ }
+
+ @Test
+ public void testV4Answer() throws Exception {
+ final byte[] v4blob = new byte[] {
+ /* Header */
+ 0x55, 0x66, /* Transaction ID */
+ (byte) 0x81, (byte) 0x80, /* Flags */
+ 0x00, 0x01, /* Questions */
+ 0x00, 0x01, /* Answer RRs */
+ 0x00, 0x00, /* Authority RRs */
+ 0x00, 0x00, /* Additional RRs */
+ /* Queries */
+ 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ /* Answers */
+ (byte) 0xc0, 0x0c, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ 0x00, 0x00, 0x01, 0x2b, /* TTL */
+ 0x00, 0x04, /* Data length */
+ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 /* Address */
+ };
+ TestDnsPacket packet = new TestDnsPacket(v4blob);
+
+ // Header part
+ assertHeaderParses(packet.getHeader(), 0x5566, 0x8180, 1, 1, 0, 0);
+
+ // Section part
+ List<DnsPacket.DnsSection> qdSectionList =
+ packet.getSectionList(DnsPacket.QDSECTION);
+ assertEquals(qdSectionList.size(), 1);
+ assertSectionParses(qdSectionList.get(0), "www.google.com", 1, 1, 0, null);
+
+ List<DnsPacket.DnsSection> anSectionList =
+ packet.getSectionList(DnsPacket.ANSECTION);
+ assertEquals(anSectionList.size(), 1);
+ assertSectionParses(anSectionList.get(0), "www.google.com", 1, 1, 0x12b,
+ new byte[]{ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 });
+ }
+
+ @Test
+ public void testV6Answer() throws Exception {
+ final byte[] v6blob = new byte[] {
+ /* Header */
+ 0x77, 0x22, /* Transaction ID */
+ (byte) 0x81, (byte) 0x80, /* Flags */
+ 0x00, 0x01, /* Questions */
+ 0x00, 0x01, /* Answer RRs */
+ 0x00, 0x00, /* Authority RRs */
+ 0x00, 0x00, /* Additional RRs */
+ /* Queries */
+ 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x1c, /* Type */
+ 0x00, 0x01, /* Class */
+ /* Answers */
+ (byte) 0xc0, 0x0c, /* Name */
+ 0x00, 0x1c, /* Type */
+ 0x00, 0x01, /* Class */
+ 0x00, 0x00, 0x00, 0x37, /* TTL */
+ 0x00, 0x10, /* Data length */
+ 0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 /* Address */
+ };
+ TestDnsPacket packet = new TestDnsPacket(v6blob);
+
+ // Header part
+ assertHeaderParses(packet.getHeader(), 0x7722, 0x8180, 1, 1, 0, 0);
+
+ // Section part
+ List<DnsPacket.DnsSection> qdSectionList =
+ packet.getSectionList(DnsPacket.QDSECTION);
+ assertEquals(qdSectionList.size(), 1);
+ assertSectionParses(qdSectionList.get(0), "www.google.com", 28, 1, 0, null);
+
+ List<DnsPacket.DnsSection> anSectionList =
+ packet.getSectionList(DnsPacket.ANSECTION);
+ assertEquals(anSectionList.size(), 1);
+ assertSectionParses(anSectionList.get(0), "www.google.com", 28, 1, 0x37,
+ new byte[]{ 0x24, 0x04, 0x68, 0x00, 0x40, 0x05, 0x08, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x04 });
+ }
+}
diff --git a/tests/net/java/android/net/ip/IpClientTest.java b/tests/net/java/android/net/ip/IpClientTest.java
index a2dcfef50a49..5110ce1830b3 100644
--- a/tests/net/java/android/net/ip/IpClientTest.java
+++ b/tests/net/java/android/net/ip/IpClientTest.java
@@ -33,6 +33,7 @@ import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.content.Context;
import android.content.res.Resources;
+import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -82,6 +83,7 @@ public class IpClientTest {
private static final int TEST_TIMEOUT_MS = 400;
@Mock private Context mContext;
+ @Mock private ConnectivityManager mCm;
@Mock private INetworkManagementService mNMService;
@Mock private INetd mNetd;
@Mock private Resources mResources;
@@ -98,6 +100,9 @@ public class IpClientTest {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm);
+ when(mContext.getSystemServiceName(ConnectivityManager.class))
+ .thenReturn(Context.CONNECTIVITY_SERVICE);
+ when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm);
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
.thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
@@ -122,13 +127,14 @@ public class IpClientTest {
private IpClient makeIpClient(String ifname) throws Exception {
setTestInterfaceParams(ifname);
final IpClient ipc = new IpClient(mContext, ifname, mCb, mDependecies);
- verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(ifname);
- verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(ifname);
+ verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(ifname, false);
+ verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(ifname);
ArgumentCaptor<BaseNetworkObserver> arg =
ArgumentCaptor.forClass(BaseNetworkObserver.class);
verify(mNMService, times(1)).registerObserver(arg.capture());
mObserver = arg.getValue();
reset(mNMService);
+ reset(mNetd);
// Verify IpClient doesn't call onLinkPropertiesChange() when it starts.
verify(mCb, never()).onLinkPropertiesChange(any());
reset(mCb);
@@ -200,8 +206,8 @@ public class IpClientTest {
verify(mCb, never()).onProvisioningFailure(any());
ipc.shutdown();
- verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(iface);
- verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(iface);
+ verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
+ verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
.onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
}
@@ -251,8 +257,8 @@ public class IpClientTest {
verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(eq(want));
ipc.shutdown();
- verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(iface);
- verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(iface);
+ verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
+ verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
.onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
}
diff --git a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
index e65585f8ff0f..e3b5ddf6f4cf 100644
--- a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
+++ b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
@@ -16,11 +16,10 @@
package android.net.ip;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.Handler;
@@ -45,6 +44,7 @@ public class IpReachabilityMonitorTest {
@Mock IpReachabilityMonitor.Callback mCallback;
@Mock IpReachabilityMonitor.Dependencies mDependencies;
@Mock SharedLog mLog;
+ @Mock Context mContext;
Handler mHandler;
@Before
@@ -56,7 +56,8 @@ public class IpReachabilityMonitorTest {
IpReachabilityMonitor makeMonitor() {
final InterfaceParams ifParams = new InterfaceParams("fake0", 1, null);
- return new IpReachabilityMonitor(ifParams, mHandler, mLog, mCallback, null, mDependencies);
+ return new IpReachabilityMonitor(
+ mContext, ifParams, mHandler, mLog, mCallback, false, mDependencies);
}
@Test
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index c3162af1868d..80aac047a723 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -33,6 +33,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -47,6 +48,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.net.INetd;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
@@ -92,6 +94,7 @@ public class IpServerTest {
private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
@Mock private INetworkManagementService mNMService;
+ @Mock private INetd mNetd;
@Mock private INetworkStatsService mStatsService;
@Mock private IpServer.Callback mCallback;
@Mock private InterfaceConfiguration mInterfaceConfiguration;
@@ -112,16 +115,6 @@ public class IpServerTest {
}
private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception {
- mIpServer = new IpServer(
- IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
- mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies);
- mIpServer.start();
- // Starting the state machine always puts us in a consistent state and notifies
- // the rest of the world that we've changed from an unknown to available state.
- mLooper.dispatchAll();
- reset(mNMService, mStatsService, mCallback);
- when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
-
doAnswer(inv -> {
final IDhcpServerCallbacks cb = inv.getArgument(2);
new Thread(() -> {
@@ -135,6 +128,17 @@ public class IpServerTest {
}).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
+ when(mDependencies.getNetdService()).thenReturn(mNetd);
+
+ mIpServer = new IpServer(
+ IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
+ mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies);
+ mIpServer.start();
+ // Starting the state machine always puts us in a consistent state and notifies
+ // the rest of the world that we've changed from an unknown to available state.
+ mLooper.dispatchAll();
+ reset(mNMService, mStatsService, mCallback);
+ when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
when(mRaDaemon.start()).thenReturn(true);
}
@@ -223,9 +227,9 @@ public class IpServerTest {
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
+ InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback);
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
- inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
+ inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
inOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
@@ -318,12 +322,12 @@ public class IpServerTest {
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNMService, mStatsService, mCallback);
+ InOrder inOrder = inOrder(mNMService, mNetd, mStatsService, mCallback);
inOrder.verify(mStatsService).forceUpdate();
inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNMService).untetherInterface(IFACE_NAME);
- inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any());
+ inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
inOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
diff --git a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
index 6f711c0b5743..b6d01dbc1cac 100644
--- a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
+++ b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java
@@ -48,61 +48,52 @@ public class LinkPropertiesParcelableUtilTest {
private LinkProperties mLinkProperties;
private static final String TEST_LINKPROPS_IFACE = "TEST_IFACE";
- private static final String TEST_STACKED_LINK_1_IFACE = "TEST_STACKED_IFACE_1";
- private static final String TEST_STACKED_LINK_2_IFACE = "TEST_STACKED_IFACE_2";
@Before
public void setUp() {
- mLinkProperties = makeLinkProperties(TEST_LINKPROPS_IFACE);
- mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_1_IFACE));
- mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_2_IFACE));
- }
-
- private static LinkProperties makeLinkProperties(String iface) {
- final LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(iface);
- lp.setLinkAddresses(Arrays.asList(
+ mLinkProperties = new LinkProperties();
+ mLinkProperties.setInterfaceName(TEST_LINKPROPS_IFACE);
+ mLinkProperties.setLinkAddresses(Arrays.asList(
new LinkAddress(InetAddresses.parseNumericAddress("192.168.0.42"), 16),
new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::7"), 42)));
- lp.setDnsServers(Arrays.asList(
+ mLinkProperties.setDnsServers(Arrays.asList(
InetAddresses.parseNumericAddress("2001:db8::42"),
InetAddresses.parseNumericAddress("192.168.1.1")
));
- lp.setValidatedPrivateDnsServers(Arrays.asList(
+ mLinkProperties.setValidatedPrivateDnsServers(Arrays.asList(
InetAddresses.parseNumericAddress("2001:db8::43"),
InetAddresses.parseNumericAddress("192.168.42.43")
));
- lp.setPcscfServers(Arrays.asList(
+ mLinkProperties.setPcscfServers(Arrays.asList(
InetAddresses.parseNumericAddress("2001:db8::47"),
InetAddresses.parseNumericAddress("192.168.42.47")
));
- lp.setUsePrivateDns(true);
- lp.setPrivateDnsServerName("test.example.com");
- lp.setDomains("test1.example.com,test2.example.com");
- lp.addRoute(new RouteInfo(
+ mLinkProperties.setUsePrivateDns(true);
+ mLinkProperties.setPrivateDnsServerName("test.example.com");
+ mLinkProperties.setDomains("test1.example.com,test2.example.com");
+ mLinkProperties.addRoute(new RouteInfo(
new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::44"), 45),
InetAddresses.parseNumericAddress("2001:db8::45"),
- iface,
+ TEST_LINKPROPS_IFACE,
RouteInfo.RTN_UNICAST
));
- lp.addRoute(new RouteInfo(
+ mLinkProperties.addRoute(new RouteInfo(
new IpPrefix(InetAddresses.parseNumericAddress("192.168.44.45"), 16),
InetAddresses.parseNumericAddress("192.168.45.1"),
- iface,
+ TEST_LINKPROPS_IFACE,
RouteInfo.RTN_THROW
));
- lp.setHttpProxy(new ProxyInfo("test3.example.com", 8000,
+ mLinkProperties.setHttpProxy(new ProxyInfo("test3.example.com", 8000,
"excl1.example.com,excl2.example.com"));
- lp.setMtu(5000);
- lp.setTcpBufferSizes("1,2,3,4,5,6");
- lp.setNat64Prefix(new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::48"), 96));
+ mLinkProperties.setMtu(5000);
+ mLinkProperties.setTcpBufferSizes("1,2,3,4,5,6");
+ mLinkProperties.setNat64Prefix(
+ new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::48"), 96));
// Verify that this test does not miss any new field added later.
// If any added field is not included in LinkProperties#equals, assertLinkPropertiesEquals
// must also be updated.
assertFieldCountEquals(14, LinkProperties.class);
-
- return lp;
}
@Test
@@ -186,7 +177,7 @@ public class LinkPropertiesParcelableUtilTest {
private static void assertLinkPropertiesEquals(LinkProperties expected, LinkProperties actual) {
assertEquals(expected, actual);
- // LinkProperties equals() does not include stacked links
- assertEquals(expected.getStackedLinks(), actual.getStackedLinks());
+ // Equality on stacked links is not tested as they should not be passed to processes using
+ // LinkPropertiesParcelable.
}
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 1ea83c2bbb6b..b6356076db60 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -803,13 +803,14 @@ public class TetheringTest {
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
- // We verify get/set called thrice here: once for setup and twice during
- // teardown because all events happen over the course of the single
+ // We verify get/set called thrice here: twice for setup (on NMService) and once during
+ // teardown (on Netd) because all events happen over the course of the single
// dispatchAll() above. Note that once the IpServer IPv4 address config
// code is refactored the two calls during shutdown will revert to one.
verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
- verify(mNMService, times(3))
+ verify(mNMService, times(2))
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
+ verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 9bf758797ed2..0b74d878f069 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -57,7 +57,6 @@ import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
import android.net.IpPrefix;
import android.net.LinkProperties;
import android.net.Network;
@@ -97,7 +96,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@@ -240,6 +238,30 @@ public class VpnTest {
}
@Test
+ public void testGetAlwaysAndOnGetLockDown() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+
+ // Default state.
+ assertFalse(vpn.getAlwaysOn());
+ assertFalse(vpn.getLockdown());
+
+ // Set always-on without lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false));
+ assertTrue(vpn.getAlwaysOn());
+ assertFalse(vpn.getLockdown());
+
+ // Set always-on with lockdown.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true));
+ assertTrue(vpn.getAlwaysOn());
+ assertTrue(vpn.getLockdown());
+
+ // Remove always-on configuration.
+ assertTrue(vpn.setAlwaysOnPackage(null, false));
+ assertFalse(vpn.getAlwaysOn());
+ assertFalse(vpn.getLockdown());
+ }
+
+ @Test
public void testLockdownChangingPackage() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = UidRange.createForUser(primaryUser.id);
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
index 94bcd28bf009..f2ecef95b599 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/IpMemoryStoreServiceTest.java
@@ -27,10 +27,14 @@ import static org.mockito.Mockito.doReturn;
import android.content.Context;
import android.net.ipmemorystore.Blob;
import android.net.ipmemorystore.IOnBlobRetrievedListener;
+import android.net.ipmemorystore.IOnL2KeyResponseListener;
import android.net.ipmemorystore.IOnNetworkAttributesRetrieved;
+import android.net.ipmemorystore.IOnSameNetworkResponseListener;
import android.net.ipmemorystore.IOnStatusListener;
import android.net.ipmemorystore.NetworkAttributes;
import android.net.ipmemorystore.NetworkAttributesParcelable;
+import android.net.ipmemorystore.SameL3NetworkResponse;
+import android.net.ipmemorystore.SameL3NetworkResponseParcelable;
import android.net.ipmemorystore.Status;
import android.net.ipmemorystore.StatusParcelable;
import android.os.IBinder;
@@ -53,7 +57,6 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
-import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -65,6 +68,15 @@ public class IpMemoryStoreServiceTest {
private static final String TEST_CLIENT_ID = "testClientId";
private static final String TEST_DATA_NAME = "testData";
+ private static final int FAKE_KEY_COUNT = 20;
+ private static final String[] FAKE_KEYS;
+ static {
+ FAKE_KEYS = new String[FAKE_KEY_COUNT];
+ for (int i = 0; i < FAKE_KEYS.length; ++i) {
+ FAKE_KEYS[i] = "fakeKey" + i;
+ }
+ }
+
@Mock
private Context mMockContext;
private File mDbFile;
@@ -130,8 +142,8 @@ public class IpMemoryStoreServiceTest {
final OnNetworkAttributesRetrievedListener functor) {
return new IOnNetworkAttributesRetrieved() {
@Override
- public void onL2KeyResponse(final StatusParcelable status, final String l2Key,
- final NetworkAttributesParcelable attributes)
+ public void onNetworkAttributesRetrieved(final StatusParcelable status,
+ final String l2Key, final NetworkAttributesParcelable attributes)
throws RemoteException {
functor.onNetworkAttributesRetrieved(new Status(status), l2Key,
null == attributes ? null : new NetworkAttributes(attributes));
@@ -144,6 +156,47 @@ public class IpMemoryStoreServiceTest {
};
}
+ /** Helper method to make an IOnSameNetworkResponseListener */
+ private interface OnSameNetworkResponseListener {
+ void onSameNetworkResponse(Status status, SameL3NetworkResponse answer);
+ }
+ private IOnSameNetworkResponseListener onSameResponse(
+ final OnSameNetworkResponseListener functor) {
+ return new IOnSameNetworkResponseListener() {
+ @Override
+ public void onSameNetworkResponse(final StatusParcelable status,
+ final SameL3NetworkResponseParcelable sameL3Network)
+ throws RemoteException {
+ functor.onSameNetworkResponse(new Status(status),
+ null == sameL3Network ? null : new SameL3NetworkResponse(sameL3Network));
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+ }
+
+ /** Helper method to make an IOnL2KeyResponseListener */
+ private interface OnL2KeyResponseListener {
+ void onL2KeyResponse(Status status, String key);
+ }
+ private IOnL2KeyResponseListener onL2KeyResponse(final OnL2KeyResponseListener functor) {
+ return new IOnL2KeyResponseListener() {
+ @Override
+ public void onL2KeyResponse(final StatusParcelable status, final String key)
+ throws RemoteException {
+ functor.onL2KeyResponse(new Status(status), key);
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+ }
+
// Helper method to factorize some boilerplate
private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor) {
final CountDownLatch latch = new CountDownLatch(1);
@@ -155,24 +208,28 @@ public class IpMemoryStoreServiceTest {
}
}
+ // Helper methods to factorize more boilerplate
+ private void storeAttributes(final String l2Key, final NetworkAttributes na) {
+ storeAttributes("Did not complete storing attributes", l2Key, na);
+ }
+ private void storeAttributes(final String timeoutMessage, final String l2Key,
+ final NetworkAttributes na) {
+ doLatched(timeoutMessage, latch -> mService.storeNetworkAttributes(l2Key, na.toParcelable(),
+ onStatus(status -> {
+ assertTrue("Store not successful : " + status.resultCode, status.isSuccess());
+ latch.countDown();
+ })));
+ }
+
@Test
- public void testNetworkAttributes() {
+ public void testNetworkAttributes() throws UnknownHostException {
final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
- try {
- na.setAssignedV4Address(
- (Inet4Address) Inet4Address.getByAddress(new byte[]{1, 2, 3, 4}));
- } catch (UnknownHostException e) { /* Can't happen */ }
+ na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
na.setGroupHint("hint1");
na.setMtu(219);
- final String l2Key = UUID.randomUUID().toString();
+ final String l2Key = FAKE_KEYS[0];
NetworkAttributes attributes = na.build();
- doLatched("Did not complete storing attributes", latch ->
- mService.storeNetworkAttributes(l2Key, attributes.toParcelable(),
- onStatus(status -> {
- assertTrue("Store status not successful : " + status.resultCode,
- status.isSuccess());
- latch.countDown();
- })));
+ storeAttributes(l2Key, attributes);
doLatched("Did not complete retrieving attributes", latch ->
mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved(
@@ -185,14 +242,10 @@ public class IpMemoryStoreServiceTest {
})));
final NetworkAttributes.Builder na2 = new NetworkAttributes.Builder();
- try {
- na.setDnsAddresses(Arrays.asList(
- new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
- } catch (UnknownHostException e) { /* Still can't happen */ }
+ na.setDnsAddresses(Arrays.asList(
+ new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
final NetworkAttributes attributes2 = na2.build();
- doLatched("Did not complete storing attributes 2", latch ->
- mService.storeNetworkAttributes(l2Key, attributes2.toParcelable(),
- onStatus(status -> latch.countDown())));
+ storeAttributes("Did not complete storing attributes 2", l2Key, attributes2);
doLatched("Did not complete retrieving attributes 2", latch ->
mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved(
@@ -268,7 +321,7 @@ public class IpMemoryStoreServiceTest {
public void testPrivateData() {
final Blob b = new Blob();
b.data = new byte[] { -3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34 };
- final String l2Key = UUID.randomUUID().toString();
+ final String l2Key = FAKE_KEYS[0];
doLatched("Did not complete storing private data", latch ->
mService.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
onStatus(status -> {
@@ -302,12 +355,139 @@ public class IpMemoryStoreServiceTest {
}
@Test
- public void testFindL2Key() {
- // TODO : implement this
+ public void testFindL2Key() throws UnknownHostException {
+ final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
+ na.setGroupHint("hint0");
+ storeAttributes(FAKE_KEYS[0], na.build());
+
+ na.setDnsAddresses(Arrays.asList(
+ new InetAddress[] {Inet6Address.getByName("8D56:9AF1::08EE:20F1")}));
+ na.setMtu(219);
+ storeAttributes(FAKE_KEYS[1], na.build());
+ na.setMtu(null);
+ na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+ na.setDnsAddresses(Arrays.asList(
+ new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
+ na.setGroupHint("hint1");
+ storeAttributes(FAKE_KEYS[2], na.build());
+ na.setMtu(219);
+ storeAttributes(FAKE_KEYS[3], na.build());
+ na.setMtu(240);
+ storeAttributes(FAKE_KEYS[4], na.build());
+ na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("5.6.7.8"));
+ storeAttributes(FAKE_KEYS[5], na.build());
+
+ // Matches key 5 exactly
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[5], key);
+ })));
+
+ // MTU matches key 4 but v4 address matches key 5. The latter is stronger.
+ na.setMtu(240);
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[5], key);
+ })));
+
+ // Closest to key 3 (indeed, identical)
+ na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+ na.setMtu(219);
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[3], key);
+ })));
+
+ // Group hint alone must not be strong enough to override the rest
+ na.setGroupHint("hint0");
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[3], key);
+ })));
+
+ // Still closest to key 3, though confidence is lower
+ na.setGroupHint("hint1");
+ na.setDnsAddresses(null);
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[3], key);
+ })));
+
+ // But changing the MTU makes this closer to key 4
+ na.setMtu(240);
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(FAKE_KEYS[4], key);
+ })));
+
+ // MTU alone not strong enough to make this group-close
+ na.setGroupHint(null);
+ na.setDnsAddresses(null);
+ na.setAssignedV4Address(null);
+ doLatched("Did not finish finding L2Key", latch ->
+ mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertNull(key);
+ })));
+ }
+
+ private void assertNetworksSameness(final String key1, final String key2, final int sameness) {
+ doLatched("Did not finish evaluating sameness", latch ->
+ mService.isSameNetwork(key1, key2, onSameResponse((status, answer) -> {
+ assertTrue("Retrieve network sameness not successful : " + status.resultCode,
+ status.isSuccess());
+ assertEquals(sameness, answer.getNetworkSameness());
+ })));
}
@Test
- public void testIsSameNetwork() {
- // TODO : implement this
+ public void testIsSameNetwork() throws UnknownHostException {
+ final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
+ na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+ na.setGroupHint("hint1");
+ na.setMtu(219);
+ na.setDnsAddresses(Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6")));
+
+ storeAttributes(FAKE_KEYS[0], na.build());
+ // 0 and 1 have identical attributes
+ storeAttributes(FAKE_KEYS[1], na.build());
+
+ // Hopefully only the MTU being different still means it's the same network
+ na.setMtu(200);
+ storeAttributes(FAKE_KEYS[2], na.build());
+
+ // Hopefully different MTU, assigned V4 address and grouphint make a different network,
+ // even with identical DNS addresses
+ na.setAssignedV4Address(null);
+ na.setGroupHint("hint2");
+ storeAttributes(FAKE_KEYS[3], na.build());
+
+ assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[1], SameL3NetworkResponse.NETWORK_SAME);
+ assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[2], SameL3NetworkResponse.NETWORK_SAME);
+ assertNetworksSameness(FAKE_KEYS[1], FAKE_KEYS[2], SameL3NetworkResponse.NETWORK_SAME);
+ assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[3], SameL3NetworkResponse.NETWORK_DIFFERENT);
+ assertNetworksSameness(FAKE_KEYS[0], "neverInsertedKey",
+ SameL3NetworkResponse.NETWORK_NEVER_CONNECTED);
+
+ doLatched("Did not finish evaluating sameness", latch ->
+ mService.isSameNetwork(null, null, onSameResponse((status, answer) -> {
+ assertFalse("Retrieve network sameness suspiciously successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
+ assertNull(answer);
+ })));
}
}
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
new file mode 100644
index 000000000000..fe19eee4594c
--- /dev/null
+++ b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.net.ipmemorystore;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.ipmemorystore.NetworkAttributes;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+/** Unit tests for {@link NetworkAttributes}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NetworkAttributesTest {
+ private static final String WEIGHT_FIELD_NAME_PREFIX = "WEIGHT_";
+ private static final float EPSILON = 0.0001f;
+
+ // This is running two tests to make sure the total weight is the sum of all weights. To be
+ // sure this is not fireproof, but you'd kind of need to do it on purpose to pass.
+ @Test
+ public void testTotalWeight() throws IllegalAccessException, UnknownHostException {
+ // Make sure that TOTAL_WEIGHT is equal to the sum of the fields starting with WEIGHT_
+ float sum = 0f;
+ final Field[] fieldList = NetworkAttributes.class.getDeclaredFields();
+ for (final Field field : fieldList) {
+ if (!field.getName().startsWith(WEIGHT_FIELD_NAME_PREFIX)) continue;
+ field.setAccessible(true);
+ sum += (float) field.get(null);
+ }
+ assertEquals(sum, NetworkAttributes.TOTAL_WEIGHT, EPSILON);
+
+ // Use directly the constructor with all attributes, and make sure that when compared
+ // to itself the score is a clean 1.0f.
+ final NetworkAttributes na =
+ new NetworkAttributes(
+ (Inet4Address) Inet4Address.getByAddress(new byte[] {1, 2, 3, 4}),
+ "some hint",
+ Arrays.asList(Inet4Address.getByAddress(new byte[] {5, 6, 7, 8}),
+ Inet4Address.getByAddress(new byte[] {9, 0, 1, 2})),
+ 98);
+ assertEquals(1.0f, na.getNetworkGroupSamenessConfidence(na), EPSILON);
+ }
+}
diff --git a/tests/utils/SleepUtils/AlarmService/Android.mk b/tests/utils/SleepUtils/AlarmService/Android.mk
deleted file mode 100644
index 9022f03b7cf2..000000000000
--- a/tests/utils/SleepUtils/AlarmService/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SRC_FILES += \
- src/com/android/testing/alarmservice/Alarm.aidl
-LOCAL_PACKAGE_NAME := SleepUtilsAlarmService
-LOCAL_SDK_VERSION := 7
-include $(BUILD_PACKAGE)
diff --git a/tests/utils/SleepUtils/AlarmService/AndroidManifest.xml b/tests/utils/SleepUtils/AlarmService/AndroidManifest.xml
deleted file mode 100644
index 1b6de39c91da..000000000000
--- a/tests/utils/SleepUtils/AlarmService/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.testing.alarmservice" >
-
- <uses-sdk android:minSdkVersion="7" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
-
- <application android:label="Sleep Utils Alarm Service">
- <service android:name=".AlarmService"
- android:label="Sleep Utils Alarm Service"
- android:exported="true"
- android:enabled="true">
- <intent-filter>
- <action android:name="com.android.testing.ALARM_SERVICE" />
- </intent-filter>
- </service>
- <receiver android:name=".WakeUpCall">
- <intent-filter>
- <action android:name="com.android.testing.alarmservice.WAKEUP" />
- </intent-filter>
- </receiver>
- </application>
-</manifest>
diff --git a/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmImpl.java b/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmImpl.java
deleted file mode 100644
index 122d55deb3e5..000000000000
--- a/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmImpl.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2013 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.testing.alarmservice;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.testing.alarmservice.Alarm.Stub;
-
-public class AlarmImpl extends Stub {
-
- private static final String LOG_TAG = AlarmImpl.class.getSimpleName();
-
- private Context mContext;
-
- public AlarmImpl(Context context) {
- super();
- mContext = context;
- }
-
- @Override
- public int prepare() throws RemoteException {
- WakeUpController.getController().getWakeLock().acquire();
- Log.d(LOG_TAG, "AlarmService prepared, wake lock acquired");
- return 0;
- }
-
- @Override
- public int setAlarmAndWait(long timeoutMills) throws RemoteException {
- // calculate when device should be waken up
- long atTime = SystemClock.elapsedRealtime() + timeoutMills;
- AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- Intent wakupIntent = new Intent(WakeUpCall.WAKEUP_CALL);
- PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, wakupIntent, 0);
- // set alarm, which will be delivered in form of the wakeupIntent
- am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, atTime, pi);
- Log.d(LOG_TAG, String.format("Alarm set: %d, giving up wake lock", atTime));
- Object lock = WakeUpController.getController().getWakeSync();
- // release wakelock and wait for the lock to be poked from the broadcast receiver
- WakeUpController.getController().getWakeLock().release();
- // does not really matter if device enters suspend before we start waiting on lock
- synchronized (lock) {
- try {
- lock.wait();
- } catch (InterruptedException e) {
- }
- }
- Log.d(LOG_TAG, String.format("Alarm triggered, done waiting"));
- return 0;
- }
-
- @Override
- public int done() throws RemoteException {
- WakeUpController.getController().getWakeLock().release();
- return 0;
- }
-
-}
diff --git a/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmService.java b/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmService.java
deleted file mode 100644
index 576a1cfcfbaf..000000000000
--- a/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/AlarmService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2013 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.testing.alarmservice;
-
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class AlarmService extends Service {
-
- private AlarmImpl mAlarmImpl = null;
- static Context sContext;
-
- @Override
- public void onCreate() {
- super.onCreate();
- sContext = this;
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return getAlarmImpl();
- }
-
- private AlarmImpl getAlarmImpl() {
- if (mAlarmImpl == null) {
- mAlarmImpl = new AlarmImpl(this);
- }
- return mAlarmImpl;
- }
-
- @Override
- public void onDestroy() {
- sContext = null;
- super.onDestroy();
- }
-}
diff --git a/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpCall.java b/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpCall.java
deleted file mode 100644
index f4bb4dba4cda..000000000000
--- a/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpCall.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2013 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.testing.alarmservice;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * The receiver for the alarm we set
- *
- */
-public class WakeUpCall extends BroadcastReceiver {
-
- public static final String WAKEUP_CALL = "com.android.testing.alarmservice.WAKEUP";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- // we acquire wakelock without release because user is supposed to manually release it
- WakeUpController.getController().getWakeLock().acquire();
- Object lock = WakeUpController.getController().getWakeSync();
- synchronized (lock) {
- // poke the lock so the service side can be woken from waiting on the lock
- lock.notifyAll();
- }
- }
-
-}
diff --git a/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpController.java b/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpController.java
deleted file mode 100644
index 478371f61f36..000000000000
--- a/tests/utils/SleepUtils/AlarmService/src/com/android/testing/alarmservice/WakeUpController.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2013 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.testing.alarmservice;
-
-import android.content.Context;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.util.Log;
-
-/**
- * A singleton used for controlling and sharing of states/wakelocks
- *
- */
-public class WakeUpController {
-
- private static final String LOG_TAG = WakeUpController.class.getName();
- private static WakeUpController mController = null;
- private WakeLock mWakeLock = null;
- private Object mWakeSync = new Object();
-
- private WakeUpController() {
- Log.i(LOG_TAG, "Created instance: 0x" + Integer.toHexString(this.hashCode()));
- }
-
- public static synchronized WakeUpController getController() {
- if (mController == null) {
- mController = new WakeUpController();
- }
- return mController;
- }
-
- public WakeLock getWakeLock() {
- if (mWakeLock == null) {
- PowerManager pm =
- (PowerManager) AlarmService.sContext.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "testing-alarmservice");
- Log.i(LOG_TAG, "Create wakelock: 0x" + Integer.toHexString(mWakeLock.hashCode()));
- }
- return mWakeLock;
- }
-
- public Object getWakeSync() {
- return mWakeSync;
- }
-}
diff --git a/tests/utils/SleepUtils/Android.mk b/tests/utils/SleepUtils/Android.mk
deleted file mode 100644
index 0e65e2255c5b..000000000000
--- a/tests/utils/SleepUtils/Android.mk
+++ /dev/null
@@ -1,2 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/tests/utils/SleepUtils/README b/tests/utils/SleepUtils/README
deleted file mode 100644
index bfe07da4a389..000000000000
--- a/tests/utils/SleepUtils/README
+++ /dev/null
@@ -1,23 +0,0 @@
-This folder contains utils to properly perform timed suspend and wakeup.
-
-AlarmService - a service that client can bind to and perform:
-1) holding wakelock (singleton to this service)
-2) setting alarm for a specified period and releasing the wakelock; service
- call will block until alarm has been triggered and the wakelock is held
-3) releasing the wakelock
-
-SleepHelper - a self instrumentation meant as a convenient way to trigger
-the service functions from command line. Corresponding to service function
-above, supported operations are:
-1) holding wakelock
-am instrument -w -e command prepare \
- com.android.testing.sleephelper/.SetAlarm
-
-2) setting alarm and wait til triggered
-am instrument -w -e command set_wait \
- -e param <time in ms> com.android.testing.sleephelper/.SetAlarm
-Note: for the function to work properly, "-w" parameter is required
-
-3) releasing wakelock
-am instrument -w -e command done \
- com.android.testing.sleephelper/.SetAlarm
diff --git a/tests/utils/SleepUtils/SleepHelper/Android.mk b/tests/utils/SleepUtils/SleepHelper/Android.mk
deleted file mode 100644
index f8267fd530ed..000000000000
--- a/tests/utils/SleepUtils/SleepHelper/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2013 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-# Only compile source java files in this apk.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SRC_FILES += \
- ../AlarmService/src/com/android/testing/alarmservice/Alarm.aidl
-LOCAL_SDK_VERSION := 7
-LOCAL_PACKAGE_NAME := SleepUtilsSleepHelper
-
-include $(BUILD_PACKAGE)
diff --git a/tests/utils/SleepUtils/SleepHelper/AndroidManifest.xml b/tests/utils/SleepUtils/SleepHelper/AndroidManifest.xml
deleted file mode 100644
index 0f1d49185169..000000000000
--- a/tests/utils/SleepUtils/SleepHelper/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.testing.sleephelper">
-
- <uses-sdk android:minSdkVersion="7" />
- <instrumentation android:label="Sleep Helper"
- android:name="com.android.testing.sleephelper.SetAlarm"
- android:targetPackage="com.android.testing.sleephelper" />
-
- <application android:label="Sleep Utils Sleep Helper">
- <uses-library android:name="android.test.runner" />
- </application>
-</manifest>
diff --git a/tests/utils/SleepUtils/SleepHelper/src/com/android/testing/sleephelper/SetAlarm.java b/tests/utils/SleepUtils/SleepHelper/src/com/android/testing/sleephelper/SetAlarm.java
deleted file mode 100644
index b558741da908..000000000000
--- a/tests/utils/SleepUtils/SleepHelper/src/com/android/testing/sleephelper/SetAlarm.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2013 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.testing.sleephelper;
-
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.testing.alarmservice.Alarm;
-
-public class SetAlarm extends Instrumentation {
-
- private static final String COMMAND = "command";
- private static final String PARAM = "param";
- private static final String CMD_PREPARE = "prepare";
- private static final String CMD_SET = "set_wait";
- private static final String CMD_DONE = "done";
- private static final String SERVICE_ACTION = "com.android.testing.ALARM_SERVICE";
- private static final String SERVICE_PKG = "com.android.testing.alarmservice";
- private static final String LOG_TAG = SetAlarm.class.getSimpleName();
-
- private Alarm mAlarmService = null;
- private Bundle mArgs = null;
- private String mCommand = null;
- private Intent mServceIntent = new Intent(SERVICE_ACTION).setPackage(SERVICE_PKG);
-
- private ServiceConnection mConn = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.d(LOG_TAG, "Service disconnected.");
- mAlarmService = null;
- errorFinish("service disconnected");
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(LOG_TAG, "Service connected.");
- mAlarmService = Alarm.Stub.asInterface(service);
- handleCommands();
- }
- };
-
-
- private void handleCommands() {
- if (CMD_PREPARE.equals(mCommand)) {
- callPrepare();
- } else if (CMD_SET.equals(mCommand)) {
- String paramString = mArgs.getString(PARAM);
- if (paramString == null) {
- errorFinish("argument expected for alarm time");
- }
- long timeout = -1;
- try {
- timeout = Long.parseLong(paramString);
- } catch (NumberFormatException nfe) {
- errorFinish("a number argument is expected");
- }
- callSetAndWait(timeout);
- } else if (CMD_DONE.equals(mCommand)) {
- callDone();
- } else {
- errorFinish("Unrecognized command: " + mCommand);
- }
- finish(Activity.RESULT_OK, new Bundle());
- }
-
- @Override
- public void onCreate(Bundle arguments) {
- super.onCreate(arguments);
- mCommand = arguments.getString(COMMAND);
- if ("true".equals(arguments.getString("debug"))) {
- Debug.waitForDebugger();
- }
- if (mCommand == null) {
- errorFinish("No command specified");
- }
- mArgs = arguments;
- connectToAlarmService();
- }
-
- private void errorFinish(String msg) {
- Bundle ret = new Bundle();
- ret.putString("error", msg);
- finish(Activity.RESULT_CANCELED, ret);
- }
-
- private void connectToAlarmService() {
- // start the service with an intent, this ensures the service keeps running after unbind
- ComponentName cn = getContext().startService(mServceIntent);
- if (cn == null) {
- errorFinish("failed to start service");
- }
- if (!getContext().bindService(mServceIntent, mConn, Context.BIND_AUTO_CREATE)) {
- errorFinish("failed to bind service");
- }
- }
-
- private void callPrepare() {
- try {
- mAlarmService.prepare();
- } catch (RemoteException e) {
- errorFinish("RemoteExeption in prepare()");
- } finally {
- getContext().unbindService(mConn);
- }
- }
-
- private void callDone() {
- try {
- mAlarmService.done();
- } catch (RemoteException e) {
- errorFinish("RemoteExeption in prepare()");
- } finally {
- getContext().unbindService(mConn);
- }
- // explicitly stop the service (started in prepare()) so that the service is now free
- // to be reclaimed
- getContext().stopService(mServceIntent);
- }
-
- private void callSetAndWait(long timeoutMills) {
- try {
- mAlarmService.setAlarmAndWait(timeoutMills);
- } catch (RemoteException e) {
- errorFinish("RemoteExeption in setAlarmAndWait()");
- } finally {
- getContext().unbindService(mConn);
- }
- }
-}
diff --git a/tests/utils/SleepUtils/WakeLoopService/Android.mk b/tests/utils/SleepUtils/WakeLoopService/Android.mk
deleted file mode 100644
index a8a944b215d2..000000000000
--- a/tests/utils/SleepUtils/WakeLoopService/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := WakeupLoopService
-LOCAL_SDK_VERSION := 7
-include $(BUILD_PACKAGE)
diff --git a/tests/utils/SleepUtils/WakeLoopService/AndroidManifest.xml b/tests/utils/SleepUtils/WakeLoopService/AndroidManifest.xml
deleted file mode 100644
index a7028c4f671e..000000000000
--- a/tests/utils/SleepUtils/WakeLoopService/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.test.wakeuploop" >
-
- <uses-sdk android:minSdkVersion="7" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
- <application android:label="Auto Wakeup Loop">
- <service android:name=".WakeLoopService"
- android:label="Wakup Loop Service"
- android:exported="true"
- android:enabled="true">
- <intent-filter>
- <action android:name="android.test.wakeuploop.WAKEUP_SERVICE" />
- </intent-filter>
- </service>
- <receiver android:name=".WakeUpCall">
- <intent-filter>
- <action android:name="android.test.wakeuploop.WAKEUP" />
- </intent-filter>
- </receiver>
- </application>
-</manifest>
diff --git a/tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/FileUtil.java b/tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/FileUtil.java
deleted file mode 100644
index c8b075b4a90f..000000000000
--- a/tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/FileUtil.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-
-package android.test.wakeuploop;
-
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-public class FileUtil {
-
- private static FileUtil sInst = null;
- private static DateFormat sDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
-
- private FileUtil() {};
-
- public static FileUtil get() {
- if (sInst == null) {
- sInst = new FileUtil();
- }
- return sInst;
- }
-
- public void writeDateToFile(File file) {
- try {
- FileOutputStream fos = new FileOutputStream(file);
- fos.write(sDateFormat.format(new Date()).getBytes());
- fos.write('\n');
- fos.flush();
- fos.close();
- } catch (IOException ioe) {
- Log.e("FileUtil", "exception writing date to file", ioe);
- }
- }
-}
diff --git a/tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/WakeLoopService.java b/tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/WakeLoopService.java
deleted file mode 100644
index 4f557b8786f4..000000000000
--- a/tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/WakeLoopService.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.
- */
-
-package android.test.wakeuploop;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.io.File;
-
-public class WakeLoopService extends Service {
-
- private static final String LOG_TAG = WakeLoopService.class.getSimpleName();
- static final String WAKEUP_INTERNAL = "WAKEUP_INTERVAL";
- static final String MAX_LOOP = "MAX_LOOP";
- static final String STOP_CALLBACK = "STOP_CALLBACK";
- static final String THIS_LOOP = "THIS_LOOP";
- static final int MSG_STOP_SERVICE = 0xd1ed1e;
-
- private final Handler mHandler = new Handler() {
- public void handleMessage(Message msg) {
- if (msg.what == MSG_STOP_SERVICE) {
- stopSelf();
- } else {
- super.handleMessage(msg);
- }
- };
- };
-
- @Override
- public IBinder onBind(Intent intent) {
- // no binding, just start via intent
- return null;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- // get wakeup interval from intent
- long wakeupInterval = intent.getLongExtra(WAKEUP_INTERNAL, 0);
- long maxLoop = intent.getLongExtra(MAX_LOOP, 0);
-
- if (wakeupInterval == 0) {
- // stop and error
- Log.e(LOG_TAG, "No wakeup interval specified, not starting the service");
- stopSelf();
- return START_NOT_STICKY;
- }
- FileUtil.get().writeDateToFile(new File(Environment.getExternalStorageDirectory(),
- "wakeup-loop-start.txt"));
- Log.d(LOG_TAG, String.format("WakeLoop: STARTED interval = %d, total loop = %d",
- wakeupInterval, maxLoop));
- // calculate when device should be waken up
- long atTime = SystemClock.elapsedRealtime() + wakeupInterval;
- AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
- Intent wakupIntent = new Intent(WakeUpCall.WAKEUP_CALL)
- .putExtra(WAKEUP_INTERNAL, wakeupInterval)
- .putExtra(MAX_LOOP, maxLoop)
- .putExtra(THIS_LOOP, 0L)
- .putExtra(STOP_CALLBACK, new Messenger(mHandler));
- PendingIntent pi = PendingIntent.getBroadcast(this, 0, wakupIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- // set alarm, which will be delivered in form of the wakeupIntent
- am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, atTime, pi);
- return START_NOT_STICKY;
- }
-
- @Override
- public void onDestroy() {
- Log.d(LOG_TAG, "WakeLoop: STOPPED");
- // cancel alarms first
- Intent intent = new Intent(WakeUpCall.WAKEUP_CALL)
- .putExtra(WakeUpCall.CANCEL, "true");
- sendBroadcast(intent);
- }
-}
diff --git a/tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/WakeUpCall.java b/tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/WakeUpCall.java
deleted file mode 100644
index 8347bbf0c1c7..000000000000
--- a/tests/utils/SleepUtils/WakeLoopService/src/android/test/wakeuploop/WakeUpCall.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.
- */
-
-package android.test.wakeuploop;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Environment;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.io.File;
-
-/**
- * The receiver for the alarm we set
- *
- */
-public class WakeUpCall extends BroadcastReceiver {
- private static final String LOG_TAG = WakeUpCall.class.getSimpleName();
- static final String WAKEUP_CALL = "android.test.wakeuploop.WAKEUP";
- static final String CANCEL = "CANCEL";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- boolean cancel = intent.hasExtra(CANCEL);
- if (!cancel) {
- long maxLoop = intent.getLongExtra(WakeLoopService.MAX_LOOP, 0);
- long wakeupInterval = intent.getLongExtra(WakeLoopService.WAKEUP_INTERNAL, 0);
- long thisLoop = intent.getLongExtra(WakeLoopService.THIS_LOOP, -1);
- Log.d(LOG_TAG, String.format("incoming: interval = %d, max loop = %d, this loop = %d",
- wakeupInterval, maxLoop, thisLoop));
- if (thisLoop == -1) {
- Log.e(LOG_TAG, "no valid loop count received, trying to stop service");
- stopService(intent);
- return;
- }
- if (wakeupInterval == 0) {
- Log.e(LOG_TAG, "no valid wakeup interval received, trying to stop service");
- stopService(intent);
- return;
- }
- thisLoop++;
- Log.d(LOG_TAG, String.format("WakeLoop - iteration %d of %d", thisLoop, maxLoop));
- if (thisLoop == maxLoop) {
- // when maxLoop is 0, we loop forever, so not checking that case
- // here
- Log.d(LOG_TAG, "reached max loop count, stopping service");
- stopService(intent);
- return;
- }
- screenOn(context);
- FileUtil.get().writeDateToFile(
- new File(Environment.getExternalStorageDirectory(), "wakeup-loop.txt"));
- // calculate when device should be waken up
- long atTime = SystemClock.elapsedRealtime() + wakeupInterval;
- intent.putExtra(WakeLoopService.THIS_LOOP, thisLoop);
- PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- // set alarm, which will be delivered in form of the wakeupIntent
- am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, atTime, pi);
- } else {
- // cancel alarms
- Log.d(LOG_TAG, "cancelling future alarms on request");
- am.cancel(PendingIntent.getBroadcast(context, 0, intent, 0));
- }
- }
-
- private void stopService(Intent i) {
- Messenger msgr = i.getParcelableExtra(WakeLoopService.STOP_CALLBACK);
- if (msgr == null) {
- Log.e(LOG_TAG, "no stop service callback found, cannot stop");
- } else {
- Message msg = new Message();
- msg.what = WakeLoopService.MSG_STOP_SERVICE;
- try {
- msgr.send(msg);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "ignored remoted exception while attempting to stop service", e);
- }
- }
- }
-
- private void screenOn(Context context) {
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- @SuppressWarnings("deprecation")
- WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK |
- PowerManager.ACQUIRE_CAUSES_WAKEUP, LOG_TAG);
- wl.acquire(500);
- }
-}
diff --git a/tests/utils/testutils/java/com/android/test/filters/SelectTest.java b/tests/utils/testutils/java/com/android/test/filters/SelectTest.java
new file mode 100644
index 000000000000..d0350aff5ef5
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/test/filters/SelectTest.java
@@ -0,0 +1,338 @@
+/*
+ * 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.test.filters;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringJoiner;
+
+/**
+ * JUnit filter to select tests.
+ *
+ * <p>This filter selects tests specified by package name, class name, and method name. With this
+ * filter, the package and the class options of AndroidJUnitRunner can be superseded. Also the
+ * restriction that prevents using the package and the class options can be mitigated.
+ *
+ * <p><b>Select out tests from Java packages:</b> this option supersedes {@code -e package} option.
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.test.filters.SelectTest \
+ * -e selectTest package1.,package2. \
+ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ * Note that the ending {@code .} in package name is mandatory.
+ *
+ * <p><b>Select out test classes:</b> this option supersedes {@code -e class} option.
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.test.filters.SelectTest \
+ * -e selectTest package1.ClassA,package2.ClassB \
+ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ *
+ * <p><b>Select out test methods from Java classes:</b>
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.test.filters.SelectTest \
+ * -e selectTest package1.ClassA#methodX,package2.ClassB#methodY \
+ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ *
+ * Those options can be used simultaneously. For example
+ * <pre>
+ * adb shell am instrument -w \
+ * -e filter com.android.test.filters.SelectTest \
+ * -e selectTest package1.,package2.classA,package3.ClassB#methodZ \
+ * com.tests.pkg/androidx.test.runner.AndroidJUnitRunner
+ * </pre>
+ * will select out all tests in package1, all tests in classA, and ClassB#methodZ test.
+ *
+ * <p>Note that when this option is specified with either {@code -e package} or {@code -e class}
+ * option, filtering behaves as logically conjunction. Other options, such as {@code -e notPackage},
+ * {@code -e notClass}, {@code -e annotation}, and {@code -e notAnnotation}, should work as expected
+ * with this SelectTest option.
+ *
+ * <p>When specified with {@code -e selectTest_verbose true} option, {@link SelectTest} verbosely
+ * logs to logcat while parsing {@code -e selectTest} option.
+ */
+public class SelectTest extends Filter {
+
+ private static final String TAG = SelectTest.class.getSimpleName();
+
+ @VisibleForTesting
+ static final String OPTION_SELECT_TEST = "selectTest";
+ @VisibleForTesting
+ static final String OPTION_SELECT_TEST_VERBOSE = OPTION_SELECT_TEST + "_verbose";
+
+ private static final String ARGUMENT_ITEM_SEPARATOR = ",";
+ private static final String PACKAGE_NAME_SEPARATOR = ".";
+ private static final String METHOD_SEPARATOR = "#";
+
+ @Nullable
+ private final PackageSet mPackageSet;
+
+ /**
+ * Construct {@link SelectTest} filter from instrumentation arguments in {@link Bundle}.
+ *
+ * @param testArgs instrumentation test arguments.
+ */
+ public SelectTest(@NonNull Bundle testArgs) {
+ mPackageSet = parseSelectTest(testArgs);
+ }
+
+ @Override
+ public boolean shouldRun(Description description) {
+ if (mPackageSet == null) {
+ // Accept all tests because this filter is disabled.
+ return true;
+ }
+ String testClassName = description.getClassName();
+ String testMethodName = description.getMethodName();
+ return mPackageSet.accept(testClassName, testMethodName);
+ }
+
+ @Override
+ public String describe() {
+ return OPTION_SELECT_TEST + "=" + mPackageSet;
+ }
+
+ /**
+ * Create {@link #OPTION_SELECT_TEST} argument and add it to {@code testArgs}.
+ *
+ * <p>This method is intended to be used at constructor of extended {@link Filter} class.
+ *
+ * @param testArgs instrumentation test arguments.
+ * @param selectTests array of class name to be selected to run.
+ * @return modified instrumentation test arguments.
+ */
+ @NonNull
+ protected static Bundle addSelectTest(
+ @NonNull Bundle testArgs, @NonNull String... selectTests) {
+ if (selectTests.length == 0) {
+ return testArgs;
+ }
+ testArgs.putString(OPTION_SELECT_TEST, join(Arrays.asList(selectTests)));
+ return testArgs;
+ }
+
+ /**
+ * Parse {@code -e selectTest} argument.
+ * @param testArgs instrumentation test arguments.
+ * @return {@link PackageSet} that will filter tests. Returns {@code null} when no
+ * {@code -e selectTest} option is specified, thus this filter gets disabled.
+ */
+ @Nullable
+ private static PackageSet parseSelectTest(Bundle testArgs) {
+ final String selectTestArgs = testArgs.getString(OPTION_SELECT_TEST);
+ if (selectTestArgs == null) {
+ Log.w(TAG, "Disabled because no " + OPTION_SELECT_TEST + " option specified");
+ return null;
+ }
+
+ final boolean verbose = new Boolean(testArgs.getString(OPTION_SELECT_TEST_VERBOSE));
+ final PackageSet packageSet = new PackageSet(verbose);
+ for (String selectTestArg : selectTestArgs.split(ARGUMENT_ITEM_SEPARATOR)) {
+ packageSet.add(selectTestArg);
+ }
+ return packageSet;
+ }
+
+ private static String getPackageName(String selectTestArg) {
+ int endPackagePos = selectTestArg.lastIndexOf(PACKAGE_NAME_SEPARATOR);
+ return (endPackagePos < 0) ? "" : selectTestArg.substring(0, endPackagePos);
+ }
+
+ @Nullable
+ private static String getClassName(String selectTestArg) {
+ if (selectTestArg.endsWith(PACKAGE_NAME_SEPARATOR)) {
+ return null;
+ }
+ int methodSepPos = selectTestArg.indexOf(METHOD_SEPARATOR);
+ return (methodSepPos < 0) ? selectTestArg : selectTestArg.substring(0, methodSepPos);
+ }
+
+ @Nullable
+ private static String getMethodName(String selectTestArg) {
+ int methodSepPos = selectTestArg.indexOf(METHOD_SEPARATOR);
+ return (methodSepPos < 0) ? null : selectTestArg.substring(methodSepPos + 1);
+ }
+
+ /** Package level filter */
+ private static class PackageSet {
+ private final boolean mVerbose;
+ /**
+ * Java package name to {@link ClassSet} map. To represent package filtering, a map value
+ * can be {@code null}.
+ */
+ private final Map<String, ClassSet> mClassSetMap = new LinkedHashMap<>();
+
+ PackageSet(boolean verbose) {
+ mVerbose = verbose;
+ }
+
+ void add(final String selectTestArg) {
+ final String packageName = getPackageName(selectTestArg);
+ final String className = getClassName(selectTestArg);
+
+ if (className == null) {
+ ClassSet classSet = mClassSetMap.put(packageName, null); // package filtering.
+ if (mVerbose) {
+ logging("Select package " + selectTestArg, classSet != null,
+ "; supersede " + classSet);
+ }
+ return;
+ }
+
+ ClassSet classSet = mClassSetMap.get(packageName);
+ if (classSet == null) {
+ if (mClassSetMap.containsKey(packageName)) {
+ if (mVerbose) {
+ logging("Select package " + packageName + PACKAGE_NAME_SEPARATOR, true,
+ " ignore " + selectTestArg);
+ }
+ return;
+ }
+ classSet = new ClassSet(mVerbose);
+ mClassSetMap.put(packageName, classSet);
+ }
+ classSet.add(selectTestArg);
+ }
+
+ boolean accept(String className, @Nullable String methodName) {
+ String packageName = getPackageName(className);
+ if (!mClassSetMap.containsKey(packageName)) {
+ return false;
+ }
+ ClassSet classSet = mClassSetMap.get(packageName);
+ return classSet == null || classSet.accept(className, methodName);
+ }
+
+ @Override
+ public String toString() {
+ StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+ for (String packageName : mClassSetMap.keySet()) {
+ ClassSet classSet = mClassSetMap.get(packageName);
+ joiner.add(classSet == null
+ ? packageName + PACKAGE_NAME_SEPARATOR : classSet.toString());
+ }
+ return joiner.toString();
+ }
+ }
+
+ /** Class level filter */
+ private static class ClassSet {
+ private final boolean mVerbose;
+ /**
+ * Java class name to set of method names map. To represent class filtering, a map value
+ * can be {@code null}.
+ */
+ private final Map<String, Set<String>> mMethodSetMap = new LinkedHashMap<>();
+
+ ClassSet(boolean verbose) {
+ mVerbose = verbose;
+ }
+
+ void add(String selectTestArg) {
+ final String className = getClassName(selectTestArg);
+ final String methodName = getMethodName(selectTestArg);
+
+ if (methodName == null) {
+ Set<String> methodSet = mMethodSetMap.put(className, null); // class filtering.
+ if (mVerbose) {
+ logging("Select class " + selectTestArg, methodSet != null,
+ "; supersede " + toString(className, methodSet));
+ }
+ return;
+ }
+
+ Set<String> methodSet = mMethodSetMap.get(className);
+ if (methodSet == null) {
+ if (mMethodSetMap.containsKey(className)) {
+ if (mVerbose) {
+ logging("Select class " + className, true, "; ignore " + selectTestArg);
+ }
+ return;
+ }
+ methodSet = new LinkedHashSet<>();
+ mMethodSetMap.put(className, methodSet);
+ }
+
+ methodSet.add(methodName);
+ if (mVerbose) {
+ logging("Select method " + selectTestArg, false, null);
+ }
+ }
+
+ boolean accept(String className, @Nullable String methodName) {
+ if (!mMethodSetMap.containsKey(className)) {
+ return false;
+ }
+ Set<String> methodSet = mMethodSetMap.get(className);
+ return methodName == null || methodSet == null || methodSet.contains(methodName);
+ }
+
+ @Override
+ public String toString() {
+ StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+ for (String className : mMethodSetMap.keySet()) {
+ joiner.add(toString(className, mMethodSetMap.get(className)));
+ }
+ return joiner.toString();
+ }
+
+ private static String toString(String className, @Nullable Set<String> methodSet) {
+ if (methodSet == null) {
+ return className;
+ }
+ StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+ for (String methodName : methodSet) {
+ joiner.add(className + METHOD_SEPARATOR + methodName);
+ }
+ return joiner.toString();
+ }
+ }
+
+ private static void logging(String infoLog, boolean isWarning, String warningLog) {
+ if (isWarning) {
+ Log.w(TAG, infoLog + warningLog);
+ } else {
+ Log.i(TAG, infoLog);
+ }
+ }
+
+ private static String join(Collection<String> list) {
+ StringJoiner joiner = new StringJoiner(ARGUMENT_ITEM_SEPARATOR);
+ for (String text : list) {
+ joiner.add(text);
+ }
+ return joiner.toString();
+ }
+}
diff --git a/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java b/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java
new file mode 100644
index 000000000000..a6b0102a511f
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java
@@ -0,0 +1,219 @@
+/*
+ * 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.test.filters;
+
+import static com.android.test.filters.SelectTest.OPTION_SELECT_TEST;
+import static com.android.test.filters.SelectTest.OPTION_SELECT_TEST_VERBOSE;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Bundle;
+import android.util.ArraySet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.manipulation.Filter;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+public class SelectTestTests {
+
+ private static final String PACKAGE_A = "packageA.";
+ private static final String PACKAGE_B = "packageB.";
+ private static final String PACKAGE_C = "packageC.";
+ private static final String CLASS_A1 = PACKAGE_A + "Class1";
+ private static final String CLASS_A2 = PACKAGE_A + "Class2";
+ private static final String CLASS_B3 = PACKAGE_B + "Class3";
+ private static final String CLASS_B4 = PACKAGE_B + "Class4";
+ private static final String CLASS_C5 = PACKAGE_C + "Class5";
+ private static final String CLASS_C6 = PACKAGE_C + "Class6";
+ private static final String METHOD_A1K = CLASS_A1 + "#methodK";
+ private static final String METHOD_A1L = CLASS_A1 + "#methodL";
+ private static final String METHOD_A2M = CLASS_A2 + "#methodM";
+ private static final String METHOD_A2N = CLASS_A2 + "#methodN";
+ private static final String METHOD_B3P = CLASS_B3 + "#methodP";
+ private static final String METHOD_B3Q = CLASS_B3 + "#methodQ";
+ private static final String METHOD_B4R = CLASS_B4 + "#methodR";
+ private static final String METHOD_B4S = CLASS_B4 + "#methodS";
+ private static final String METHOD_C5W = CLASS_C5 + "#methodW";
+ private static final String METHOD_C5X = CLASS_C5 + "#methodX";
+ private static final String METHOD_C6Y = CLASS_C6 + "#methodY";
+ private static final String METHOD_C6Z = CLASS_C6 + "#methodZ";
+
+ private static final Set<Description> TEST_METHOD_A1K = methodTest(METHOD_A1K);
+ private static final Set<Description> TEST_METHOD_A1L = methodTest(METHOD_A1L);
+ private static final Set<Description> TEST_METHOD_A2M = methodTest(METHOD_A2M);
+ private static final Set<Description> TEST_METHOD_A2N = methodTest(METHOD_A2N);
+ private static final Set<Description> TEST_METHOD_B3P = methodTest(METHOD_B3P);
+ private static final Set<Description> TEST_METHOD_B3Q = methodTest(METHOD_B3Q);
+ private static final Set<Description> TEST_METHOD_B4R = methodTest(METHOD_B4R);
+ private static final Set<Description> TEST_METHOD_B4S = methodTest(METHOD_B4S);
+ private static final Set<Description> TEST_METHOD_C5W = methodTest(METHOD_C5W);
+ private static final Set<Description> TEST_METHOD_C5X = methodTest(METHOD_C5X);
+ private static final Set<Description> TEST_METHOD_C6Y = methodTest(METHOD_C6Y);
+ private static final Set<Description> TEST_METHOD_C6Z = methodTest(METHOD_C6Z);
+ private static final Set<Description> TEST_CLASS_A1 = merge(TEST_METHOD_A1K, TEST_METHOD_A1L);
+ private static final Set<Description> TEST_CLASS_A2 = merge(TEST_METHOD_A2M, TEST_METHOD_A2N);
+ private static final Set<Description> TEST_CLASS_B3 = merge(TEST_METHOD_B3P, TEST_METHOD_B3Q);
+ private static final Set<Description> TEST_CLASS_B4 = merge(TEST_METHOD_B4R, TEST_METHOD_B4S);
+ private static final Set<Description> TEST_CLASS_C5 = merge(TEST_METHOD_C5W, TEST_METHOD_C5X);
+ private static final Set<Description> TEST_CLASS_C6 = merge(TEST_METHOD_C6Y, TEST_METHOD_C6Z);
+ private static final Set<Description> TEST_PACKAGE_A = merge(TEST_CLASS_A1, TEST_CLASS_A2);
+ private static final Set<Description> TEST_PACKAGE_B = merge(TEST_CLASS_B3, TEST_CLASS_B4);
+ private static final Set<Description> TEST_PACKAGE_C = merge(TEST_CLASS_C5, TEST_CLASS_C6);
+ private static final Set<Description> TEST_ALL =
+ merge(TEST_PACKAGE_A, TEST_PACKAGE_B, TEST_PACKAGE_C);
+
+ private SelectTestBuilder mBuilder;
+
+ @Before
+ public void setUp() {
+ mBuilder = new SelectTestBuilder();
+ }
+
+ private static class SelectTestBuilder {
+ private final Bundle mTestArgs = new Bundle();
+
+ Filter build() {
+ mTestArgs.putString(OPTION_SELECT_TEST_VERBOSE, Boolean.TRUE.toString());
+ return new SelectTest(mTestArgs);
+ }
+
+ SelectTestBuilder withSelectTest(String... selectTestArgs) {
+ putTestOption(OPTION_SELECT_TEST, selectTestArgs);
+ return this;
+ }
+
+ private void putTestOption(String option, String... args) {
+ if (args.length > 0) {
+ StringJoiner joiner = new StringJoiner(",");
+ for (String arg : args) {
+ joiner.add(arg);
+ }
+ mTestArgs.putString(option, joiner.toString());
+ }
+ }
+ }
+
+ private static Set<Description> methodTest(String testName) {
+ int methodSep = testName.indexOf("#");
+ String className = testName.substring(0, methodSep);
+ String methodName = testName.substring(methodSep + 1);
+ final Set<Description> tests = new ArraySet<>();
+ tests.add(Description.createSuiteDescription(className));
+ tests.add(Description.createTestDescription(className, methodName));
+ return Collections.unmodifiableSet(tests);
+ }
+
+ @SafeVarargs
+ private static Set<Description> merge(Set<Description>... testSpecs) {
+ final Set<Description> merged = new LinkedHashSet<>();
+ for (Set<Description> testSet : testSpecs) {
+ merged.addAll(testSet);
+ }
+ return Collections.unmodifiableSet(merged);
+ }
+
+ @SafeVarargs
+ private static void acceptTests(Filter filter, Set<Description>... testSpecs) {
+ final Set<Description> accepts = merge(testSpecs);
+ for (Description test : TEST_ALL) {
+ if (accepts.contains(test)) {
+ assertTrue("accept " + test, filter.shouldRun(test));
+ } else {
+ assertFalse("reject " + test, filter.shouldRun(test));
+ }
+ }
+ }
+
+ @Test
+ public void testFilterDisabled() {
+ final Filter filter = mBuilder.build();
+ acceptTests(filter, TEST_ALL);
+ }
+
+ @Test
+ public void testSelectPackage() {
+ final Filter filter = mBuilder.withSelectTest(PACKAGE_A, PACKAGE_B).build();
+ acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_B);
+ }
+
+ @Test
+ public void testSelectClass() {
+ final Filter filter = mBuilder.withSelectTest(CLASS_A1, CLASS_A2, CLASS_B3).build();
+ acceptTests(filter, TEST_CLASS_A1, TEST_CLASS_A2, TEST_CLASS_B3);
+ }
+
+ @Test
+ public void testSelectMethod() {
+ final Filter filter = mBuilder
+ .withSelectTest(METHOD_A1K, METHOD_A2M, METHOD_A2N, METHOD_B3P).build();
+ acceptTests(filter, TEST_METHOD_A1K, TEST_METHOD_A2M, TEST_METHOD_A2N, TEST_METHOD_B3P);
+ }
+
+ @Test
+ public void testSelectClassAndPackage() {
+ final Filter filter = mBuilder.withSelectTest(CLASS_A1, PACKAGE_B, CLASS_C5).build();
+ acceptTests(filter, TEST_CLASS_A1, TEST_PACKAGE_B, TEST_CLASS_C5);
+ }
+
+ @Test
+ public void testSelectMethodAndPackage() {
+ final Filter filter = mBuilder.withSelectTest(METHOD_A1K, PACKAGE_B, METHOD_C5W).build();
+ acceptTests(filter, TEST_METHOD_A1K, TEST_PACKAGE_B, TEST_METHOD_C5W);
+ }
+
+ @Test
+ public void testSelectMethodAndClass() {
+ final Filter filter = mBuilder.withSelectTest(METHOD_A1K, CLASS_C5, METHOD_B3P).build();
+ acceptTests(filter, TEST_METHOD_A1K, TEST_CLASS_C5, TEST_METHOD_B3P);
+ }
+
+ @Test
+ public void testSelectClassAndSamePackage() {
+ final Filter filter = mBuilder.withSelectTest(CLASS_A1, CLASS_A2, PACKAGE_A,
+ CLASS_C5, CLASS_C6, PACKAGE_C).build();
+ acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_C);
+ }
+
+ @Test
+ public void testSelectMethodAndSameClass() {
+ final Filter filter = mBuilder.withSelectTest(METHOD_A1K, METHOD_A1L, METHOD_A2M, CLASS_A1,
+ CLASS_B3, METHOD_B3P, METHOD_B3Q, METHOD_B4R).build();
+ acceptTests(filter, TEST_CLASS_A1, TEST_METHOD_A2M, TEST_CLASS_B3, TEST_METHOD_B4R);
+ }
+
+ @Test
+ public void testSelectMethodAndSamePackage() {
+ final Filter filter = mBuilder.withSelectTest(METHOD_A1K, METHOD_A1L, METHOD_A2M, PACKAGE_A,
+ PACKAGE_C, METHOD_C5W, METHOD_C5X, METHOD_C6Y).build();
+ acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_C);
+ }
+
+ @Test
+ public void testSelectMethodAndClassAndPackage() {
+ final Filter filter = mBuilder.withSelectTest(
+ METHOD_A1K, CLASS_A1, METHOD_A1L, METHOD_A2M, PACKAGE_A,
+ PACKAGE_B, METHOD_B3Q, CLASS_B3, METHOD_B4R, METHOD_B3P).build();
+ acceptTests(filter, TEST_PACKAGE_A, TEST_PACKAGE_B);
+ }
+}
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 92beb4eb7ce4..0512bdc5bf72 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -469,16 +469,12 @@ static bool CompilePng(IAaptContext* context, const CompileOptions& options,
return false;
}
- // Read the file as a string
- char buffer_2[data->size()];
- memcpy(&buffer_2, data->data(), data->size());
- StringPiece content(buffer_2, data->size());
-
BigBuffer crunched_png_buffer(4096);
io::BigBufferOutputStream crunched_png_buffer_out(&crunched_png_buffer);
// Ensure that we only keep the chunks we care about if we end up
// using the original PNG instead of the crunched one.
+ const StringPiece content(reinterpret_cast<const char*>(data->data()), data->size());
PngChunkFilter png_chunk_filter(content);
std::unique_ptr<Image> image = ReadPng(context, path_data.source, &png_chunk_filter);
if (!image) {
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8c00cebf6c59..1fd45e72f1e8 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2084,6 +2084,8 @@ public class WifiManager {
public static final int WIFI_FEATURE_LOW_LATENCY = 0x40000000; // Low Latency modes
/** @hide */
public static final int WIFI_FEATURE_DPP = 0x80000000; // DPP (Easy-Connect)
+ /** @hide */
+ public static final long WIFI_FEATURE_P2P_RAND_MAC = 0x100000000L; // Random P2P MAC
private long getSupportedFeatures() {
try {
@@ -3717,10 +3719,8 @@ public class WifiManager {
* @param SSID, in the format of WifiConfiguration's SSID.
* @hide
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_SETTINGS,
- android.Manifest.permission.NETWORK_SETUP_WIZARD,
android.Manifest.permission.NETWORK_STACK
})
public void disableEphemeralNetwork(String SSID) {
diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
index aa1669ee6d94..52ee7423c8fb 100644
--- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
@@ -28,6 +28,7 @@ import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.util.Objects;
@@ -50,12 +51,24 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements
*/
private final int mOriginalRequestorUid;
+ /**
+ * The package name of the app that requested a specific wifi network using
+ * {@link WifiNetworkSpecifier}.
+ *
+ * Will only be filled when the device connects to a wifi network as a result of a
+ * {@link NetworkRequest} with {@link WifiNetworkSpecifier}. Will be set to null if the device
+ * auto-connected to a wifi network.
+ */
+ private final String mOriginalRequestorPackageName;
+
public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration,
- int originalRequestorUid) {
+ int originalRequestorUid,
+ @Nullable String originalRequestorPackageName) {
checkNotNull(wifiConfiguration);
mWifiConfiguration = wifiConfiguration;
mOriginalRequestorUid = originalRequestorUid;
+ mOriginalRequestorPackageName = originalRequestorPackageName;
}
/**
@@ -67,7 +80,9 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements
public WifiNetworkAgentSpecifier createFromParcel(@NonNull Parcel in) {
WifiConfiguration wifiConfiguration = in.readParcelable(null);
int originalRequestorUid = in.readInt();
- return new WifiNetworkAgentSpecifier(wifiConfiguration, originalRequestorUid);
+ String originalRequestorPackageName = in.readString();
+ return new WifiNetworkAgentSpecifier(
+ wifiConfiguration, originalRequestorUid, originalRequestorPackageName);
}
@Override
@@ -85,6 +100,7 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelable(mWifiConfiguration, flags);
dest.writeInt(mOriginalRequestorUid);
+ dest.writeString(mOriginalRequestorPackageName);
}
@Override
@@ -137,6 +153,9 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements
if (ns.requestorUid != this.mOriginalRequestorUid) {
return false;
}
+ if (!TextUtils.equals(ns.requestorPackageName, this.mOriginalRequestorPackageName)) {
+ return false;
+ }
return true;
}
@@ -146,7 +165,8 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements
mWifiConfiguration.SSID,
mWifiConfiguration.BSSID,
mWifiConfiguration.allowedKeyManagement,
- mOriginalRequestorUid);
+ mOriginalRequestorUid,
+ mOriginalRequestorPackageName);
}
@Override
@@ -162,7 +182,9 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements
&& Objects.equals(this.mWifiConfiguration.BSSID, lhs.mWifiConfiguration.BSSID)
&& Objects.equals(this.mWifiConfiguration.allowedKeyManagement,
lhs.mWifiConfiguration.allowedKeyManagement)
- && mOriginalRequestorUid == lhs.mOriginalRequestorUid;
+ && mOriginalRequestorUid == lhs.mOriginalRequestorUid
+ && TextUtils.equals(mOriginalRequestorPackageName,
+ lhs.mOriginalRequestorPackageName);
}
@Override
@@ -172,6 +194,7 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements
.append(", SSID=").append(mWifiConfiguration.SSID)
.append(", BSSID=").append(mWifiConfiguration.BSSID)
.append(", mOriginalRequestorUid=").append(mOriginalRequestorUid)
+ .append(", mOriginalRequestorPackageName=").append(mOriginalRequestorPackageName)
.append("]");
return sb.toString();
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
index ecee5ff0161d..42d43934e229 100644
--- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.net.MacAddress;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
@@ -586,7 +587,8 @@ public class WifiNetworkConfigBuilder {
mSsidPatternMatcher,
mBssidPatternMatcher,
buildWifiConfiguration(),
- Process.myUid());
+ Process.myUid(),
+ ActivityThread.currentApplication().getApplicationContext().getOpPackageName());
}
/**
@@ -648,7 +650,8 @@ public class WifiNetworkConfigBuilder {
buildWifiConfiguration(),
mIsAppInteractionRequired,
mIsUserInteractionRequired,
- Process.myUid());
+ Process.myUid(),
+ ActivityThread.currentApplication().getApplicationContext().getOpPackageName());
}
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index 6e4eeef4dd55..a5f4675f3956 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -25,6 +25,7 @@ import android.net.NetworkSpecifier;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
+import android.text.TextUtils;
import android.util.Pair;
import java.util.Objects;
@@ -63,18 +64,25 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc
*/
public final int requestorUid;
+ /**
+ * The package name of the app initializing this network specifier.
+ */
+ public final String requestorPackageName;
+
public WifiNetworkSpecifier(@NonNull PatternMatcher ssidPatternMatcher,
@NonNull Pair<MacAddress, MacAddress> bssidPatternMatcher,
@NonNull WifiConfiguration wifiConfiguration,
- int requestorUid) {
+ int requestorUid, @NonNull String requestorPackageName) {
checkNotNull(ssidPatternMatcher);
checkNotNull(bssidPatternMatcher);
checkNotNull(wifiConfiguration);
+ checkNotNull(requestorPackageName);
this.ssidPatternMatcher = ssidPatternMatcher;
this.bssidPatternMatcher = bssidPatternMatcher;
this.wifiConfiguration = wifiConfiguration;
this.requestorUid = requestorUid;
+ this.requestorPackageName = requestorPackageName;
}
public static final Creator<WifiNetworkSpecifier> CREATOR =
@@ -88,8 +96,9 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc
Pair.create(baseAddress, mask);
WifiConfiguration wifiConfiguration = in.readParcelable(null);
int requestorUid = in.readInt();
+ String requestorPackageName = in.readString();
return new WifiNetworkSpecifier(ssidPatternMatcher, bssidPatternMatcher,
- wifiConfiguration, requestorUid);
+ wifiConfiguration, requestorUid, requestorPackageName);
}
@Override
@@ -110,6 +119,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc
dest.writeParcelable(bssidPatternMatcher.second, flags);
dest.writeParcelable(wifiConfiguration, flags);
dest.writeInt(requestorUid);
+ dest.writeString(requestorPackageName);
}
@Override
@@ -136,7 +146,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc
ssidPatternMatcher.getType(),
bssidPatternMatcher,
wifiConfiguration.allowedKeyManagement,
- requestorUid);
+ requestorUid, requestorPackageName);
}
@Override
@@ -156,7 +166,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc
lhs.bssidPatternMatcher)
&& Objects.equals(this.wifiConfiguration.allowedKeyManagement,
lhs.wifiConfiguration.allowedKeyManagement)
- && requestorUid == lhs.requestorUid;
+ && requestorUid == lhs.requestorUid
+ && TextUtils.equals(requestorPackageName, lhs.requestorPackageName);
}
@Override
@@ -168,6 +179,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc
.append(", SSID=").append(wifiConfiguration.SSID)
.append(", BSSID=").append(wifiConfiguration.BSSID)
.append(", requestorUid=").append(requestorUid)
+ .append(", requestorPackageName=").append(requestorPackageName)
.append("]")
.toString();
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 3c90eb763e81..6b05dfc1f774 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -18,8 +18,10 @@ package android.net.wifi;
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.util.List;
import java.util.Objects;
@@ -58,17 +60,25 @@ public final class WifiNetworkSuggestion implements Parcelable {
*/
public final int suggestorUid;
+ /**
+ * The package name of the process initializing this network suggestion.
+ * @hide
+ */
+ public final String suggestorPackageName;
+
/** @hide */
- public WifiNetworkSuggestion(WifiConfiguration wifiConfiguration,
+ public WifiNetworkSuggestion(@NonNull WifiConfiguration wifiConfiguration,
boolean isAppInteractionRequired,
boolean isUserInteractionRequired,
- int suggestorUid) {
+ int suggestorUid, @NonNull String suggestorPackageName) {
checkNotNull(wifiConfiguration);
+ checkNotNull(suggestorPackageName);
this.wifiConfiguration = wifiConfiguration;
this.isAppInteractionRequired = isAppInteractionRequired;
this.isUserInteractionRequired = isUserInteractionRequired;
this.suggestorUid = suggestorUid;
+ this.suggestorPackageName = suggestorPackageName;
}
public static final Creator<WifiNetworkSuggestion> CREATOR =
@@ -79,7 +89,8 @@ public final class WifiNetworkSuggestion implements Parcelable {
in.readParcelable(null), // wifiConfiguration
in.readBoolean(), // isAppInteractionRequired
in.readBoolean(), // isUserInteractionRequired
- in.readInt() // suggestorUid
+ in.readInt(), // suggestorUid
+ in.readString() // suggestorPackageName
);
}
@@ -100,12 +111,13 @@ public final class WifiNetworkSuggestion implements Parcelable {
dest.writeBoolean(isAppInteractionRequired);
dest.writeBoolean(isUserInteractionRequired);
dest.writeInt(suggestorUid);
+ dest.writeString(suggestorPackageName);
}
@Override
public int hashCode() {
return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID,
- wifiConfiguration.allowedKeyManagement, suggestorUid);
+ wifiConfiguration.allowedKeyManagement, suggestorUid, suggestorPackageName);
}
/**
@@ -124,7 +136,8 @@ public final class WifiNetworkSuggestion implements Parcelable {
&& Objects.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID)
&& Objects.equals(this.wifiConfiguration.allowedKeyManagement,
lhs.wifiConfiguration.allowedKeyManagement)
- && suggestorUid == lhs.suggestorUid;
+ && suggestorUid == lhs.suggestorUid
+ && TextUtils.equals(suggestorPackageName, lhs.suggestorPackageName);
}
@Override
@@ -135,6 +148,7 @@ public final class WifiNetworkSuggestion implements Parcelable {
.append(", isAppInteractionRequired=").append(isAppInteractionRequired)
.append(", isUserInteractionRequired=").append(isUserInteractionRequired)
.append(", suggestorUid=").append(suggestorUid)
+ .append(", suggestorPackageName=").append(suggestorPackageName)
.append("]");
return sb.toString();
}
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index 1ee874a9698b..1d499b6654d0 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -65,9 +65,9 @@ public abstract class ProvisioningCallback {
public static final int OSU_FAILURE_PROVISIONING_NOT_AVAILABLE = 7;
/**
- * The reason code for provisioning failure due to invalid server url.
+ * The reason code for provisioning failure due to invalid web url format for an OSU web page.
*/
- public static final int OSU_FAILURE_INVALID_SERVER_URL = 8;
+ public static final int OSU_FAILURE_INVALID_URL_FORMAT_FOR_OSU = 8;
/**
* The reason code for provisioning failure when a command received is not the expected command
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index 2258e4dd79e5..e6eece85cb19 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -38,6 +38,8 @@ import org.junit.Test;
public class WifiNetworkAgentSpecifierTest {
private static final int TEST_UID = 5;
private static final int TEST_UID_1 = 8;
+ private static final String TEST_PACKAGE = "com.test";
+ private static final String TEST_PACKAGE_1 = "com.test.1";
private static final String TEST_SSID = "Test123";
private static final String TEST_SSID_PATTERN = "Test";
private static final String TEST_SSID_1 = "456test";
@@ -104,14 +106,14 @@ public class WifiNetworkAgentSpecifierTest {
WifiNetworkAgentSpecifier specifier1 =
new WifiNetworkAgentSpecifier(
wifiConfiguration1,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkAgentSpecifier specifier2 =
new WifiNetworkAgentSpecifier(
wifiConfiguration2,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
assertFalse(specifier2.equals(specifier1));
}
@@ -128,14 +130,14 @@ public class WifiNetworkAgentSpecifierTest {
WifiNetworkAgentSpecifier specifier1 =
new WifiNetworkAgentSpecifier(
wifiConfiguration1,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
wifiConfiguration2.SSID = TEST_SSID_1;
WifiNetworkAgentSpecifier specifier2 =
new WifiNetworkAgentSpecifier(
wifiConfiguration2,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
assertFalse(specifier2.equals(specifier1));
}
@@ -152,14 +154,14 @@ public class WifiNetworkAgentSpecifierTest {
WifiNetworkAgentSpecifier specifier1 =
new WifiNetworkAgentSpecifier(
wifiConfiguration1,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
wifiConfiguration2.BSSID = TEST_BSSID_1;
WifiNetworkAgentSpecifier specifier2 =
new WifiNetworkAgentSpecifier(
wifiConfiguration2,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
assertFalse(specifier2.equals(specifier1));
}
@@ -214,7 +216,7 @@ public class WifiNetworkAgentSpecifierTest {
ssidPattern,
bssidPattern,
wificonfigurationNetworkSpecifier,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -243,7 +245,7 @@ public class WifiNetworkAgentSpecifierTest {
ssidPattern,
bssidPattern,
wificonfigurationNetworkSpecifier,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -272,7 +274,7 @@ public class WifiNetworkAgentSpecifierTest {
ssidPattern,
bssidPattern,
wificonfigurationNetworkSpecifier,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -292,7 +294,7 @@ public class WifiNetworkAgentSpecifierTest {
WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
new WifiNetworkAgentSpecifier(
wifiConfigurationNetworkAgent,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
PatternMatcher ssidPattern =
new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
@@ -305,7 +307,7 @@ public class WifiNetworkAgentSpecifierTest {
ssidPattern,
bssidPattern,
wificonfigurationNetworkSpecifier,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -325,7 +327,7 @@ public class WifiNetworkAgentSpecifierTest {
WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
new WifiNetworkAgentSpecifier(
wifiConfigurationNetworkAgent,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
PatternMatcher ssidPattern =
new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB);
@@ -339,7 +341,7 @@ public class WifiNetworkAgentSpecifierTest {
ssidPattern,
bssidPattern,
wificonfigurationNetworkSpecifier,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -359,7 +361,7 @@ public class WifiNetworkAgentSpecifierTest {
WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
new WifiNetworkAgentSpecifier(
wifiConfigurationNetworkAgent,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
PatternMatcher ssidPattern =
new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
@@ -373,7 +375,7 @@ public class WifiNetworkAgentSpecifierTest {
ssidPattern,
bssidPattern,
wificonfigurationNetworkSpecifier,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -401,7 +403,7 @@ public class WifiNetworkAgentSpecifierTest {
ssidPattern,
bssidPattern,
wificonfigurationNetworkSpecifier,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE);
assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -430,7 +432,7 @@ public class WifiNetworkAgentSpecifierTest {
ssidPattern,
bssidPattern,
wificonfigurationNetworkSpecifier,
- TEST_UID_1);
+ TEST_UID_1, TEST_PACKAGE_1);
assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -446,7 +448,8 @@ public class WifiNetworkAgentSpecifierTest {
}
private WifiNetworkAgentSpecifier createDefaultNetworkAgentSpecifier() {
- return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration(), TEST_UID);
+ return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration(), TEST_UID,
+ TEST_PACKAGE);
}
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index 2a8df8dba1d2..fce247f729e0 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -38,6 +38,7 @@ import org.junit.Test;
@SmallTest
public class WifiNetworkSpecifierTest {
private static final int TEST_UID = 5;
+ private static final String TEST_PACKAGE_NAME = "com.test";
private static final String TEST_SSID = "Test123";
private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00";
private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
@@ -56,7 +57,7 @@ public class WifiNetworkSpecifierTest {
Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK)),
wifiConfiguration,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE_NAME);
Parcel parcelW = Parcel.obtain();
specifier.writeToParcel(parcelW, 0);
@@ -88,7 +89,7 @@ public class WifiNetworkSpecifierTest {
Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK)),
wifiConfiguration,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE_NAME);
assertTrue(specifier.satisfiedBy(null));
assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier()));
@@ -111,14 +112,14 @@ public class WifiNetworkSpecifierTest {
Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK)),
wifiConfiguration,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE_NAME);
WifiNetworkSpecifier specifier2 =
new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK)),
wifiConfiguration,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE_NAME);
assertTrue(specifier2.satisfiedBy(specifier1));
}
@@ -140,7 +141,7 @@ public class WifiNetworkSpecifierTest {
Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK)),
wifiConfiguration1,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE_NAME);
WifiConfiguration wifiConfiguration2 = new WifiConfiguration();
wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
@@ -149,7 +150,7 @@ public class WifiNetworkSpecifierTest {
Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK)),
wifiConfiguration2,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE_NAME);
assertFalse(specifier2.satisfiedBy(specifier1));
}
@@ -171,14 +172,14 @@ public class WifiNetworkSpecifierTest {
Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK)),
wifiConfiguration,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE_NAME);
WifiNetworkSpecifier specifier2 =
new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK)),
wifiConfiguration,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE_NAME);
assertFalse(specifier2.satisfiedBy(specifier1));
}
@@ -200,13 +201,42 @@ public class WifiNetworkSpecifierTest {
Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
MacAddress.fromString(TEST_BSSID_OUI_MASK)),
wifiConfiguration,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE_NAME);
WifiNetworkSpecifier specifier2 =
new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS),
wifiConfiguration,
- TEST_UID);
+ TEST_UID, TEST_PACKAGE_NAME);
+
+ assertFalse(specifier2.satisfiedBy(specifier1));
+ }
+
+ /**
+ * Validate NetworkSpecifier matching.
+ * a) Create network specifier 1 for WPA_PSK network
+ * b) Create network specifier 2 with different package name .
+ * c) Ensure that the specifier 2 is not satisfied by specifier 1.
+ */
+ @Test
+ public void testWifiNetworkSpecifierDoesNotSatisfyWhenPackageNameDifferent() {
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
+
+ WifiNetworkSpecifier specifier1 =
+ new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+ Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+ MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+ wifiConfiguration,
+ TEST_UID, TEST_PACKAGE_NAME);
+
+ WifiNetworkSpecifier specifier2 =
+ new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
+ Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
+ MacAddress.fromString(TEST_BSSID_OUI_MASK)),
+ wifiConfiguration,
+ TEST_UID, TEST_PACKAGE_NAME + "blah");
assertFalse(specifier2.satisfiedBy(specifier1));
}
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 31f501ff85c1..5f76055d8640 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -29,6 +29,10 @@ import org.junit.Test;
*/
@SmallTest
public class WifiNetworkSuggestionTest {
+ private static final int TEST_UID = 45677;
+ private static final int TEST_UID_OTHER = 45673;
+ private static final String TEST_PACKAGE_NAME = "com.test.packagename";
+ private static final String TEST_PACKAGE_NAME_OTHER = "com.test.packagenameother";
private static final String TEST_SSID = "\"Test123\"";
private static final String TEST_BSSID = "12:12:12:12:12:12";
private static final String TEST_SSID_1 = "\"Test1234\"";
@@ -43,7 +47,7 @@ public class WifiNetworkSuggestionTest {
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, true, 0);
+ new WifiNetworkSuggestion(configuration, false, true, TEST_UID, TEST_PACKAGE_NAME);
Parcel parcelW = Parcel.obtain();
suggestion.writeToParcel(parcelW, 0);
@@ -77,14 +81,16 @@ public class WifiNetworkSuggestionTest {
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, true, false, 0);
+ new WifiNetworkSuggestion(configuration, true, false, TEST_UID,
+ TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.BSSID = TEST_BSSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, false, true, 0);
+ new WifiNetworkSuggestion(configuration1, false, true, TEST_UID,
+ TEST_PACKAGE_NAME);
assertEquals(suggestion, suggestion1);
}
@@ -99,13 +105,15 @@ public class WifiNetworkSuggestionTest {
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, false, 0);
+ new WifiNetworkSuggestion(configuration, false, false, TEST_UID,
+ TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID_1;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, false, false, 0);
+ new WifiNetworkSuggestion(configuration1, false, false, TEST_UID,
+ TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
}
@@ -121,13 +129,15 @@ public class WifiNetworkSuggestionTest {
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, false, 0);
+ new WifiNetworkSuggestion(configuration, false, false, TEST_UID,
+ TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, false, false, 0);
+ new WifiNetworkSuggestion(configuration1, false, false, TEST_UID,
+ TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
}
@@ -142,13 +152,15 @@ public class WifiNetworkSuggestionTest {
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, false, 0);
+ new WifiNetworkSuggestion(configuration, false, false, TEST_UID,
+ TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, false, false, 0);
+ new WifiNetworkSuggestion(configuration1, false, false, TEST_UID,
+ TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
}
@@ -163,10 +175,31 @@ public class WifiNetworkSuggestionTest {
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, false, false, 0);
+ new WifiNetworkSuggestion(configuration, false, false, TEST_UID,
+ TEST_PACKAGE_NAME);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration, false, false, 1);
+ new WifiNetworkSuggestion(configuration, false, false, TEST_UID_OTHER,
+ TEST_PACKAGE_NAME);
+
+ assertNotEquals(suggestion, suggestion1);
+ }
+
+ /**
+ * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+ * SSID, BSSID and key mgmt, but different package name.
+ */
+ @Test
+ public void testWifiNetworkSuggestionEqualsFailsWhenPackageNameIsDifferent() {
+ WifiConfiguration configuration = new WifiConfiguration();
+ configuration.SSID = TEST_SSID;
+ configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ WifiNetworkSuggestion suggestion =
+ new WifiNetworkSuggestion(configuration, false, false, TEST_UID, TEST_PACKAGE_NAME);
+
+ WifiNetworkSuggestion suggestion1 =
+ new WifiNetworkSuggestion(configuration, false, false, TEST_UID,
+ TEST_PACKAGE_NAME_OTHER);
assertNotEquals(suggestion, suggestion1);
}