summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk28
-rw-r--r--api/current.txt279
-rw-r--r--api/removed.txt10
-rw-r--r--api/system-current.txt310
-rw-r--r--api/system-removed.txt10
-rw-r--r--api/test-current.txt283
-rw-r--r--api/test-removed.txt10
-rw-r--r--cmds/idmap/Android.mk2
-rw-r--r--cmds/idmap/idmap.cpp55
-rw-r--r--cmds/idmap/idmap.h6
-rw-r--r--cmds/idmap/scan.cpp240
-rw-r--r--cmds/uiautomator/library/Android.mk2
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java48
-rw-r--r--core/java/android/animation/AnimatorSet.java8
-rw-r--r--core/java/android/app/ApplicationPackageManager.java14
-rw-r--r--core/java/android/app/LoadedApk.java10
-rw-r--r--core/java/android/app/Notification.java16
-rw-r--r--core/java/android/app/NotificationChannel.java7
-rw-r--r--core/java/android/app/QueuedWork.java36
-rw-r--r--core/java/android/app/SharedPreferencesImpl.java49
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java2
-rw-r--r--core/java/android/app/assist/AssistStructure.java49
-rw-r--r--core/java/android/app/job/JobInfo.java4
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java127
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java120
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java125
-rw-r--r--core/java/android/bluetooth/BluetoothGattCallback.java132
-rw-r--r--core/java/android/bluetooth/BluetoothGattCallbackExt.java182
-rw-r--r--core/java/android/bluetooth/BluetoothGattServer.java96
-rw-r--r--core/java/android/bluetooth/BluetoothGattServerCallback.java138
-rw-r--r--core/java/android/bluetooth/BluetoothGattServerCallbackExt.java187
-rw-r--r--core/java/android/bluetooth/BluetoothMapClient.java5
-rw-r--r--core/java/android/bluetooth/IBluetooth.aidl4
-rw-r--r--core/java/android/bluetooth/IBluetoothGatt.aidl36
-rw-r--r--core/java/android/bluetooth/IBluetoothGattCallbackExt.aidl (renamed from core/java/android/bluetooth/IBluetoothGattCallback.aidl)4
-rw-r--r--core/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl (renamed from core/java/android/bluetooth/IBluetoothGattServerCallback.aidl)6
-rw-r--r--core/java/android/bluetooth/le/AdvertisingSet.java162
-rw-r--r--core/java/android/bluetooth/le/AdvertisingSetCallback.java144
-rw-r--r--core/java/android/bluetooth/le/AdvertisingSetParameters.aidl (renamed from core/java/com/android/internal/logging/legacy/Util.java)12
-rw-r--r--core/java/android/bluetooth/le/AdvertisingSetParameters.java409
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeAdvertiser.java194
-rw-r--r--core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl32
-rw-r--r--core/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl31
-rw-r--r--core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java77
-rw-r--r--core/java/android/bluetooth/le/PeriodicAdvertisingManager.java237
-rw-r--r--core/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl19
-rw-r--r--core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java134
-rw-r--r--core/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl19
-rw-r--r--core/java/android/bluetooth/le/PeriodicAdvertisingReport.java184
-rw-r--r--core/java/android/bluetooth/le/ScanResult.java207
-rw-r--r--core/java/android/bluetooth/le/ScanSettings.java86
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java41
-rw-r--r--core/java/android/companion/ICompanionDeviceDiscoveryService.aidl1
-rw-r--r--core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl2
-rw-r--r--core/java/android/companion/ICompanionDeviceManager.aidl3
-rw-r--r--core/java/android/content/Intent.java6
-rw-r--r--core/java/android/content/IntentFilter.java14
-rw-r--r--core/java/android/content/om/IOverlayManager.aidl5
-rw-r--r--core/java/android/content/pm/PackageManager.java12
-rw-r--r--core/java/android/content/pm/PackageParser.java17
-rw-r--r--core/java/android/content/pm/PackageStats.java39
-rw-r--r--core/java/android/content/pm/SELinuxUtil.java9
-rw-r--r--core/java/android/content/res/FontResourcesParser.java5
-rw-r--r--core/java/android/hardware/SensorDirectChannel.java4
-rw-r--r--core/java/android/hardware/SensorManager.java8
-rw-r--r--core/java/android/hardware/SystemSensorManager.java56
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java82
-rw-r--r--core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java3
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java38
-rw-r--r--core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java4
-rw-r--r--core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java8
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java14
-rw-r--r--core/java/android/hardware/hdmi/IHdmiControlService.aidl1
-rw-r--r--core/java/android/metrics/LogMaker.java25
-rw-r--r--core/java/android/metrics/MetricsReader.java85
-rw-r--r--core/java/android/net/ConnectivityManager.java148
-rw-r--r--core/java/android/net/IConnectivityManager.aidl2
-rw-r--r--core/java/android/net/NetworkScoreManager.java37
-rw-r--r--core/java/android/nfc/NdefRecord.java2
-rw-r--r--core/java/android/os/BaseBundle.java10
-rw-r--r--core/java/android/os/BatteryStats.java51
-rw-r--r--core/java/android/os/Bundle.java12
-rw-r--r--core/java/android/os/Environment.java5
-rw-r--r--core/java/android/os/FileUtils.java13
-rw-r--r--core/java/android/os/PatternMatcher.java6
-rw-r--r--core/java/android/os/PersistableBundle.java9
-rw-r--r--core/java/android/os/RemoteCallbackList.java31
-rw-r--r--core/java/android/os/storage/StorageManager.java5
-rw-r--r--core/java/android/preference/PreferenceActivity.java22
-rw-r--r--core/java/android/provider/FontsContract.java104
-rwxr-xr-xcore/java/android/provider/Settings.java13
-rw-r--r--core/java/android/service/autofill/FillResponse.java60
-rw-r--r--core/java/android/text/FontConfig.java22
-rw-r--r--core/java/android/text/Hyphenator.java113
-rw-r--r--core/java/android/text/StaticLayout.java3
-rw-r--r--core/java/android/util/AtomicFile.java19
-rw-r--r--core/java/android/util/ExceptionUtils.java10
-rw-r--r--core/java/android/util/MapCollections.java3
-rw-r--r--core/java/android/util/Patterns.java18
-rw-r--r--core/java/android/view/Display.java1
-rw-r--r--core/java/android/view/IPinnedStackListener.aidl6
-rw-r--r--core/java/android/view/NotificationHeaderView.java40
-rw-r--r--core/java/android/view/SurfaceControl.java23
-rw-r--r--core/java/android/view/SurfaceSession.java5
-rw-r--r--core/java/android/view/SurfaceView.java451
-rw-r--r--core/java/android/view/View.java255
-rw-r--r--core/java/android/view/ViewDebug.java24
-rw-r--r--core/java/android/view/ViewGroup.java188
-rw-r--r--core/java/android/view/ViewRootImpl.java10
-rw-r--r--core/java/android/view/ViewStructure.java9
-rw-r--r--core/java/android/view/autofill/AutoFillManager.java142
-rw-r--r--core/java/android/view/autofill/AutoFillType.aidl3
-rw-r--r--core/java/android/view/autofill/AutoFillType.java4
-rw-r--r--core/java/android/view/autofill/AutoFillValue.java34
-rw-r--r--core/java/android/view/autofill/IAutoFillManager.aidl4
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl6
-rw-r--r--core/java/android/view/textclassifier/TextClassifier.java70
-rw-r--r--core/java/android/view/textclassifier/TextClassifierImpl.java39
-rw-r--r--core/java/android/webkit/WebView.java10
-rw-r--r--core/java/android/widget/AbsSpinner.java47
-rw-r--r--core/java/android/widget/CompoundButton.java5
-rw-r--r--core/java/android/widget/DatePicker.java10
-rw-r--r--core/java/android/widget/ImageView.java5
-rw-r--r--core/java/android/widget/RadioGroup.java10
-rw-r--r--core/java/android/widget/SelectionActionModeHelper.java31
-rw-r--r--core/java/android/widget/Spinner.java20
-rw-r--r--core/java/android/widget/TextView.java11
-rw-r--r--core/java/android/widget/TimePicker.java10
-rw-r--r--core/java/com/android/internal/logging/MetricsLogger.java3
-rw-r--r--core/java/com/android/internal/logging/legacy/EventLogCollector.java173
-rw-r--r--core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java101
-rw-r--r--core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java66
-rw-r--r--core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java48
-rwxr-xr-xcore/java/com/android/internal/logging/legacy/TagParser.java104
-rw-r--r--core/java/com/android/internal/logging/legacy/TronCounters.java72
-rw-r--r--core/java/com/android/internal/logging/legacy/TronLogger.java49
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java34
-rw-r--r--core/java/com/android/internal/policy/PipSnapAlgorithm.java48
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java16
-rw-r--r--core/java/com/android/internal/util/ExponentiallyBucketedHistogram.java97
-rw-r--r--core/java/com/android/internal/view/SurfaceCallbackHelper.java21
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuItem.java8
-rw-r--r--core/java/com/android/internal/view/menu/MenuItemImpl.java4
-rw-r--r--core/java/com/android/internal/widget/NotificationExpandButton.java8
-rw-r--r--core/java/com/android/server/BootReceiver.java60
-rw-r--r--core/jni/Android.mk1
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp166
-rw-r--r--core/jni/android/graphics/GraphicBuffer.cpp23
-rw-r--r--core/jni/android/graphics/Graphics.cpp9
-rw-r--r--core/jni/android/graphics/GraphicsJNI.h1
-rw-r--r--core/jni/android_database_CursorWindow.cpp8
-rw-r--r--core/jni/android_hardware_HardwareBuffer.cpp27
-rw-r--r--core/jni/android_hardware_SensorManager.cpp60
-rw-r--r--core/jni/android_text_StaticLayout.cpp8
-rw-r--r--core/jni/android_util_AssetManager.cpp93
-rw-r--r--core/jni/android_view_RenderNode.cpp8
-rw-r--r--core/jni/android_view_SurfaceControl.cpp28
-rw-r--r--core/jni/android_view_SurfaceSession.cpp11
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp4
-rw-r--r--core/res/res/layout/notification_template_header.xml2
-rw-r--r--core/res/res/values-television/config.xml7
-rw-r--r--core/res/res/values/attrs.xml22
-rw-r--r--core/res/res/values/attrs_manifest.xml12
-rw-r--r--core/res/res/values/config.xml14
-rw-r--r--core/res/res/values/dimens.xml3
-rw-r--r--core/res/res/values/public.xml9
-rw-r--r--core/res/res/values/strings.xml7
-rw-r--r--core/res/res/values/symbols.xml8
-rw-r--r--core/tests/coretests/Android.mk1
-rw-r--r--core/tests/coretests/res/font/samplexmldownloadedfont.xml3
-rw-r--r--core/tests/coretests/src/android/content/res/FontResourcesParserTest.java3
-rw-r--r--core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java193
-rw-r--r--core/tests/coretests/src/android/metrics/LogMakerTest.java49
-rw-r--r--core/tests/coretests/src/android/net/SSLSessionCacheTest.java12
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java49
-rw-r--r--core/tests/coretests/src/android/provider/FontsContractTest.java235
-rw-r--r--core/tests/coretests/src/android/provider/TestFontsProvider.java117
-rw-r--r--core/tests/coretests/src/android/view/PopupWindowVisibility.java2
-rw-r--r--core/tests/coretests/src/android/widget/focus/RequestFocusTest.java6
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java111
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java69
-rw-r--r--core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java67
-rw-r--r--data/etc/platform.xml1
-rw-r--r--graphics/java/android/graphics/Bitmap.java65
-rw-r--r--graphics/java/android/graphics/ColorSpace.java691
-rw-r--r--graphics/java/android/graphics/Typeface.java4
-rw-r--r--graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java19
-rw-r--r--graphics/java/android/graphics/fonts/FontRequest.java76
-rw-r--r--libs/androidfw/AssetManager.cpp104
-rw-r--r--libs/androidfw/include/androidfw/AssetManager.h15
-rw-r--r--libs/hwui/BakedOpState.cpp16
-rw-r--r--libs/hwui/BakedOpState.h9
-rw-r--r--libs/hwui/Caches.h2
-rw-r--r--libs/hwui/Extensions.cpp17
-rw-r--r--libs/hwui/Extensions.h7
-rw-r--r--libs/hwui/FrameBuilder.cpp12
-rw-r--r--libs/hwui/FrameBuilder.h3
-rw-r--r--libs/hwui/GradientCache.cpp4
-rw-r--r--libs/hwui/GradientCache.h2
-rw-r--r--libs/hwui/ProgramCache.cpp6
-rw-r--r--libs/hwui/ProgramCache.h2
-rw-r--r--libs/hwui/Texture.cpp16
-rw-r--r--libs/hwui/Texture.h6
-rw-r--r--libs/hwui/TextureCache.h2
-rw-r--r--libs/hwui/TreeInfo.h2
-rw-r--r--libs/hwui/hwui/Bitmap.cpp29
-rw-r--r--libs/hwui/renderstate/RenderState.cpp6
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp13
-rw-r--r--libs/hwui/tests/unit/BakedOpStateTests.cpp16
-rw-r--r--libs/hwui/tests/unit/FrameBuilderTests.cpp29
-rw-r--r--libs/hwui/utils/Color.h2
-rw-r--r--media/java/android/media/MediaCodecInfo.java4
-rw-r--r--media/java/android/media/VolumeShaper.java221
-rw-r--r--media/java/android/media/tv/TvView.java4
-rw-r--r--media/jni/android_media_VolumeShaper.h18
-rw-r--r--native/android/Android.mk1
-rw-r--r--native/android/libandroid.map.txt1
-rw-r--r--native/android/sensor.cpp154
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java4
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java38
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java414
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java32
-rw-r--r--packages/SystemUI/res/drawable/pip_expand.xml38
-rw-r--r--packages/SystemUI/res/layout/pip_menu_activity.xml21
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/res/xml/tuner_prefs.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java123
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java82
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java77
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java191
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java19
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java63
-rw-r--r--proto/src/metrics_constants.proto54
-rw-r--r--rs/java/android/renderscript/Element.java2
-rw-r--r--rs/java/android/renderscript/Type.java1
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillManagerService.java13
-rw-r--r--services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java56
-rw-r--r--services/autofill/java/com/android/server/autofill/RemoteFillService.java4
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java16
-rw-r--r--services/core/java/com/android/server/BatteryService.java5
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java20
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java92
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java14
-rw-r--r--services/core/java/com/android/server/NetworkScoreService.java10
-rw-r--r--services/core/java/com/android/server/NetworkScorerAppManager.java92
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java26
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java20
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java33
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java2
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java4
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java12
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java8
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/OffloadController.java53
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java10
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java34
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java1
-rw-r--r--services/core/java/com/android/server/notification/NotificationRecord.java6
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java83
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java74
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java32
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerSettings.java16
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java149
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java7
-rw-r--r--services/core/java/com/android/server/storage/AppCollector.java46
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java10
-rw-r--r--services/core/java/com/android/server/wm/BoundsAnimationController.java14
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackController.java52
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java21
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfaceController.java14
-rw-r--r--services/core/jni/com_android_server_location_GnssLocationProvider.cpp2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java41
-rw-r--r--services/print/java/com/android/server/print/CompanionDeviceManagerService.java200
-rw-r--r--services/tests/notification/src/com/android/server/notification/RankingHelperTest.java69
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java91
-rw-r--r--services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java83
-rw-r--r--services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java147
-rw-r--r--telecomm/java/android/telecom/Call.java46
-rw-r--r--telecomm/java/android/telecom/Connection.java171
-rw-r--r--telecomm/java/android/telecom/ConnectionService.java188
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapter.java62
-rw-r--r--telecomm/java/android/telecom/ConnectionServiceAdapterServant.java44
-rw-r--r--telecomm/java/android/telecom/InCallAdapter.java16
-rw-r--r--telecomm/java/android/telecom/InCallService.java15
-rw-r--r--telecomm/java/android/telecom/ParcelableCall.java9
-rw-r--r--telecomm/java/android/telecom/Phone.java17
-rw-r--r--telecomm/java/android/telecom/RemoteConnection.java146
-rw-r--r--telecomm/java/android/telecom/RemoteConnectionService.java52
-rw-r--r--telecomm/java/android/telecom/VideoCallImpl.java7
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionService.aidl13
-rw-r--r--telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl8
-rw-r--r--telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl8
-rw-r--r--telecomm/java/com/android/internal/telecom/IInCallService.aidl2
-rw-r--r--telecomm/java/com/android/internal/telecom/IVideoProvider.aidl2
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java22
-rw-r--r--telephony/java/android/telephony/VisualVoicemailService.java7
-rw-r--r--telephony/java/android/telephony/ims/ImsServiceBase.java10
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl2
-rw-r--r--tests/FeatureSplit/base/Android.mk1
-rw-r--r--tests/FeatureSplit/feature1/Android.mk12
-rw-r--r--tests/FeatureSplit/feature2/Android.mk15
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java2
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java2
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java2
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java2
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java2
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java2
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java2
-rw-r--r--tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java2
-rw-r--r--tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java2
-rw-r--r--tests/TtsTests/Android.mk2
-rw-r--r--tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java95
-rw-r--r--tests/net/Android.mk14
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java28
-rw-r--r--tools/aapt/ResourceTable.cpp4
-rw-r--r--tools/aapt2/Main.cpp2
-rw-r--r--tools/aapt2/diff/Diff.cpp2
-rw-r--r--tools/aapt2/flatten/XmlFlattener_test.cpp31
-rw-r--r--tools/aapt2/link/Link.cpp110
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp64
-rw-r--r--tools/aapt2/link/ReferenceLinker.h12
-rw-r--r--tools/aapt2/link/ReferenceLinker_test.cpp74
-rw-r--r--tools/aapt2/link/XmlReferenceLinker.cpp35
-rw-r--r--tools/aapt2/process/SymbolTable_test.cpp6
-rw-r--r--tools/aapt2/readme.md8
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java14
-rw-r--r--tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java2
-rw-r--r--tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java5
-rw-r--r--wifi/java/android/net/wifi/aware/DiscoverySession.java145
-rw-r--r--wifi/java/android/net/wifi/aware/WifiAwareManager.java30
-rw-r--r--wifi/java/android/net/wifi/aware/WifiAwareSession.java64
-rw-r--r--wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java34
350 files changed, 11972 insertions, 4254 deletions
diff --git a/Android.mk b/Android.mk
index 6fc8fbfd9c95..0e5dfed53607 100644
--- a/Android.mk
+++ b/Android.mk
@@ -140,9 +140,11 @@ LOCAL_SRC_FILES += \
core/java/android/bluetooth/IBluetoothInputHost.aidl \
core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl \
core/java/android/bluetooth/IBluetoothGatt.aidl \
- core/java/android/bluetooth/IBluetoothGattCallback.aidl \
- core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \
+ core/java/android/bluetooth/IBluetoothGattCallbackExt.aidl \
+ core/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl \
core/java/android/bluetooth/le/IAdvertiserCallback.aidl \
+ core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl \
+ core/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl \
core/java/android/bluetooth/le/IScannerCallback.aidl \
core/java/android/content/IClipboard.aidl \
core/java/android/content/IContentService.aidl \
@@ -942,6 +944,28 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \
-werror -hide 111 -hide 113 \
-overview $(LOCAL_PATH)/core/java/overview.html
+SUPPORT_API_DIR := ./frameworks/support/api
+
+# More API Level information for the Support Library, which is currently
+# included as part of the core framework docs build.
+framework_docs_LOCAL_DROIDDOC_OPTIONS += \
+ -since $(SUPPORT_API_DIR)/22.0.0.txt 22.0.0 \
+ -since $(SUPPORT_API_DIR)/22.0.0.txt 22.0.0 \
+ -since $(SUPPORT_API_DIR)/22.1.0.txt 22.1.0 \
+ -since $(SUPPORT_API_DIR)/22.2.0.txt 22.2.0 \
+ -since $(SUPPORT_API_DIR)/22.2.1.txt 22.2.1 \
+ -since $(SUPPORT_API_DIR)/23.0.0.txt 23.0.0 \
+ -since $(SUPPORT_API_DIR)/23.1.0.txt 23.1.0 \
+ -since $(SUPPORT_API_DIR)/23.1.1.txt 23.1.1 \
+ -since $(SUPPORT_API_DIR)/23.2.0.txt 23.2.0 \
+ -since $(SUPPORT_API_DIR)/23.2.1.txt 23.2.1 \
+ -since $(SUPPORT_API_DIR)/23.4.0.txt 23.4.0 \
+ -since $(SUPPORT_API_DIR)/24.0.0.txt 24.0.0 \
+ -since $(SUPPORT_API_DIR)/24.1.0.txt 24.1.0 \
+ -since $(SUPPORT_API_DIR)/24.2.0.txt 24.2.0 \
+ -since $(SUPPORT_API_DIR)/25.0.0.txt 25.0.0 \
+ -since $(SUPPORT_API_DIR)/25.1.0.txt 25.1.0
+
framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
$(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
diff --git a/api/current.txt b/api/current.txt
index 6685c1ed9eb9..6a62fdea1853 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -607,8 +607,9 @@ package android {
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int fontProviderAuthority = 16844114; // 0x1010552
+ field public static final int fontProviderPackage = 16844122; // 0x101055a
field public static final int fontProviderQuery = 16844115; // 0x1010553
- field public static final int fontStyle = 16844081; // 0x1010531
+ field public static final int fontStyle = 16844095; // 0x101053f
field public static final int fontWeight = 16844083; // 0x1010533
field public static final int footerDividersEnabled = 16843311; // 0x101022f
field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
@@ -709,6 +710,7 @@ package android {
field public static final int imeSubtypeMode = 16843501; // 0x10102ed
field public static final int immersive = 16843456; // 0x10102c0
field public static final int importantForAccessibility = 16843690; // 0x10103aa
+ field public static final int importantForAutofill = 16844123; // 0x101055b
field public static final int inAnimation = 16843127; // 0x1010177
field public static final int includeFontPadding = 16843103; // 0x101015f
field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -1459,7 +1461,7 @@ package android {
field public static final int viewportWidth = 16843778; // 0x1010402
field public static final int visibility = 16842972; // 0x10100dc
field public static final int visible = 16843156; // 0x1010194
- field public static final int visibleToInstantApps = 16844095; // 0x101053f
+ field public static final int visibleToInstantApps = 16844081; // 0x1010531
field public static final int vmSafeMode = 16843448; // 0x10102b8
field public static final int voiceIcon = 16843908; // 0x1010484
field public static final int voiceLanguage = 16843349; // 0x1010255
@@ -2811,6 +2813,7 @@ package android.accessibilityservice {
method public android.content.pm.ResolveInfo getResolveInfo();
method public java.lang.String getSettingsActivityName();
method public java.lang.String loadDescription(android.content.pm.PackageManager);
+ method public java.lang.String loadSummary(android.content.pm.PackageManager);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES = 64; // 0x40
field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
@@ -6560,8 +6563,9 @@ package android.app.assist {
method public int getAutoFillHint();
method public android.view.autofill.AutoFillId getAutoFillId();
method public java.lang.String[] getAutoFillOptions();
- method public android.view.autofill.AutoFillType getAutoFillType();
+ method public deprecated android.view.autofill.AutoFillType getAutoFillType();
method public android.view.autofill.AutoFillValue getAutoFillValue();
+ method public int getAutofillType();
method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
method public int getChildCount();
method public java.lang.String getClassName();
@@ -7112,6 +7116,7 @@ package android.bluetooth {
method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
method public java.lang.String getName();
+ method public android.bluetooth.le.PeriodicAdvertisingManager getPeriodicAdvertisingManager();
method public int getProfileConnectionState(int);
method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int);
method public android.bluetooth.BluetoothDevice getRemoteDevice(java.lang.String);
@@ -7120,6 +7125,10 @@ package android.bluetooth {
method public int getState();
method public boolean isDiscovering();
method public boolean isEnabled();
+ method public boolean isLe2MPhySupported();
+ method public boolean isLeCodedPhySupported();
+ method public boolean isLeExtendedAdvertisingSupported();
+ method public boolean isLePeriodicAdvertisingSupported();
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
@@ -7488,6 +7497,9 @@ package android.bluetooth {
public final class BluetoothDevice implements android.os.Parcelable {
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int, int);
method public boolean createBond();
method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
@@ -7531,6 +7543,13 @@ package android.bluetooth {
field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
field public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; // 0x2
field public static final int PAIRING_VARIANT_PIN = 0; // 0x0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_ANY = 7; // 0x7
+ field public static final int PHY_LE_CODED = 4; // 0x4
+ field public static final int PHY_OPTION_NO_PREFERRED = 0; // 0x0
+ field public static final int PHY_OPTION_S2 = 1; // 0x1
+ field public static final int PHY_OPTION_S8 = 2; // 0x2
field public static final int TRANSPORT_AUTO = 0; // 0x0
field public static final int TRANSPORT_BREDR = 1; // 0x1
field public static final int TRANSPORT_LE = 2; // 0x2
@@ -7553,10 +7572,12 @@ package android.bluetooth {
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method public void readPhy();
method public boolean readRemoteRssi();
method public boolean requestConnectionPriority(int);
method public boolean requestMtu(int);
method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void setPreferredPhy(int, int, int);
method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0
@@ -7574,8 +7595,12 @@ package android.bluetooth {
field public static final int GATT_WRITE_NOT_PERMITTED = 3; // 0x3
}
- public abstract class BluetoothGattCallback {
+ public abstract deprecated class BluetoothGattCallback extends android.bluetooth.BluetoothGattCallbackExt {
ctor public BluetoothGattCallback();
+ }
+
+ public abstract class BluetoothGattCallbackExt {
+ ctor public BluetoothGattCallbackExt();
method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
@@ -7583,6 +7608,8 @@ package android.bluetooth {
method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onMtuChanged(android.bluetooth.BluetoothGatt, int, int);
+ method public void onPhyRead(android.bluetooth.BluetoothGatt, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
@@ -7676,12 +7703,18 @@ package android.bluetooth {
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void readPhy(android.bluetooth.BluetoothDevice);
method public boolean removeService(android.bluetooth.BluetoothGattService);
method public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
+ method public void setPreferredPhy(android.bluetooth.BluetoothDevice, int, int, int);
}
- public abstract class BluetoothGattServerCallback {
+ public abstract deprecated class BluetoothGattServerCallback extends android.bluetooth.BluetoothGattServerCallbackExt {
ctor public BluetoothGattServerCallback();
+ }
+
+ public abstract class BluetoothGattServerCallbackExt {
+ ctor public BluetoothGattServerCallbackExt();
method public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattCharacteristic, boolean, boolean, int, byte[]);
method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int);
@@ -7690,6 +7723,8 @@ package android.bluetooth {
method public void onExecuteWrite(android.bluetooth.BluetoothDevice, int, boolean);
method public void onMtuChanged(android.bluetooth.BluetoothDevice, int);
method public void onNotificationSent(android.bluetooth.BluetoothDevice, int);
+ method public void onPhyRead(android.bluetooth.BluetoothDevice, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothDevice, int, int, int);
method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
}
@@ -7890,10 +7925,85 @@ package android.bluetooth.le {
method public android.bluetooth.le.AdvertiseSettings.Builder setTxPowerLevel(int);
}
+ public final class AdvertisingSet {
+ method public void enableAdvertising(boolean);
+ method public void periodicAdvertisingEnable(boolean);
+ method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
+ method public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters);
+ method public void setScanResponseData(android.bluetooth.le.AdvertiseData);
+ }
+
+ public abstract class AdvertisingSetCallback {
+ ctor public AdvertisingSetCallback();
+ method public void onAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingEnabled(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStopped(android.bluetooth.le.AdvertisingSet);
+ method public void onPeriodicAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onPeriodicAdvertisingEnable(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onPeriodicAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onScanResponseDataSet(android.bluetooth.le.AdvertisingSet, int);
+ field public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; // 0x3
+ field public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; // 0x1
+ field public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; // 0x5
+ field public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; // 0x4
+ field public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; // 0x2
+ field public static final int ADVERTISE_SUCCESS = 0; // 0x0
+ }
+
+ public final class AdvertisingSetParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getInterval();
+ method public int getPrimaryPhy();
+ method public int getSecondaryPhy();
+ method public int getTimeout();
+ method public int getTxPowerLevel();
+ method public boolean includeTxPower();
+ method public boolean isAnonymous();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertisingSetParameters> CREATOR;
+ field public static final int INTERVAL_HIGH = 160; // 0xa0
+ field public static final int INTERVAL_LOW = 1600; // 0x640
+ field public static final int INTERVAL_MAX = 16777215; // 0xffffff
+ field public static final int INTERVAL_MEDIUM = 400; // 0x190
+ field public static final int INTERVAL_MIN = 160; // 0xa0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int TX_POWER_HIGH = 1; // 0x1
+ field public static final int TX_POWER_LOW = -15; // 0xfffffff1
+ field public static final int TX_POWER_MAX = 1; // 0x1
+ field public static final int TX_POWER_MEDIUM = -7; // 0xfffffff9
+ field public static final int TX_POWER_MIN = -127; // 0xffffff81
+ field public static final int TX_POWER_ULTRA_LOW = -21; // 0xffffffeb
+ }
+
+ public static final class AdvertisingSetParameters.Builder {
+ ctor public AdvertisingSetParameters.Builder();
+ method public android.bluetooth.le.AdvertisingSetParameters build();
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setAnonymouus(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setConnectable(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setInterval(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
+ }
+
public final class BluetoothLeAdvertiser {
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
+ method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
}
public final class BluetoothLeScanner {
@@ -7903,6 +8013,53 @@ package android.bluetooth.le {
method public void stopScan(android.bluetooth.le.ScanCallback);
}
+ public abstract class PeriodicAdvertisingCallback {
+ ctor public PeriodicAdvertisingCallback();
+ method public void onPeriodicAdvertisingReport(android.bluetooth.le.PeriodicAdvertisingReport);
+ method public void onSyncEstablished(int, android.bluetooth.BluetoothDevice, int, int, int, int);
+ method public void onSyncLost(int);
+ field public static final int SYNC_NO_RESOURCES = 2; // 0x2
+ field public static final int SYNC_NO_RESPONSE = 1; // 0x1
+ }
+
+ public final class PeriodicAdvertisingManager {
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback);
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback, android.os.Handler);
+ method public void unregisterSync(android.bluetooth.le.PeriodicAdvertisingCallback);
+ }
+
+ public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getEnable();
+ method public boolean getIncludeTxPower();
+ method public int getInterval();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingParameters> CREATOR;
+ }
+
+ public static final class PeriodicAdvertisingParameters.Builder {
+ ctor public PeriodicAdvertisingParameters.Builder();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters build();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setEnable(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setInterval(int);
+ }
+
+ public final class PeriodicAdvertisingReport implements android.os.Parcelable {
+ ctor public PeriodicAdvertisingReport(int, int, int, int, android.bluetooth.le.ScanRecord);
+ method public int describeContents();
+ method public android.bluetooth.le.ScanRecord getData();
+ method public int getDataStatus();
+ method public int getRssi();
+ method public int getSyncHandle();
+ method public long getTimestampNanos();
+ method public int getTxPower();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingReport> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_INCOMPLETE_TRUNCATED = 2; // 0x2
+ }
+
public abstract class ScanCallback {
ctor public ScanCallback();
method public void onBatchScanResults(java.util.List<android.bluetooth.le.ScanResult>);
@@ -7957,19 +8114,37 @@ package android.bluetooth.le {
}
public final class ScanResult implements android.os.Parcelable {
- ctor public ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public deprecated ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public ScanResult(android.bluetooth.BluetoothDevice, int, int, int, int, int, int, int, android.bluetooth.le.ScanRecord, long);
method public int describeContents();
+ method public int getAdvertisingSid();
+ method public int getDataStatus();
method public android.bluetooth.BluetoothDevice getDevice();
+ method public int getPeriodicAdvertisingInterval();
+ method public int getPrimaryPhy();
method public int getRssi();
method public android.bluetooth.le.ScanRecord getScanRecord();
+ method public int getSecondaryPhy();
method public long getTimestampNanos();
+ method public int getTxPower();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.bluetooth.le.ScanResult> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_TRUNCATED = 2; // 0x2
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int PHY_UNUSED = 0; // 0x0
+ field public static final int SID_NOT_PRESENT = 255; // 0xff
}
public final class ScanSettings implements android.os.Parcelable {
method public int describeContents();
method public int getCallbackType();
+ method public boolean getLegacy();
+ method public int getPhy();
method public long getReportDelayMillis();
method public int getScanMode();
method public int getScanResultType();
@@ -7983,6 +8158,9 @@ package android.bluetooth.le {
field public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2; // 0x2
field public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3; // 0x3
field public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1; // 0x1
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_ALL_SUPPORTED = 255; // 0xff
+ field public static final int PHY_LE_CODED = 3; // 0x3
field public static final int SCAN_MODE_BALANCED = 1; // 0x1
field public static final int SCAN_MODE_LOW_LATENCY = 2; // 0x2
field public static final int SCAN_MODE_LOW_POWER = 0; // 0x0
@@ -7993,8 +8171,10 @@ package android.bluetooth.le {
ctor public ScanSettings.Builder();
method public android.bluetooth.le.ScanSettings build();
method public android.bluetooth.le.ScanSettings.Builder setCallbackType(int);
+ method public android.bluetooth.le.ScanSettings.Builder setLegacy(boolean);
method public android.bluetooth.le.ScanSettings.Builder setMatchMode(int);
method public android.bluetooth.le.ScanSettings.Builder setNumOfMatches(int);
+ method public android.bluetooth.le.ScanSettings.Builder setPhy(int);
method public android.bluetooth.le.ScanSettings.Builder setReportDelay(long);
method public android.bluetooth.le.ScanSettings.Builder setScanMode(int);
}
@@ -8046,6 +8226,8 @@ package android.companion {
public final class CompanionDeviceManager {
method public void associate(android.companion.AssociationRequest<?>, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
+ method public void disassociate(java.lang.String);
+ method public java.util.List<java.lang.String> getAssociations();
field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE";
}
@@ -10523,7 +10705,7 @@ package android.content.pm {
ctor public PackageManager.NameNotFoundException(java.lang.String);
}
- public class PackageStats implements android.os.Parcelable {
+ public deprecated class PackageStats implements android.os.Parcelable {
ctor public PackageStats(java.lang.String);
ctor public PackageStats(android.os.Parcel);
ctor public PackageStats(android.content.pm.PackageStats);
@@ -12210,6 +12392,7 @@ package android.graphics {
method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]);
method public final int getAllocationByteCount();
method public final int getByteCount();
+ method public final android.graphics.ColorSpace getColorSpace();
method public final android.graphics.Bitmap.Config getConfig();
method public int getDensity();
method public int getGenerationId();
@@ -12592,6 +12775,7 @@ package android.graphics {
method public java.lang.String getName();
method public boolean isSrgb();
method public abstract boolean isWideGamut();
+ method public static android.graphics.ColorSpace match(float[], android.graphics.ColorSpace.Rgb.TransferParameters);
method public float[] toXyz(float, float, float);
method public abstract float[] toXyz(float[]);
field public static final float[] ILLUMINANT_A;
@@ -12676,6 +12860,10 @@ package android.graphics {
public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
ctor public ColorSpace.Rgb(java.lang.String, float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator);
ctor public ColorSpace.Rgb(java.lang.String, float[], float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator, float, float);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], android.graphics.ColorSpace.Rgb.TransferParameters);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], float[], android.graphics.ColorSpace.Rgb.TransferParameters);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], double);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], float[], double);
method public float[] fromLinear(float, float, float);
method public float[] fromLinear(float[]);
method public float[] fromXyz(float[]);
@@ -12687,6 +12875,7 @@ package android.graphics {
method public java.util.function.DoubleUnaryOperator getOetf();
method public float[] getPrimaries(float[]);
method public float[] getPrimaries();
+ method public android.graphics.ColorSpace.Rgb.TransferParameters getTransferParameters();
method public float[] getTransform(float[]);
method public float[] getTransform();
method public float[] getWhitePoint(float[]);
@@ -12697,6 +12886,18 @@ package android.graphics {
method public float[] toXyz(float[]);
}
+ public static class ColorSpace.Rgb.TransferParameters {
+ ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double);
+ ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double, double, double);
+ field public final double a;
+ field public final double b;
+ field public final double c;
+ field public final double d;
+ field public final double e;
+ field public final double f;
+ field public final double g;
+ }
+
public class ComposePathEffect extends android.graphics.PathEffect {
ctor public ComposePathEffect(android.graphics.PathEffect, android.graphics.PathEffect);
}
@@ -14088,9 +14289,12 @@ package android.graphics.drawable.shapes {
package android.graphics.fonts {
public final class FontRequest implements android.os.Parcelable {
- ctor public FontRequest(java.lang.String, java.lang.String);
+ ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String);
+ ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.util.List<byte[]>>);
method public int describeContents();
+ method public java.util.List<java.util.List<byte[]>> getCertificates();
method public java.lang.String getProviderAuthority();
+ method public java.lang.String getProviderPackage();
method public java.lang.String getQuery();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
@@ -19634,6 +19838,8 @@ package android.icu.util {
field public static final int SHORT_COMMONLY_USED = 6; // 0x6
field public static final int SHORT_GENERIC = 2; // 0x2
field public static final int SHORT_GMT = 4; // 0x4
+ field public static final int TIMEZONE_ICU = 0; // 0x0
+ field public static final int TIMEZONE_JDK = 1; // 0x1
field public static final android.icu.util.TimeZone UNKNOWN_ZONE;
field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown";
}
@@ -24948,6 +25154,7 @@ package android.net {
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
method public deprecated void setNetworkPreference(int);
method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
@@ -24995,6 +25202,7 @@ package android.net {
method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
method public void onLosing(android.net.Network, int);
method public void onLost(android.net.Network);
+ method public void onUnavailable();
}
public static abstract interface ConnectivityManager.OnNetworkActiveListener {
@@ -30269,7 +30477,7 @@ package android.os {
ctor public Bundle(android.os.Bundle);
ctor public Bundle(android.os.PersistableBundle);
method public java.lang.Object clone();
- method public android.os.Bundle deepcopy();
+ method public android.os.Bundle deepCopy();
method public int describeContents();
method public android.os.IBinder getBinder(java.lang.String);
method public android.os.Bundle getBundle(java.lang.String);
@@ -30994,6 +31202,7 @@ package android.os {
method public boolean match(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.os.PatternMatcher> CREATOR;
+ field public static final int PATTERN_ADVANCED_GLOB = 3; // 0x3
field public static final int PATTERN_LITERAL = 0; // 0x0
field public static final int PATTERN_PREFIX = 1; // 0x1
field public static final int PATTERN_SIMPLE_GLOB = 2; // 0x2
@@ -31004,7 +31213,7 @@ package android.os {
ctor public PersistableBundle(int);
ctor public PersistableBundle(android.os.PersistableBundle);
method public java.lang.Object clone();
- method public android.os.PersistableBundle deepcopy();
+ method public android.os.PersistableBundle deepCopy();
method public int describeContents();
method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
method public void putPersistableBundle(java.lang.String, android.os.PersistableBundle);
@@ -31112,6 +31321,7 @@ package android.os {
method public E getBroadcastItem(int);
method public java.lang.Object getRegisteredCallbackCookie(int);
method public int getRegisteredCallbackCount();
+ method public E getRegisteredCallbackItem(int);
method public void kill();
method public void onCallbackDied(E);
method public void onCallbackDied(E, java.lang.Object);
@@ -38198,6 +38408,7 @@ package android.telecom {
method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
method public void onParentChanged(android.telecom.Call, android.telecom.Call);
method public void onPostDialWait(android.telecom.Call, java.lang.String);
+ method public void onRttInitiationFailure(android.telecom.Call, int);
method public void onRttModeChanged(android.telecom.Call, int);
method public void onRttRequest(android.telecom.Call, int);
method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall);
@@ -38464,6 +38675,15 @@ package android.telecom {
field public static final int STATE_RINGING = 2; // 0x2
}
+ public static final class Connection.RttModifyStatus {
+ ctor public Connection.RttModifyStatus();
+ field public static final int SESSION_MODIFY_REQUEST_FAIL = 2; // 0x2
+ field public static final int SESSION_MODIFY_REQUEST_INVALID = 3; // 0x3
+ field public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5; // 0x5
+ field public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1; // 0x1
+ field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4
+ }
+
public static abstract class Connection.VideoProvider {
ctor public Connection.VideoProvider();
method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
@@ -38521,9 +38741,9 @@ package android.telecom {
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -39628,6 +39848,7 @@ package android.telephony {
field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
+ field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_STATE = "state";
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
@@ -43367,6 +43588,7 @@ package android.view {
field public static final int STATE_OFF = 1; // 0x1
field public static final int STATE_ON = 2; // 0x2
field public static final int STATE_UNKNOWN = 0; // 0x0
+ field public static final int STATE_VR = 5; // 0x5
}
public static final class Display.HdrCapabilities implements android.os.Parcelable {
@@ -44832,8 +45054,9 @@ package android.view {
method public android.os.IBinder getApplicationWindowToken();
method public int getAutoFillHint();
method public int getAutoFillMode();
- method public android.view.autofill.AutoFillType getAutoFillType();
+ method public final deprecated android.view.autofill.AutoFillType getAutoFillType();
method public android.view.autofill.AutoFillValue getAutoFillValue();
+ method public int getAutofillType();
method public android.graphics.drawable.Drawable getBackground();
method public android.content.res.ColorStateList getBackgroundTintList();
method public android.graphics.PorterDuff.Mode getBackgroundTintMode();
@@ -44877,6 +45100,7 @@ package android.view {
method protected int getHorizontalScrollbarHeight();
method public int getId();
method public int getImportantForAccessibility();
+ method public int getImportantForAutofill();
method public boolean getKeepScreenOn();
method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
method public int getLabelFor();
@@ -45006,6 +45230,7 @@ package android.view {
method public boolean isHorizontalScrollBarEnabled();
method public boolean isHovered();
method public boolean isImportantForAccessibility();
+ method public final boolean isImportantForAutofill();
method public boolean isInEditMode();
method public boolean isInLayout();
method public boolean isInTouchMode();
@@ -45188,6 +45413,7 @@ package android.view {
method public void setHovered(boolean);
method public void setId(int);
method public void setImportantForAccessibility(int);
+ method public void setImportantForAutofill(int);
method public void setKeepScreenOn(boolean);
method public void setKeyboardNavigationCluster(boolean);
method public void setLabelFor(int);
@@ -45291,6 +45517,11 @@ package android.view {
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+ field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4
+ field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3
+ field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0
+ field public static final int AUTOFILL_TYPE_TEXT = 1; // 0x1
+ field public static final int AUTOFILL_TYPE_TOGGLE = 2; // 0x2
field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
@@ -45348,6 +45579,9 @@ package android.view {
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
+ field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -45960,8 +46194,9 @@ package android.view {
method public abstract void setAlpha(float);
method public abstract void setAutoFillHint(int);
method public abstract void setAutoFillOptions(java.lang.String[]);
- method public abstract void setAutoFillType(android.view.autofill.AutoFillType);
+ method public abstract deprecated void setAutoFillType(android.view.autofill.AutoFillType);
method public abstract void setAutoFillValue(android.view.autofill.AutoFillValue);
+ method public abstract void setAutofillType(int);
method public abstract void setCheckable(boolean);
method public abstract void setChecked(boolean);
method public abstract void setChildCount(int);
@@ -47217,17 +47452,27 @@ package android.view.autofill {
}
public final class AutoFillManager {
+ method public void registerCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void reset();
method public void startAutoFillRequest(android.view.View);
method public void startAutoFillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
method public void stopAutoFillRequest(android.view.View);
method public void stopAutoFillRequestOnVirtualView(android.view.View, int);
+ method public void unregisterCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void valueChanged(android.view.View);
method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
}
+ public static abstract class AutoFillManager.AutofillCallback {
+ ctor public AutoFillManager.AutofillCallback();
+ method public void onAutofillEvent(android.view.View, int);
+ method public void onAutofillEventVirtual(android.view.View, int, int);
+ field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2
+ field public static final int EVENT_INPUT_SHOWN = 1; // 0x1
+ }
+
public final class AutoFillType implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutoFillType forDate();
@@ -47692,9 +47937,9 @@ package android.view.textclassifier {
}
public abstract interface TextClassifier {
- method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int);
- method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int);
- method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
+ method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int, android.os.LocaleList);
+ method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int, android.os.LocaleList);
+ method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
field public static final android.view.textclassifier.TextClassifier NO_OP;
field public static final java.lang.String TYPE_ADDRESS = "address";
field public static final java.lang.String TYPE_EMAIL = "email";
diff --git a/api/removed.txt b/api/removed.txt
index c5dbf8daf0a4..148f3f1e3281 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -369,6 +369,16 @@ package android.view {
}
+package android.view.textclassifier {
+
+ public abstract interface TextClassifier {
+ method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int);
+ method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int);
+ method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
+ }
+
+}
+
package android.webkit {
public class WebViewClient {
diff --git a/api/system-current.txt b/api/system-current.txt
index 9645e74a9a42..6ee593c5fbf6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -719,8 +719,9 @@ package android {
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int fontProviderAuthority = 16844114; // 0x1010552
+ field public static final int fontProviderPackage = 16844122; // 0x101055a
field public static final int fontProviderQuery = 16844115; // 0x1010553
- field public static final int fontStyle = 16844081; // 0x1010531
+ field public static final int fontStyle = 16844095; // 0x101053f
field public static final int fontWeight = 16844083; // 0x1010533
field public static final int footerDividersEnabled = 16843311; // 0x101022f
field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
@@ -821,6 +822,7 @@ package android {
field public static final int imeSubtypeMode = 16843501; // 0x10102ed
field public static final int immersive = 16843456; // 0x10102c0
field public static final int importantForAccessibility = 16843690; // 0x10103aa
+ field public static final int importantForAutofill = 16844123; // 0x101055b
field public static final int inAnimation = 16843127; // 0x1010177
field public static final int includeFontPadding = 16843103; // 0x101015f
field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -1575,7 +1577,7 @@ package android {
field public static final int viewportWidth = 16843778; // 0x1010402
field public static final int visibility = 16842972; // 0x10100dc
field public static final int visible = 16843156; // 0x1010194
- field public static final int visibleToInstantApps = 16844095; // 0x101053f
+ field public static final int visibleToInstantApps = 16844081; // 0x1010531
field public static final int vmSafeMode = 16843448; // 0x10102b8
field public static final int voiceIcon = 16843908; // 0x1010484
field public static final int voiceLanguage = 16843349; // 0x1010255
@@ -2930,6 +2932,7 @@ package android.accessibilityservice {
method public android.content.pm.ResolveInfo getResolveInfo();
method public java.lang.String getSettingsActivityName();
method public java.lang.String loadDescription(android.content.pm.PackageManager);
+ method public java.lang.String loadSummary(android.content.pm.PackageManager);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES = 64; // 0x40
field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
@@ -6809,8 +6812,9 @@ package android.app.assist {
method public int getAutoFillHint();
method public android.view.autofill.AutoFillId getAutoFillId();
method public java.lang.String[] getAutoFillOptions();
- method public android.view.autofill.AutoFillType getAutoFillType();
+ method public deprecated android.view.autofill.AutoFillType getAutoFillType();
method public android.view.autofill.AutoFillValue getAutoFillValue();
+ method public int getAutofillType();
method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
method public int getChildCount();
method public java.lang.String getClassName();
@@ -7584,6 +7588,7 @@ package android.bluetooth {
method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
method public java.lang.String getName();
+ method public android.bluetooth.le.PeriodicAdvertisingManager getPeriodicAdvertisingManager();
method public int getProfileConnectionState(int);
method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int);
method public android.bluetooth.BluetoothDevice getRemoteDevice(java.lang.String);
@@ -7593,7 +7598,11 @@ package android.bluetooth {
method public boolean isBleScanAlwaysAvailable();
method public boolean isDiscovering();
method public boolean isEnabled();
+ method public boolean isLe2MPhySupported();
+ method public boolean isLeCodedPhySupported();
method public boolean isLeEnabled();
+ method public boolean isLeExtendedAdvertisingSupported();
+ method public boolean isLePeriodicAdvertisingSupported();
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
@@ -7964,6 +7973,9 @@ package android.bluetooth {
public final class BluetoothDevice implements android.os.Parcelable {
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int, int);
method public boolean createBond();
method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
@@ -8009,6 +8021,13 @@ package android.bluetooth {
field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
field public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; // 0x2
field public static final int PAIRING_VARIANT_PIN = 0; // 0x0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_ANY = 7; // 0x7
+ field public static final int PHY_LE_CODED = 4; // 0x4
+ field public static final int PHY_OPTION_NO_PREFERRED = 0; // 0x0
+ field public static final int PHY_OPTION_S2 = 1; // 0x1
+ field public static final int PHY_OPTION_S8 = 2; // 0x2
field public static final int TRANSPORT_AUTO = 0; // 0x0
field public static final int TRANSPORT_BREDR = 1; // 0x1
field public static final int TRANSPORT_LE = 2; // 0x2
@@ -8031,10 +8050,12 @@ package android.bluetooth {
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method public void readPhy();
method public boolean readRemoteRssi();
method public boolean requestConnectionPriority(int);
method public boolean requestMtu(int);
method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void setPreferredPhy(int, int, int);
method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0
@@ -8052,8 +8073,12 @@ package android.bluetooth {
field public static final int GATT_WRITE_NOT_PERMITTED = 3; // 0x3
}
- public abstract class BluetoothGattCallback {
+ public abstract deprecated class BluetoothGattCallback extends android.bluetooth.BluetoothGattCallbackExt {
ctor public BluetoothGattCallback();
+ }
+
+ public abstract class BluetoothGattCallbackExt {
+ ctor public BluetoothGattCallbackExt();
method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
@@ -8061,6 +8086,8 @@ package android.bluetooth {
method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onMtuChanged(android.bluetooth.BluetoothGatt, int, int);
+ method public void onPhyRead(android.bluetooth.BluetoothGatt, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
@@ -8154,12 +8181,18 @@ package android.bluetooth {
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void readPhy(android.bluetooth.BluetoothDevice);
method public boolean removeService(android.bluetooth.BluetoothGattService);
method public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
+ method public void setPreferredPhy(android.bluetooth.BluetoothDevice, int, int, int);
}
- public abstract class BluetoothGattServerCallback {
+ public abstract deprecated class BluetoothGattServerCallback extends android.bluetooth.BluetoothGattServerCallbackExt {
ctor public BluetoothGattServerCallback();
+ }
+
+ public abstract class BluetoothGattServerCallbackExt {
+ ctor public BluetoothGattServerCallbackExt();
method public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattCharacteristic, boolean, boolean, int, byte[]);
method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int);
@@ -8168,6 +8201,8 @@ package android.bluetooth {
method public void onExecuteWrite(android.bluetooth.BluetoothDevice, int, boolean);
method public void onMtuChanged(android.bluetooth.BluetoothDevice, int);
method public void onNotificationSent(android.bluetooth.BluetoothDevice, int);
+ method public void onPhyRead(android.bluetooth.BluetoothDevice, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothDevice, int, int, int);
method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
}
@@ -8368,10 +8403,85 @@ package android.bluetooth.le {
method public android.bluetooth.le.AdvertiseSettings.Builder setTxPowerLevel(int);
}
+ public final class AdvertisingSet {
+ method public void enableAdvertising(boolean);
+ method public void periodicAdvertisingEnable(boolean);
+ method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
+ method public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters);
+ method public void setScanResponseData(android.bluetooth.le.AdvertiseData);
+ }
+
+ public abstract class AdvertisingSetCallback {
+ ctor public AdvertisingSetCallback();
+ method public void onAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingEnabled(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStopped(android.bluetooth.le.AdvertisingSet);
+ method public void onPeriodicAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onPeriodicAdvertisingEnable(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onPeriodicAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onScanResponseDataSet(android.bluetooth.le.AdvertisingSet, int);
+ field public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; // 0x3
+ field public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; // 0x1
+ field public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; // 0x5
+ field public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; // 0x4
+ field public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; // 0x2
+ field public static final int ADVERTISE_SUCCESS = 0; // 0x0
+ }
+
+ public final class AdvertisingSetParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getInterval();
+ method public int getPrimaryPhy();
+ method public int getSecondaryPhy();
+ method public int getTimeout();
+ method public int getTxPowerLevel();
+ method public boolean includeTxPower();
+ method public boolean isAnonymous();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertisingSetParameters> CREATOR;
+ field public static final int INTERVAL_HIGH = 160; // 0xa0
+ field public static final int INTERVAL_LOW = 1600; // 0x640
+ field public static final int INTERVAL_MAX = 16777215; // 0xffffff
+ field public static final int INTERVAL_MEDIUM = 400; // 0x190
+ field public static final int INTERVAL_MIN = 160; // 0xa0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int TX_POWER_HIGH = 1; // 0x1
+ field public static final int TX_POWER_LOW = -15; // 0xfffffff1
+ field public static final int TX_POWER_MAX = 1; // 0x1
+ field public static final int TX_POWER_MEDIUM = -7; // 0xfffffff9
+ field public static final int TX_POWER_MIN = -127; // 0xffffff81
+ field public static final int TX_POWER_ULTRA_LOW = -21; // 0xffffffeb
+ }
+
+ public static final class AdvertisingSetParameters.Builder {
+ ctor public AdvertisingSetParameters.Builder();
+ method public android.bluetooth.le.AdvertisingSetParameters build();
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setAnonymouus(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setConnectable(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setInterval(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
+ }
+
public final class BluetoothLeAdvertiser {
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
+ method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
}
public final class BluetoothLeScanner {
@@ -8384,6 +8494,53 @@ package android.bluetooth.le {
method public void stopScan(android.bluetooth.le.ScanCallback);
}
+ public abstract class PeriodicAdvertisingCallback {
+ ctor public PeriodicAdvertisingCallback();
+ method public void onPeriodicAdvertisingReport(android.bluetooth.le.PeriodicAdvertisingReport);
+ method public void onSyncEstablished(int, android.bluetooth.BluetoothDevice, int, int, int, int);
+ method public void onSyncLost(int);
+ field public static final int SYNC_NO_RESOURCES = 2; // 0x2
+ field public static final int SYNC_NO_RESPONSE = 1; // 0x1
+ }
+
+ public final class PeriodicAdvertisingManager {
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback);
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback, android.os.Handler);
+ method public void unregisterSync(android.bluetooth.le.PeriodicAdvertisingCallback);
+ }
+
+ public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getEnable();
+ method public boolean getIncludeTxPower();
+ method public int getInterval();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingParameters> CREATOR;
+ }
+
+ public static final class PeriodicAdvertisingParameters.Builder {
+ ctor public PeriodicAdvertisingParameters.Builder();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters build();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setEnable(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setInterval(int);
+ }
+
+ public final class PeriodicAdvertisingReport implements android.os.Parcelable {
+ ctor public PeriodicAdvertisingReport(int, int, int, int, android.bluetooth.le.ScanRecord);
+ method public int describeContents();
+ method public android.bluetooth.le.ScanRecord getData();
+ method public int getDataStatus();
+ method public int getRssi();
+ method public int getSyncHandle();
+ method public long getTimestampNanos();
+ method public int getTxPower();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingReport> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_INCOMPLETE_TRUNCATED = 2; // 0x2
+ }
+
public final class ResultStorageDescriptor implements android.os.Parcelable {
ctor public ResultStorageDescriptor(int, int, int);
method public int describeContents();
@@ -8448,19 +8605,37 @@ package android.bluetooth.le {
}
public final class ScanResult implements android.os.Parcelable {
- ctor public ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public deprecated ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public ScanResult(android.bluetooth.BluetoothDevice, int, int, int, int, int, int, int, android.bluetooth.le.ScanRecord, long);
method public int describeContents();
+ method public int getAdvertisingSid();
+ method public int getDataStatus();
method public android.bluetooth.BluetoothDevice getDevice();
+ method public int getPeriodicAdvertisingInterval();
+ method public int getPrimaryPhy();
method public int getRssi();
method public android.bluetooth.le.ScanRecord getScanRecord();
+ method public int getSecondaryPhy();
method public long getTimestampNanos();
+ method public int getTxPower();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.bluetooth.le.ScanResult> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_TRUNCATED = 2; // 0x2
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int PHY_UNUSED = 0; // 0x0
+ field public static final int SID_NOT_PRESENT = 255; // 0xff
}
public final class ScanSettings implements android.os.Parcelable {
method public int describeContents();
method public int getCallbackType();
+ method public boolean getLegacy();
+ method public int getPhy();
method public long getReportDelayMillis();
method public int getScanMode();
method public int getScanResultType();
@@ -8474,6 +8649,9 @@ package android.bluetooth.le {
field public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2; // 0x2
field public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3; // 0x3
field public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1; // 0x1
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_ALL_SUPPORTED = 255; // 0xff
+ field public static final int PHY_LE_CODED = 3; // 0x3
field public static final int SCAN_MODE_BALANCED = 1; // 0x1
field public static final int SCAN_MODE_LOW_LATENCY = 2; // 0x2
field public static final int SCAN_MODE_LOW_POWER = 0; // 0x0
@@ -8486,8 +8664,10 @@ package android.bluetooth.le {
ctor public ScanSettings.Builder();
method public android.bluetooth.le.ScanSettings build();
method public android.bluetooth.le.ScanSettings.Builder setCallbackType(int);
+ method public android.bluetooth.le.ScanSettings.Builder setLegacy(boolean);
method public android.bluetooth.le.ScanSettings.Builder setMatchMode(int);
method public android.bluetooth.le.ScanSettings.Builder setNumOfMatches(int);
+ method public android.bluetooth.le.ScanSettings.Builder setPhy(int);
method public android.bluetooth.le.ScanSettings.Builder setReportDelay(long);
method public android.bluetooth.le.ScanSettings.Builder setScanMode(int);
method public android.bluetooth.le.ScanSettings.Builder setScanResultType(int);
@@ -8546,6 +8726,8 @@ package android.companion {
public final class CompanionDeviceManager {
method public void associate(android.companion.AssociationRequest<?>, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
+ method public void disassociate(java.lang.String);
+ method public java.util.List<java.lang.String> getAssociations();
field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE";
}
@@ -9906,6 +10088,7 @@ package android.content {
method public final java.lang.String getDataScheme(int);
method public final android.os.PatternMatcher getDataSchemeSpecificPart(int);
method public final java.lang.String getDataType(int);
+ method public final int getOrder();
method public final int getPriority();
method public final boolean hasAction(java.lang.String);
method public final boolean hasCategory(java.lang.String);
@@ -9924,6 +10107,7 @@ package android.content {
method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public final java.util.Iterator<android.os.PatternMatcher> schemeSpecificPartsIterator();
method public final java.util.Iterator<java.lang.String> schemesIterator();
+ method public final void setOrder(int);
method public final void setPriority(int);
method public final java.util.Iterator<java.lang.String> typesIterator();
method public final void writeToParcel(android.os.Parcel, int);
@@ -10597,6 +10781,20 @@ package android.content.pm {
field public int version;
}
+ public final class InstantAppInfo implements android.os.Parcelable {
+ ctor public InstantAppInfo(android.content.pm.ApplicationInfo, java.lang.String[], java.lang.String[]);
+ ctor public InstantAppInfo(java.lang.String, java.lang.CharSequence, java.lang.String[], java.lang.String[]);
+ method public int describeContents();
+ method public android.content.pm.ApplicationInfo getApplicationInfo();
+ method public java.lang.String[] getGrantedPermissions();
+ method public java.lang.String getPackageName();
+ method public java.lang.String[] getRequestedPermissions();
+ method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
+ method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppInfo> CREATOR;
+ }
+
public final class InstantAppIntentFilter implements android.os.Parcelable {
ctor public InstantAppIntentFilter(java.lang.String, java.util.List<android.content.IntentFilter>);
method public int describeContents();
@@ -10952,6 +11150,8 @@ package android.content.pm {
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
method public abstract byte[] getInstantAppCookie();
method public abstract int getInstantAppCookieMaxSize();
+ method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
+ method public abstract java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
@@ -11196,6 +11396,7 @@ package android.content.pm {
field public static final int MATCH_DIRECT_BOOT_UNAWARE = 262144; // 0x40000
field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200
field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+ field public static final int MATCH_INSTANT = 8388608; // 0x800000
field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
@@ -11224,7 +11425,7 @@ package android.content.pm {
public static abstract class PackageManager.PermissionFlags implements java.lang.annotation.Annotation {
}
- public class PackageStats implements android.os.Parcelable {
+ public deprecated class PackageStats implements android.os.Parcelable {
ctor public PackageStats(java.lang.String);
ctor public PackageStats(android.os.Parcel);
ctor public PackageStats(android.content.pm.PackageStats);
@@ -12927,6 +13128,7 @@ package android.graphics {
method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]);
method public final int getAllocationByteCount();
method public final int getByteCount();
+ method public final android.graphics.ColorSpace getColorSpace();
method public final android.graphics.Bitmap.Config getConfig();
method public int getDensity();
method public int getGenerationId();
@@ -13309,6 +13511,7 @@ package android.graphics {
method public java.lang.String getName();
method public boolean isSrgb();
method public abstract boolean isWideGamut();
+ method public static android.graphics.ColorSpace match(float[], android.graphics.ColorSpace.Rgb.TransferParameters);
method public float[] toXyz(float, float, float);
method public abstract float[] toXyz(float[]);
field public static final float[] ILLUMINANT_A;
@@ -13393,6 +13596,10 @@ package android.graphics {
public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
ctor public ColorSpace.Rgb(java.lang.String, float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator);
ctor public ColorSpace.Rgb(java.lang.String, float[], float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator, float, float);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], android.graphics.ColorSpace.Rgb.TransferParameters);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], float[], android.graphics.ColorSpace.Rgb.TransferParameters);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], double);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], float[], double);
method public float[] fromLinear(float, float, float);
method public float[] fromLinear(float[]);
method public float[] fromXyz(float[]);
@@ -13404,6 +13611,7 @@ package android.graphics {
method public java.util.function.DoubleUnaryOperator getOetf();
method public float[] getPrimaries(float[]);
method public float[] getPrimaries();
+ method public android.graphics.ColorSpace.Rgb.TransferParameters getTransferParameters();
method public float[] getTransform(float[]);
method public float[] getTransform();
method public float[] getWhitePoint(float[]);
@@ -13414,6 +13622,18 @@ package android.graphics {
method public float[] toXyz(float[]);
}
+ public static class ColorSpace.Rgb.TransferParameters {
+ ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double);
+ ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double, double, double);
+ field public final double a;
+ field public final double b;
+ field public final double c;
+ field public final double d;
+ field public final double e;
+ field public final double f;
+ field public final double g;
+ }
+
public class ComposePathEffect extends android.graphics.PathEffect {
ctor public ComposePathEffect(android.graphics.PathEffect, android.graphics.PathEffect);
}
@@ -14805,9 +15025,12 @@ package android.graphics.drawable.shapes {
package android.graphics.fonts {
public final class FontRequest implements android.os.Parcelable {
- ctor public FontRequest(java.lang.String, java.lang.String);
+ ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String);
+ ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.util.List<byte[]>>);
method public int describeContents();
+ method public java.util.List<java.util.List<byte[]>> getCertificates();
method public java.lang.String getProviderAuthority();
+ method public java.lang.String getProviderPackage();
method public java.lang.String getQuery();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
@@ -15589,10 +15812,14 @@ package android.hardware.camera2 {
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSessionByConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
+ field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1
+ field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0
+ field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000
field public static final int TEMPLATE_MANUAL = 6; // 0x6
field public static final int TEMPLATE_PREVIEW = 1; // 0x1
field public static final int TEMPLATE_RECORD = 3; // 0x3
@@ -16266,6 +16493,7 @@ package android.hardware.hdmi {
method public android.hardware.hdmi.HdmiPlaybackClient getPlaybackClient();
method public android.hardware.hdmi.HdmiTvClient getTvClient();
method public void removeHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
+ method public void setStandbyMode(boolean);
field public static final java.lang.String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
field public static final int AVR_VOLUME_MUTED = 101; // 0x65
field public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 162; // 0xa2
@@ -21073,6 +21301,8 @@ package android.icu.util {
field public static final int SHORT_COMMONLY_USED = 6; // 0x6
field public static final int SHORT_GENERIC = 2; // 0x2
field public static final int SHORT_GMT = 4; // 0x4
+ field public static final int TIMEZONE_ICU = 0; // 0x0
+ field public static final int TIMEZONE_JDK = 1; // 0x1
field public static final android.icu.util.TimeZone UNKNOWN_ZONE;
field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown";
}
@@ -26722,7 +26952,12 @@ package android.metrics {
ctor public LogMaker(int);
ctor public LogMaker(java.lang.Object[]);
method public android.metrics.LogMaker addTaggedData(int, java.lang.Object);
+ method public android.metrics.LogMaker clearCategory();
+ method public android.metrics.LogMaker clearPackageName();
+ method public android.metrics.LogMaker clearSubtype();
method public android.metrics.LogMaker clearTaggedData(int);
+ method public android.metrics.LogMaker clearTimestamp();
+ method public android.metrics.LogMaker clearType();
method public void deserialize(java.lang.Object[]);
method public int getCategory();
method public long getCounterBucket();
@@ -27041,6 +27276,7 @@ package android.net {
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
method public deprecated void setNetworkPreference(int);
method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
@@ -27094,6 +27330,7 @@ package android.net {
method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
method public void onLosing(android.net.Network, int);
method public void onLost(android.net.Network);
+ method public void onUnavailable();
}
public static abstract interface ConnectivityManager.OnNetworkActiveListener {
@@ -32953,7 +33190,7 @@ package android.os {
ctor public Bundle(android.os.Bundle);
ctor public Bundle(android.os.PersistableBundle);
method public java.lang.Object clone();
- method public android.os.Bundle deepcopy();
+ method public android.os.Bundle deepCopy();
method public int describeContents();
method public android.os.IBinder getBinder(java.lang.String);
method public android.os.Bundle getBundle(java.lang.String);
@@ -33699,6 +33936,7 @@ package android.os {
method public boolean match(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.os.PatternMatcher> CREATOR;
+ field public static final int PATTERN_ADVANCED_GLOB = 3; // 0x3
field public static final int PATTERN_LITERAL = 0; // 0x0
field public static final int PATTERN_PREFIX = 1; // 0x1
field public static final int PATTERN_SIMPLE_GLOB = 2; // 0x2
@@ -33709,7 +33947,7 @@ package android.os {
ctor public PersistableBundle(int);
ctor public PersistableBundle(android.os.PersistableBundle);
method public java.lang.Object clone();
- method public android.os.PersistableBundle deepcopy();
+ method public android.os.PersistableBundle deepCopy();
method public int describeContents();
method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
method public void putPersistableBundle(java.lang.String, android.os.PersistableBundle);
@@ -33845,6 +34083,7 @@ package android.os {
method public E getBroadcastItem(int);
method public java.lang.Object getRegisteredCallbackCookie(int);
method public int getRegisteredCallbackCount();
+ method public E getRegisteredCallbackItem(int);
method public void kill();
method public void onCallbackDied(E);
method public void onCallbackDied(E, java.lang.Object);
@@ -41330,6 +41569,7 @@ package android.telecom {
method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
method public void onParentChanged(android.telecom.Call, android.telecom.Call);
method public void onPostDialWait(android.telecom.Call, java.lang.String);
+ method public void onRttInitiationFailure(android.telecom.Call, int);
method public void onRttModeChanged(android.telecom.Call, int);
method public void onRttRequest(android.telecom.Call, int);
method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall);
@@ -41607,6 +41847,15 @@ package android.telecom {
field public static final int STATE_RINGING = 2; // 0x2
}
+ public static final class Connection.RttModifyStatus {
+ ctor public Connection.RttModifyStatus();
+ field public static final int SESSION_MODIFY_REQUEST_FAIL = 2; // 0x2
+ field public static final int SESSION_MODIFY_REQUEST_INVALID = 3; // 0x3
+ field public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5; // 0x5
+ field public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1; // 0x1
+ field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4
+ }
+
public static abstract class Connection.VideoProvider {
ctor public Connection.VideoProvider();
method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
@@ -41664,9 +41913,9 @@ package android.telecom {
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -43029,6 +43278,7 @@ package android.telephony {
field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
+ field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_STATE = "state";
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
@@ -43781,6 +44031,8 @@ package android.test.mock {
method public java.lang.String getInstallerPackageName(java.lang.String);
method public byte[] getInstantAppCookie();
method public int getInstantAppCookieMaxSize();
+ method public android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
+ method public java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
method public int getIntentVerificationStatusAsUser(java.lang.String, int);
@@ -46797,6 +47049,7 @@ package android.view {
field public static final int STATE_OFF = 1; // 0x1
field public static final int STATE_ON = 2; // 0x2
field public static final int STATE_UNKNOWN = 0; // 0x0
+ field public static final int STATE_VR = 5; // 0x5
}
public static final class Display.HdrCapabilities implements android.os.Parcelable {
@@ -48262,8 +48515,9 @@ package android.view {
method public android.os.IBinder getApplicationWindowToken();
method public int getAutoFillHint();
method public int getAutoFillMode();
- method public android.view.autofill.AutoFillType getAutoFillType();
+ method public final deprecated android.view.autofill.AutoFillType getAutoFillType();
method public android.view.autofill.AutoFillValue getAutoFillValue();
+ method public int getAutofillType();
method public android.graphics.drawable.Drawable getBackground();
method public android.content.res.ColorStateList getBackgroundTintList();
method public android.graphics.PorterDuff.Mode getBackgroundTintMode();
@@ -48307,6 +48561,7 @@ package android.view {
method protected int getHorizontalScrollbarHeight();
method public int getId();
method public int getImportantForAccessibility();
+ method public int getImportantForAutofill();
method public boolean getKeepScreenOn();
method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
method public int getLabelFor();
@@ -48436,6 +48691,7 @@ package android.view {
method public boolean isHorizontalScrollBarEnabled();
method public boolean isHovered();
method public boolean isImportantForAccessibility();
+ method public final boolean isImportantForAutofill();
method public boolean isInEditMode();
method public boolean isInLayout();
method public boolean isInTouchMode();
@@ -48618,6 +48874,7 @@ package android.view {
method public void setHovered(boolean);
method public void setId(int);
method public void setImportantForAccessibility(int);
+ method public void setImportantForAutofill(int);
method public void setKeepScreenOn(boolean);
method public void setKeyboardNavigationCluster(boolean);
method public void setLabelFor(int);
@@ -48721,6 +48978,11 @@ package android.view {
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+ field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4
+ field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3
+ field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0
+ field public static final int AUTOFILL_TYPE_TEXT = 1; // 0x1
+ field public static final int AUTOFILL_TYPE_TOGGLE = 2; // 0x2
field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
@@ -48778,6 +49040,9 @@ package android.view {
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
+ field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -49390,8 +49655,9 @@ package android.view {
method public abstract void setAlpha(float);
method public abstract void setAutoFillHint(int);
method public abstract void setAutoFillOptions(java.lang.String[]);
- method public abstract void setAutoFillType(android.view.autofill.AutoFillType);
+ method public abstract deprecated void setAutoFillType(android.view.autofill.AutoFillType);
method public abstract void setAutoFillValue(android.view.autofill.AutoFillValue);
+ method public abstract void setAutofillType(int);
method public abstract void setCheckable(boolean);
method public abstract void setChecked(boolean);
method public abstract void setChildCount(int);
@@ -50650,17 +50916,27 @@ package android.view.autofill {
}
public final class AutoFillManager {
+ method public void registerCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void reset();
method public void startAutoFillRequest(android.view.View);
method public void startAutoFillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
method public void stopAutoFillRequest(android.view.View);
method public void stopAutoFillRequestOnVirtualView(android.view.View, int);
+ method public void unregisterCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void valueChanged(android.view.View);
method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
}
+ public static abstract class AutoFillManager.AutofillCallback {
+ ctor public AutoFillManager.AutofillCallback();
+ method public void onAutofillEvent(android.view.View, int);
+ method public void onAutofillEventVirtual(android.view.View, int, int);
+ field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2
+ field public static final int EVENT_INPUT_SHOWN = 1; // 0x1
+ }
+
public final class AutoFillType implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutoFillType forDate();
@@ -51125,9 +51401,9 @@ package android.view.textclassifier {
}
public abstract interface TextClassifier {
- method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int);
- method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int);
- method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
+ method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int, android.os.LocaleList);
+ method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int, android.os.LocaleList);
+ method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
field public static final android.view.textclassifier.TextClassifier NO_OP;
field public static final java.lang.String TYPE_ADDRESS = "address";
field public static final java.lang.String TYPE_EMAIL = "email";
diff --git a/api/system-removed.txt b/api/system-removed.txt
index a3093e294d15..bd535d2be513 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -363,6 +363,16 @@ package android.view {
}
+package android.view.textclassifier {
+
+ public abstract interface TextClassifier {
+ method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int);
+ method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int);
+ method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
+ }
+
+}
+
package android.webkit {
public class WebViewClient {
diff --git a/api/test-current.txt b/api/test-current.txt
index 8a672abcb62b..fc96532ba383 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -607,8 +607,9 @@ package android {
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int fontProviderAuthority = 16844114; // 0x1010552
+ field public static final int fontProviderPackage = 16844122; // 0x101055a
field public static final int fontProviderQuery = 16844115; // 0x1010553
- field public static final int fontStyle = 16844081; // 0x1010531
+ field public static final int fontStyle = 16844095; // 0x101053f
field public static final int fontWeight = 16844083; // 0x1010533
field public static final int footerDividersEnabled = 16843311; // 0x101022f
field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
@@ -709,6 +710,7 @@ package android {
field public static final int imeSubtypeMode = 16843501; // 0x10102ed
field public static final int immersive = 16843456; // 0x10102c0
field public static final int importantForAccessibility = 16843690; // 0x10103aa
+ field public static final int importantForAutofill = 16844123; // 0x101055b
field public static final int inAnimation = 16843127; // 0x1010177
field public static final int includeFontPadding = 16843103; // 0x101015f
field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -1459,7 +1461,7 @@ package android {
field public static final int viewportWidth = 16843778; // 0x1010402
field public static final int visibility = 16842972; // 0x10100dc
field public static final int visible = 16843156; // 0x1010194
- field public static final int visibleToInstantApps = 16844095; // 0x101053f
+ field public static final int visibleToInstantApps = 16844081; // 0x1010531
field public static final int vmSafeMode = 16843448; // 0x10102b8
field public static final int voiceIcon = 16843908; // 0x1010484
field public static final int voiceLanguage = 16843349; // 0x1010255
@@ -2811,6 +2813,7 @@ package android.accessibilityservice {
method public android.content.pm.ResolveInfo getResolveInfo();
method public java.lang.String getSettingsActivityName();
method public java.lang.String loadDescription(android.content.pm.PackageManager);
+ method public java.lang.String loadSummary(android.content.pm.PackageManager);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES = 64; // 0x40
field public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 16; // 0x10
@@ -6587,8 +6590,9 @@ package android.app.assist {
method public int getAutoFillHint();
method public android.view.autofill.AutoFillId getAutoFillId();
method public java.lang.String[] getAutoFillOptions();
- method public android.view.autofill.AutoFillType getAutoFillType();
+ method public deprecated android.view.autofill.AutoFillType getAutoFillType();
method public android.view.autofill.AutoFillValue getAutoFillValue();
+ method public int getAutofillType();
method public android.app.assist.AssistStructure.ViewNode getChildAt(int);
method public int getChildCount();
method public java.lang.String getClassName();
@@ -7139,6 +7143,7 @@ package android.bluetooth {
method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
method public java.lang.String getName();
+ method public android.bluetooth.le.PeriodicAdvertisingManager getPeriodicAdvertisingManager();
method public int getProfileConnectionState(int);
method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int);
method public android.bluetooth.BluetoothDevice getRemoteDevice(java.lang.String);
@@ -7147,6 +7152,10 @@ package android.bluetooth {
method public int getState();
method public boolean isDiscovering();
method public boolean isEnabled();
+ method public boolean isLe2MPhySupported();
+ method public boolean isLeCodedPhySupported();
+ method public boolean isLeExtendedAdvertisingSupported();
+ method public boolean isLePeriodicAdvertisingSupported();
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
@@ -7515,6 +7524,9 @@ package android.bluetooth {
public final class BluetoothDevice implements android.os.Parcelable {
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int, int);
method public boolean createBond();
method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
@@ -7558,6 +7570,13 @@ package android.bluetooth {
field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
field public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; // 0x2
field public static final int PAIRING_VARIANT_PIN = 0; // 0x0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_ANY = 7; // 0x7
+ field public static final int PHY_LE_CODED = 4; // 0x4
+ field public static final int PHY_OPTION_NO_PREFERRED = 0; // 0x0
+ field public static final int PHY_OPTION_S2 = 1; // 0x1
+ field public static final int PHY_OPTION_S8 = 2; // 0x2
field public static final int TRANSPORT_AUTO = 0; // 0x0
field public static final int TRANSPORT_BREDR = 1; // 0x1
field public static final int TRANSPORT_LE = 2; // 0x2
@@ -7580,10 +7599,12 @@ package android.bluetooth {
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method public void readPhy();
method public boolean readRemoteRssi();
method public boolean requestConnectionPriority(int);
method public boolean requestMtu(int);
method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void setPreferredPhy(int, int, int);
method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0
@@ -7601,8 +7622,12 @@ package android.bluetooth {
field public static final int GATT_WRITE_NOT_PERMITTED = 3; // 0x3
}
- public abstract class BluetoothGattCallback {
+ public abstract deprecated class BluetoothGattCallback extends android.bluetooth.BluetoothGattCallbackExt {
ctor public BluetoothGattCallback();
+ }
+
+ public abstract class BluetoothGattCallbackExt {
+ ctor public BluetoothGattCallbackExt();
method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
@@ -7610,6 +7635,8 @@ package android.bluetooth {
method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onMtuChanged(android.bluetooth.BluetoothGatt, int, int);
+ method public void onPhyRead(android.bluetooth.BluetoothGatt, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
@@ -7703,12 +7730,18 @@ package android.bluetooth {
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void readPhy(android.bluetooth.BluetoothDevice);
method public boolean removeService(android.bluetooth.BluetoothGattService);
method public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
+ method public void setPreferredPhy(android.bluetooth.BluetoothDevice, int, int, int);
}
- public abstract class BluetoothGattServerCallback {
+ public abstract deprecated class BluetoothGattServerCallback extends android.bluetooth.BluetoothGattServerCallbackExt {
ctor public BluetoothGattServerCallback();
+ }
+
+ public abstract class BluetoothGattServerCallbackExt {
+ ctor public BluetoothGattServerCallbackExt();
method public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattCharacteristic, boolean, boolean, int, byte[]);
method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int);
@@ -7717,6 +7750,8 @@ package android.bluetooth {
method public void onExecuteWrite(android.bluetooth.BluetoothDevice, int, boolean);
method public void onMtuChanged(android.bluetooth.BluetoothDevice, int);
method public void onNotificationSent(android.bluetooth.BluetoothDevice, int);
+ method public void onPhyRead(android.bluetooth.BluetoothDevice, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothDevice, int, int, int);
method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
}
@@ -7917,10 +7952,85 @@ package android.bluetooth.le {
method public android.bluetooth.le.AdvertiseSettings.Builder setTxPowerLevel(int);
}
+ public final class AdvertisingSet {
+ method public void enableAdvertising(boolean);
+ method public void periodicAdvertisingEnable(boolean);
+ method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
+ method public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters);
+ method public void setScanResponseData(android.bluetooth.le.AdvertiseData);
+ }
+
+ public abstract class AdvertisingSetCallback {
+ ctor public AdvertisingSetCallback();
+ method public void onAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingEnabled(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStopped(android.bluetooth.le.AdvertisingSet);
+ method public void onPeriodicAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onPeriodicAdvertisingEnable(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onPeriodicAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onScanResponseDataSet(android.bluetooth.le.AdvertisingSet, int);
+ field public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; // 0x3
+ field public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; // 0x1
+ field public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; // 0x5
+ field public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; // 0x4
+ field public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; // 0x2
+ field public static final int ADVERTISE_SUCCESS = 0; // 0x0
+ }
+
+ public final class AdvertisingSetParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getInterval();
+ method public int getPrimaryPhy();
+ method public int getSecondaryPhy();
+ method public int getTimeout();
+ method public int getTxPowerLevel();
+ method public boolean includeTxPower();
+ method public boolean isAnonymous();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertisingSetParameters> CREATOR;
+ field public static final int INTERVAL_HIGH = 160; // 0xa0
+ field public static final int INTERVAL_LOW = 1600; // 0x640
+ field public static final int INTERVAL_MAX = 16777215; // 0xffffff
+ field public static final int INTERVAL_MEDIUM = 400; // 0x190
+ field public static final int INTERVAL_MIN = 160; // 0xa0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int TX_POWER_HIGH = 1; // 0x1
+ field public static final int TX_POWER_LOW = -15; // 0xfffffff1
+ field public static final int TX_POWER_MAX = 1; // 0x1
+ field public static final int TX_POWER_MEDIUM = -7; // 0xfffffff9
+ field public static final int TX_POWER_MIN = -127; // 0xffffff81
+ field public static final int TX_POWER_ULTRA_LOW = -21; // 0xffffffeb
+ }
+
+ public static final class AdvertisingSetParameters.Builder {
+ ctor public AdvertisingSetParameters.Builder();
+ method public android.bluetooth.le.AdvertisingSetParameters build();
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setAnonymouus(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setConnectable(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setInterval(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
+ }
+
public final class BluetoothLeAdvertiser {
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
+ method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
}
public final class BluetoothLeScanner {
@@ -7930,6 +8040,53 @@ package android.bluetooth.le {
method public void stopScan(android.bluetooth.le.ScanCallback);
}
+ public abstract class PeriodicAdvertisingCallback {
+ ctor public PeriodicAdvertisingCallback();
+ method public void onPeriodicAdvertisingReport(android.bluetooth.le.PeriodicAdvertisingReport);
+ method public void onSyncEstablished(int, android.bluetooth.BluetoothDevice, int, int, int, int);
+ method public void onSyncLost(int);
+ field public static final int SYNC_NO_RESOURCES = 2; // 0x2
+ field public static final int SYNC_NO_RESPONSE = 1; // 0x1
+ }
+
+ public final class PeriodicAdvertisingManager {
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback);
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback, android.os.Handler);
+ method public void unregisterSync(android.bluetooth.le.PeriodicAdvertisingCallback);
+ }
+
+ public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getEnable();
+ method public boolean getIncludeTxPower();
+ method public int getInterval();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingParameters> CREATOR;
+ }
+
+ public static final class PeriodicAdvertisingParameters.Builder {
+ ctor public PeriodicAdvertisingParameters.Builder();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters build();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setEnable(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setInterval(int);
+ }
+
+ public final class PeriodicAdvertisingReport implements android.os.Parcelable {
+ ctor public PeriodicAdvertisingReport(int, int, int, int, android.bluetooth.le.ScanRecord);
+ method public int describeContents();
+ method public android.bluetooth.le.ScanRecord getData();
+ method public int getDataStatus();
+ method public int getRssi();
+ method public int getSyncHandle();
+ method public long getTimestampNanos();
+ method public int getTxPower();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingReport> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_INCOMPLETE_TRUNCATED = 2; // 0x2
+ }
+
public abstract class ScanCallback {
ctor public ScanCallback();
method public void onBatchScanResults(java.util.List<android.bluetooth.le.ScanResult>);
@@ -7984,19 +8141,37 @@ package android.bluetooth.le {
}
public final class ScanResult implements android.os.Parcelable {
- ctor public ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public deprecated ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public ScanResult(android.bluetooth.BluetoothDevice, int, int, int, int, int, int, int, android.bluetooth.le.ScanRecord, long);
method public int describeContents();
+ method public int getAdvertisingSid();
+ method public int getDataStatus();
method public android.bluetooth.BluetoothDevice getDevice();
+ method public int getPeriodicAdvertisingInterval();
+ method public int getPrimaryPhy();
method public int getRssi();
method public android.bluetooth.le.ScanRecord getScanRecord();
+ method public int getSecondaryPhy();
method public long getTimestampNanos();
+ method public int getTxPower();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.bluetooth.le.ScanResult> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_TRUNCATED = 2; // 0x2
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int PHY_UNUSED = 0; // 0x0
+ field public static final int SID_NOT_PRESENT = 255; // 0xff
}
public final class ScanSettings implements android.os.Parcelable {
method public int describeContents();
method public int getCallbackType();
+ method public boolean getLegacy();
+ method public int getPhy();
method public long getReportDelayMillis();
method public int getScanMode();
method public int getScanResultType();
@@ -8010,6 +8185,9 @@ package android.bluetooth.le {
field public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2; // 0x2
field public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3; // 0x3
field public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1; // 0x1
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_ALL_SUPPORTED = 255; // 0xff
+ field public static final int PHY_LE_CODED = 3; // 0x3
field public static final int SCAN_MODE_BALANCED = 1; // 0x1
field public static final int SCAN_MODE_LOW_LATENCY = 2; // 0x2
field public static final int SCAN_MODE_LOW_POWER = 0; // 0x0
@@ -8020,8 +8198,10 @@ package android.bluetooth.le {
ctor public ScanSettings.Builder();
method public android.bluetooth.le.ScanSettings build();
method public android.bluetooth.le.ScanSettings.Builder setCallbackType(int);
+ method public android.bluetooth.le.ScanSettings.Builder setLegacy(boolean);
method public android.bluetooth.le.ScanSettings.Builder setMatchMode(int);
method public android.bluetooth.le.ScanSettings.Builder setNumOfMatches(int);
+ method public android.bluetooth.le.ScanSettings.Builder setPhy(int);
method public android.bluetooth.le.ScanSettings.Builder setReportDelay(long);
method public android.bluetooth.le.ScanSettings.Builder setScanMode(int);
}
@@ -8073,6 +8253,8 @@ package android.companion {
public final class CompanionDeviceManager {
method public void associate(android.companion.AssociationRequest<?>, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
+ method public void disassociate(java.lang.String);
+ method public java.util.List<java.lang.String> getAssociations();
field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE";
}
@@ -10559,7 +10741,7 @@ package android.content.pm {
ctor public PackageManager.NameNotFoundException(java.lang.String);
}
- public class PackageStats implements android.os.Parcelable {
+ public deprecated class PackageStats implements android.os.Parcelable {
ctor public PackageStats(java.lang.String);
ctor public PackageStats(android.os.Parcel);
ctor public PackageStats(android.content.pm.PackageStats);
@@ -12248,6 +12430,7 @@ package android.graphics {
method public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]);
method public final int getAllocationByteCount();
method public final int getByteCount();
+ method public final android.graphics.ColorSpace getColorSpace();
method public final android.graphics.Bitmap.Config getConfig();
method public int getDensity();
method public int getGenerationId();
@@ -12630,6 +12813,7 @@ package android.graphics {
method public java.lang.String getName();
method public boolean isSrgb();
method public abstract boolean isWideGamut();
+ method public static android.graphics.ColorSpace match(float[], android.graphics.ColorSpace.Rgb.TransferParameters);
method public float[] toXyz(float, float, float);
method public abstract float[] toXyz(float[]);
field public static final float[] ILLUMINANT_A;
@@ -12714,6 +12898,10 @@ package android.graphics {
public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
ctor public ColorSpace.Rgb(java.lang.String, float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator);
ctor public ColorSpace.Rgb(java.lang.String, float[], float[], java.util.function.DoubleUnaryOperator, java.util.function.DoubleUnaryOperator, float, float);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], android.graphics.ColorSpace.Rgb.TransferParameters);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], float[], android.graphics.ColorSpace.Rgb.TransferParameters);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], double);
+ ctor public ColorSpace.Rgb(java.lang.String, float[], float[], double);
method public float[] fromLinear(float, float, float);
method public float[] fromLinear(float[]);
method public float[] fromXyz(float[]);
@@ -12725,6 +12913,7 @@ package android.graphics {
method public java.util.function.DoubleUnaryOperator getOetf();
method public float[] getPrimaries(float[]);
method public float[] getPrimaries();
+ method public android.graphics.ColorSpace.Rgb.TransferParameters getTransferParameters();
method public float[] getTransform(float[]);
method public float[] getTransform();
method public float[] getWhitePoint(float[]);
@@ -12735,6 +12924,18 @@ package android.graphics {
method public float[] toXyz(float[]);
}
+ public static class ColorSpace.Rgb.TransferParameters {
+ ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double);
+ ctor public ColorSpace.Rgb.TransferParameters(double, double, double, double, double, double, double);
+ field public final double a;
+ field public final double b;
+ field public final double c;
+ field public final double d;
+ field public final double e;
+ field public final double f;
+ field public final double g;
+ }
+
public class ComposePathEffect extends android.graphics.PathEffect {
ctor public ComposePathEffect(android.graphics.PathEffect, android.graphics.PathEffect);
}
@@ -14127,9 +14328,12 @@ package android.graphics.drawable.shapes {
package android.graphics.fonts {
public final class FontRequest implements android.os.Parcelable {
- ctor public FontRequest(java.lang.String, java.lang.String);
+ ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String);
+ ctor public FontRequest(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.util.List<byte[]>>);
method public int describeContents();
+ method public java.util.List<java.util.List<byte[]>> getCertificates();
method public java.lang.String getProviderAuthority();
+ method public java.lang.String getProviderPackage();
method public java.lang.String getQuery();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.graphics.fonts.FontRequest> CREATOR;
@@ -14903,10 +15107,14 @@ package android.hardware.camera2 {
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSessionByConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
+ field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1
+ field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0
+ field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000
field public static final int TEMPLATE_MANUAL = 6; // 0x6
field public static final int TEMPLATE_PREVIEW = 1; // 0x1
field public static final int TEMPLATE_RECORD = 3; // 0x3
@@ -19673,6 +19881,8 @@ package android.icu.util {
field public static final int SHORT_COMMONLY_USED = 6; // 0x6
field public static final int SHORT_GENERIC = 2; // 0x2
field public static final int SHORT_GMT = 4; // 0x4
+ field public static final int TIMEZONE_ICU = 0; // 0x0
+ field public static final int TIMEZONE_JDK = 1; // 0x1
field public static final android.icu.util.TimeZone UNKNOWN_ZONE;
field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown";
}
@@ -25045,6 +25255,7 @@ package android.net {
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
method public deprecated void setNetworkPreference(int);
method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
@@ -25092,6 +25303,7 @@ package android.net {
method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
method public void onLosing(android.net.Network, int);
method public void onLost(android.net.Network);
+ method public void onUnavailable();
}
public static abstract interface ConnectivityManager.OnNetworkActiveListener {
@@ -30366,7 +30578,7 @@ package android.os {
ctor public Bundle(android.os.Bundle);
ctor public Bundle(android.os.PersistableBundle);
method public java.lang.Object clone();
- method public android.os.Bundle deepcopy();
+ method public android.os.Bundle deepCopy();
method public int describeContents();
method public android.os.IBinder getBinder(java.lang.String);
method public android.os.Bundle getBundle(java.lang.String);
@@ -31112,6 +31324,7 @@ package android.os {
method public boolean match(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.os.PatternMatcher> CREATOR;
+ field public static final int PATTERN_ADVANCED_GLOB = 3; // 0x3
field public static final int PATTERN_LITERAL = 0; // 0x0
field public static final int PATTERN_PREFIX = 1; // 0x1
field public static final int PATTERN_SIMPLE_GLOB = 2; // 0x2
@@ -31122,7 +31335,7 @@ package android.os {
ctor public PersistableBundle(int);
ctor public PersistableBundle(android.os.PersistableBundle);
method public java.lang.Object clone();
- method public android.os.PersistableBundle deepcopy();
+ method public android.os.PersistableBundle deepCopy();
method public int describeContents();
method public android.os.PersistableBundle getPersistableBundle(java.lang.String);
method public void putPersistableBundle(java.lang.String, android.os.PersistableBundle);
@@ -31231,6 +31444,7 @@ package android.os {
method public E getBroadcastItem(int);
method public java.lang.Object getRegisteredCallbackCookie(int);
method public int getRegisteredCallbackCount();
+ method public E getRegisteredCallbackItem(int);
method public void kill();
method public void onCallbackDied(E);
method public void onCallbackDied(E, java.lang.Object);
@@ -38383,6 +38597,7 @@ package android.telecom {
method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
method public void onParentChanged(android.telecom.Call, android.telecom.Call);
method public void onPostDialWait(android.telecom.Call, java.lang.String);
+ method public void onRttInitiationFailure(android.telecom.Call, int);
method public void onRttModeChanged(android.telecom.Call, int);
method public void onRttRequest(android.telecom.Call, int);
method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall);
@@ -38649,6 +38864,15 @@ package android.telecom {
field public static final int STATE_RINGING = 2; // 0x2
}
+ public static final class Connection.RttModifyStatus {
+ ctor public Connection.RttModifyStatus();
+ field public static final int SESSION_MODIFY_REQUEST_FAIL = 2; // 0x2
+ field public static final int SESSION_MODIFY_REQUEST_INVALID = 3; // 0x3
+ field public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5; // 0x5
+ field public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1; // 0x1
+ field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4
+ }
+
public static abstract class Connection.VideoProvider {
ctor public Connection.VideoProvider();
method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
@@ -38706,9 +38930,9 @@ package android.telecom {
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -39813,6 +40037,7 @@ package android.telephony {
field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
+ field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_STATE = "state";
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
@@ -43721,6 +43946,7 @@ package android.view {
field public static final int STATE_OFF = 1; // 0x1
field public static final int STATE_ON = 2; // 0x2
field public static final int STATE_UNKNOWN = 0; // 0x0
+ field public static final int STATE_VR = 5; // 0x5
}
public static final class Display.HdrCapabilities implements android.os.Parcelable {
@@ -45188,8 +45414,9 @@ package android.view {
method public android.os.IBinder getApplicationWindowToken();
method public int getAutoFillHint();
method public int getAutoFillMode();
- method public android.view.autofill.AutoFillType getAutoFillType();
+ method public final deprecated android.view.autofill.AutoFillType getAutoFillType();
method public android.view.autofill.AutoFillValue getAutoFillValue();
+ method public int getAutofillType();
method public android.graphics.drawable.Drawable getBackground();
method public android.content.res.ColorStateList getBackgroundTintList();
method public android.graphics.PorterDuff.Mode getBackgroundTintMode();
@@ -45233,6 +45460,7 @@ package android.view {
method protected int getHorizontalScrollbarHeight();
method public int getId();
method public int getImportantForAccessibility();
+ method public int getImportantForAutofill();
method public boolean getKeepScreenOn();
method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
method public int getLabelFor();
@@ -45363,6 +45591,7 @@ package android.view {
method public boolean isHorizontalScrollBarEnabled();
method public boolean isHovered();
method public boolean isImportantForAccessibility();
+ method public final boolean isImportantForAutofill();
method public boolean isInEditMode();
method public boolean isInLayout();
method public boolean isInTouchMode();
@@ -45547,6 +45776,7 @@ package android.view {
method public void setHovered(boolean);
method public void setId(int);
method public void setImportantForAccessibility(int);
+ method public void setImportantForAutofill(int);
method public void setKeepScreenOn(boolean);
method public void setKeyboardNavigationCluster(boolean);
method public void setLabelFor(int);
@@ -45650,6 +45880,11 @@ package android.view {
field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
+ field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4
+ field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3
+ field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0
+ field public static final int AUTOFILL_TYPE_TEXT = 1; // 0x1
+ field public static final int AUTOFILL_TYPE_TOGGLE = 2; // 0x2
field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
field public static final int AUTO_FILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
@@ -45707,6 +45942,9 @@ package android.view {
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 2; // 0x2
field public static final int IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS = 4; // 0x4
field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
+ field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -46323,8 +46561,9 @@ package android.view {
method public abstract void setAlpha(float);
method public abstract void setAutoFillHint(int);
method public abstract void setAutoFillOptions(java.lang.String[]);
- method public abstract void setAutoFillType(android.view.autofill.AutoFillType);
+ method public abstract deprecated void setAutoFillType(android.view.autofill.AutoFillType);
method public abstract void setAutoFillValue(android.view.autofill.AutoFillValue);
+ method public abstract void setAutofillType(int);
method public abstract void setCheckable(boolean);
method public abstract void setChecked(boolean);
method public abstract void setChildCount(int);
@@ -47582,17 +47821,27 @@ package android.view.autofill {
}
public final class AutoFillManager {
+ method public void registerCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void reset();
method public void startAutoFillRequest(android.view.View);
method public void startAutoFillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
method public void stopAutoFillRequest(android.view.View);
method public void stopAutoFillRequestOnVirtualView(android.view.View, int);
+ method public void unregisterCallback(android.view.autofill.AutoFillManager.AutofillCallback);
method public void valueChanged(android.view.View);
method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutoFillValue);
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
}
+ public static abstract class AutoFillManager.AutofillCallback {
+ ctor public AutoFillManager.AutofillCallback();
+ method public void onAutofillEvent(android.view.View, int);
+ method public void onAutofillEventVirtual(android.view.View, int, int);
+ field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2
+ field public static final int EVENT_INPUT_SHOWN = 1; // 0x1
+ }
+
public final class AutoFillType implements android.os.Parcelable {
method public int describeContents();
method public static android.view.autofill.AutoFillType forDate();
@@ -48057,9 +48306,9 @@ package android.view.textclassifier {
}
public abstract interface TextClassifier {
- method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int);
- method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int);
- method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
+ method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int, android.os.LocaleList);
+ method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int, android.os.LocaleList);
+ method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
field public static final android.view.textclassifier.TextClassifier NO_OP;
field public static final java.lang.String TYPE_ADDRESS = "address";
field public static final java.lang.String TYPE_EMAIL = "email";
diff --git a/api/test-removed.txt b/api/test-removed.txt
index c5dbf8daf0a4..148f3f1e3281 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -369,6 +369,16 @@ package android.view {
}
+package android.view.textclassifier {
+
+ public abstract interface TextClassifier {
+ method public abstract android.view.textclassifier.LinksInfo getLinks(java.lang.CharSequence, int);
+ method public abstract android.view.textclassifier.TextClassificationResult getTextClassificationResult(java.lang.CharSequence, int, int);
+ method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
+ }
+
+}
+
package android.webkit {
public class WebViewClient {
diff --git a/cmds/idmap/Android.mk b/cmds/idmap/Android.mk
index eb6da18ea0ad..50ccb07a3826 100644
--- a/cmds/idmap/Android.mk
+++ b/cmds/idmap/Android.mk
@@ -15,7 +15,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := idmap.cpp create.cpp inspect.cpp
+LOCAL_SRC_FILES := idmap.cpp create.cpp scan.cpp inspect.cpp
LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw
diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp
index d388977e8e2f..3ab191553625 100644
--- a/cmds/idmap/idmap.cpp
+++ b/cmds/idmap/idmap.cpp
@@ -13,6 +13,8 @@ SYNOPSIS \n\
idmap --help \n\
idmap --fd target overlay fd \n\
idmap --path target overlay idmap \n\
+ idmap --scan target-package-name-to-look-for path-to-target-apk dir-to-hold-idmaps \\\
+ dir-to-scan [additional-dir-to-scan [additional-dir-to-scan [...]]]\n\
idmap --inspect idmap \n\
\n\
DESCRIPTION \n\
@@ -47,6 +49,11 @@ OPTIONS \n\
--path: create idmap for target package 'target' (path to apk) and overlay package \n\
'overlay' (path to apk); write results to 'idmap' (path). \n\
\n\
+ --scan: non-recursively search directory 'dir-to-scan' (path) for overlay packages with \n\
+ target package 'target-package-name-to-look-for' (package name) present at\n\
+ 'path-to-target-apk' (path to apk). For each overlay package found, create an\n\
+ idmap file in 'dir-to-hold-idmaps' (path). \n\
+\n\
--inspect: decode the binary format of 'idmap' (path) and display the contents in a \n\
debug-friendly format. \n\
\n\
@@ -90,6 +97,16 @@ EXAMPLES \n\
NOTES \n\
This tool and its expected invocation from installd is modelled on dexopt.";
+ bool verify_directory_readable(const char *path)
+ {
+ return access(path, R_OK | X_OK) == 0;
+ }
+
+ bool verify_directory_writable(const char *path)
+ {
+ return access(path, W_OK) == 0;
+ }
+
bool verify_file_readable(const char *path)
{
return access(path, R_OK) == 0;
@@ -150,6 +167,36 @@ NOTES \n\
return idmap_create_path(target_apk_path, overlay_apk_path, idmap_path);
}
+ int maybe_scan(const char *target_package_name, const char *target_apk_path,
+ const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
+ {
+ if (!verify_root_or_system()) {
+ fprintf(stderr, "error: permission denied: not user root or user system\n");
+ return -1;
+ }
+
+ if (!verify_file_readable(target_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
+ return -1;
+ }
+
+ if (!verify_directory_writable(idmap_dir)) {
+ ALOGD("error: no write access to %s: %s\n", idmap_dir, strerror(errno));
+ return -1;
+ }
+
+ const size_t N = overlay_dirs->size();
+ for (size_t i = 0; i < N; i++) {
+ const char *dir = overlay_dirs->itemAt(i);
+ if (!verify_directory_readable(dir)) {
+ ALOGD("error: no read access to %s: %s\n", dir, strerror(errno));
+ return -1;
+ }
+ }
+
+ return idmap_scan(target_package_name, target_apk_path, idmap_dir, overlay_dirs);
+ }
+
int maybe_inspect(const char *idmap_path)
{
// anyone (not just root or system) may do --inspect
@@ -188,6 +235,14 @@ int main(int argc, char **argv)
return maybe_create_path(argv[2], argv[3], argv[4]);
}
+ if (argc >= 6 && !strcmp(argv[1], "--scan")) {
+ android::Vector<const char *> v;
+ for (int i = 5; i < argc; i++) {
+ v.push(argv[i]);
+ }
+ return maybe_scan(argv[2], argv[3], argv[4], &v);
+ }
+
if (argc == 3 && !strcmp(argv[1], "--inspect")) {
return maybe_inspect(argv[2]);
}
diff --git a/cmds/idmap/idmap.h b/cmds/idmap/idmap.h
index 5914de96a99d..8d4210bcb443 100644
--- a/cmds/idmap/idmap.h
+++ b/cmds/idmap/idmap.h
@@ -25,6 +25,12 @@ int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,
int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd);
+// Regarding target_package_name: the idmap_scan implementation should
+// be able to extract this from the manifest in target_apk_path,
+// simplifying the external API.
+int idmap_scan(const char *target_package_name, const char *target_apk_path,
+ const char *idmap_dir, const android::Vector<const char *> *overlay_dirs);
+
int idmap_inspect(const char *idmap_path);
#endif // _IDMAP_H_
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
new file mode 100644
index 000000000000..8122395d8061
--- /dev/null
+++ b/cmds/idmap/scan.cpp
@@ -0,0 +1,240 @@
+#include <dirent.h>
+#include <inttypes.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include "idmap.h"
+
+#include <memory>
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/StreamingZipInflater.h>
+#include <androidfw/ZipFileRO.h>
+#include <private/android_filesystem_config.h> // for AID_SYSTEM
+#include <utils/SortedVector.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#define NO_OVERLAY_TAG (-1000)
+
+using namespace android;
+
+namespace {
+ struct Overlay {
+ Overlay() {}
+ Overlay(const String8& a, const String8& i, int p) :
+ apk_path(a), idmap_path(i), priority(p) {}
+
+ bool operator<(Overlay const& rhs) const
+ {
+ return rhs.priority > priority;
+ }
+
+ String8 apk_path;
+ String8 idmap_path;
+ int priority;
+ };
+
+ bool writePackagesList(const char *filename, const SortedVector<Overlay>& overlayVector)
+ {
+ // the file is opened for appending so that it doesn't get truncated
+ // before we can guarantee mutual exclusion via the flock
+ FILE* fout = fopen(filename, "a");
+ if (fout == NULL) {
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_EX)) != 0) {
+ fclose(fout);
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(ftruncate(fileno(fout), 0)) != 0) {
+ TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
+ fclose(fout);
+ return false;
+ }
+
+ for (size_t i = 0; i < overlayVector.size(); ++i) {
+ const Overlay& overlay = overlayVector[i];
+ fprintf(fout, "%s %s\n", overlay.apk_path.string(), overlay.idmap_path.string());
+ }
+
+ TEMP_FAILURE_RETRY(fflush(fout));
+ TEMP_FAILURE_RETRY(flock(fileno(fout), LOCK_UN));
+ fclose(fout);
+
+ // Make file world readable since Zygote (running as root) will read
+ // it when creating the initial AssetManger object
+ const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644
+ if (chmod(filename, mode) == -1) {
+ unlink(filename);
+ return false;
+ }
+
+ return true;
+ }
+
+ String8 flatten_path(const char *path)
+ {
+ String16 tmp(path);
+ tmp.replaceAll('/', '@');
+ return String8(tmp);
+ }
+
+ int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name)
+ {
+ const size_t N = parser.getAttributeCount();
+ String16 target;
+ int priority = -1;
+ for (size_t i = 0; i < N; ++i) {
+ size_t len;
+ String16 key(parser.getAttributeName(i, &len));
+ if (key == String16("targetPackage")) {
+ const char16_t *p = parser.getAttributeStringValue(i, &len);
+ if (p != NULL) {
+ target = String16(p, len);
+ }
+ } else if (key == String16("priority")) {
+ Res_value v;
+ if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
+ priority = v.data;
+ if (priority < 0 || priority > 9999) {
+ return -1;
+ }
+ }
+ }
+ }
+ if (target == String16(target_package_name)) {
+ return priority;
+ }
+ return NO_OVERLAY_TAG;
+ }
+
+ int parse_manifest(const void *data, size_t size, const char *target_package_name)
+ {
+ ResXMLTree parser;
+ parser.setTo(data, size);
+ if (parser.getError() != NO_ERROR) {
+ ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
+ return -1;
+ }
+
+ ResXMLParser::event_code_t type;
+ do {
+ type = parser.next();
+ if (type == ResXMLParser::START_TAG) {
+ size_t len;
+ String16 tag(parser.getElementName(&len));
+ if (tag == String16("overlay")) {
+ return parse_overlay_tag(parser, target_package_name);
+ }
+ }
+ } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
+
+ return NO_OVERLAY_TAG;
+ }
+
+ int parse_apk(const char *path, const char *target_package_name)
+ {
+ std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(path));
+ if (zip.get() == NULL) {
+ ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
+ return -1;
+ }
+ ZipEntryRO entry;
+ if ((entry = zip->findEntryByName("AndroidManifest.xml")) == NULL) {
+ ALOGW("%s: failed to find entry AndroidManifest.xml\n", __FUNCTION__);
+ return -1;
+ }
+ uint32_t uncompLen = 0;
+ uint16_t method;
+ if (!zip->getEntryInfo(entry, &method, &uncompLen, NULL, NULL, NULL, NULL)) {
+ ALOGW("%s: failed to read entry info\n", __FUNCTION__);
+ return -1;
+ }
+ if (method != ZipFileRO::kCompressDeflated) {
+ ALOGW("%s: cannot handle zip compression method %" PRIu16 "\n", __FUNCTION__, method);
+ return -1;
+ }
+ FileMap *dataMap = zip->createEntryFileMap(entry);
+ if (dataMap == NULL) {
+ ALOGW("%s: failed to create FileMap\n", __FUNCTION__);
+ return -1;
+ }
+ char *buf = new char[uncompLen];
+ if (NULL == buf) {
+ ALOGW("%s: failed to allocate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
+ delete dataMap;
+ return -1;
+ }
+ StreamingZipInflater inflater(dataMap, uncompLen);
+ if (inflater.read(buf, uncompLen) < 0) {
+ ALOGW("%s: failed to inflate %" PRIu32 " byte\n", __FUNCTION__, uncompLen);
+ delete[] buf;
+ delete dataMap;
+ return -1;
+ }
+
+ int priority = parse_manifest(buf, static_cast<size_t>(uncompLen), target_package_name);
+ delete[] buf;
+ delete dataMap;
+ return priority;
+ }
+}
+
+int idmap_scan(const char *target_package_name, const char *target_apk_path,
+ const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
+{
+ String8 filename = String8(idmap_dir);
+ filename.appendPath("overlays.list");
+
+ SortedVector<Overlay> overlayVector;
+ const size_t N = overlay_dirs->size();
+ for (size_t i = 0; i < N; ++i) {
+ const char *overlay_dir = overlay_dirs->itemAt(i);
+ DIR *dir = opendir(overlay_dir);
+ if (dir == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ struct dirent *dirent;
+ while ((dirent = readdir(dir)) != NULL) {
+ struct stat st;
+ char overlay_apk_path[PATH_MAX + 1];
+ snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name);
+ if (stat(overlay_apk_path, &st) < 0) {
+ continue;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ continue;
+ }
+
+ int priority = parse_apk(overlay_apk_path, target_package_name);
+ if (priority < 0) {
+ continue;
+ }
+
+ String8 idmap_path(idmap_dir);
+ idmap_path.appendPath(flatten_path(overlay_apk_path + 1));
+ idmap_path.append("@idmap");
+
+ if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) {
+ ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n",
+ target_apk_path, overlay_apk_path, idmap_path.string());
+ continue;
+ }
+
+ Overlay overlay(String8(overlay_apk_path), idmap_path, priority);
+ overlayVector.add(overlay);
+ }
+
+ closedir(dir);
+ }
+
+ if (!writePackagesList(filename.string(), overlayVector)) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index f932388b0617..e70bd1162fbb 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -49,7 +49,7 @@ LOCAL_DROIDDOC_OPTIONS:= \
-api $(uiautomator_internal_api_file) \
-removedApi $(uiautomator_internal_removed_api_file)
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := external/doclava/res/assets/templates-sdk
LOCAL_UNINSTALLABLE_MODULE := true
LOCAL_MODULE := uiautomator-stubs
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 19d1a7d899c9..5937dd951dc4 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -69,10 +69,10 @@ import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
* @attr ref android.R.styleable#AccessibilityService_canRequestTouchExplorationMode
* @attr ref android.R.styleable#AccessibilityService_canRetrieveWindowContent
* @attr ref android.R.styleable#AccessibilityService_description
+ * @attr ref android.R.styleable#AccessibilityService_summary
* @attr ref android.R.styleable#AccessibilityService_notificationTimeout
* @attr ref android.R.styleable#AccessibilityService_packageNames
* @attr ref android.R.styleable#AccessibilityService_settingsActivity
- *
* @see AccessibilityService
* @see android.view.accessibility.AccessibilityEvent
* @see android.view.accessibility.AccessibilityManager
@@ -431,6 +431,16 @@ public class AccessibilityServiceInfo implements Parcelable {
private int mCapabilities;
/**
+ * Resource id of the summary of the accessibility service.
+ */
+ private int mSummaryResId;
+
+ /**
+ * Non-localized summary of the accessibility service.
+ */
+ private String mNonLocalizedSummary;
+
+ /**
* Resource id of the description of the accessibility service.
*/
private int mDescriptionResId;
@@ -544,6 +554,15 @@ public class AccessibilityServiceInfo implements Parcelable {
mNonLocalizedDescription = nonLocalizedDescription.toString().trim();
}
}
+ peekedValue = asAttributes.peekValue(
+ com.android.internal.R.styleable.AccessibilityService_summary);
+ if (peekedValue != null) {
+ mSummaryResId = peekedValue.resourceId;
+ CharSequence nonLocalizedSummary = peekedValue.coerceToString();
+ if (nonLocalizedSummary != null) {
+ mNonLocalizedSummary = nonLocalizedSummary.toString().trim();
+ }
+ }
asAttributes.recycle();
} catch (NameNotFoundException e) {
throw new XmlPullParserException( "Unable to create context for: "
@@ -669,6 +688,27 @@ public class AccessibilityServiceInfo implements Parcelable {
}
/**
+ * The localized summary of the accessibility service.
+ * <p>
+ * <strong>Statically set from
+ * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
+ * </p>
+ * @return The localized summary.
+ */
+ public String loadSummary(PackageManager packageManager) {
+ if (mSummaryResId == 0) {
+ return mNonLocalizedSummary;
+ }
+ ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
+ CharSequence summary = packageManager.getText(serviceInfo.packageName,
+ mSummaryResId, serviceInfo.applicationInfo);
+ if (summary != null) {
+ return summary.toString().trim();
+ }
+ return null;
+ }
+
+ /**
* Gets the non-localized description of the accessibility service.
* <p>
* <strong>Statically set from
@@ -726,6 +766,8 @@ public class AccessibilityServiceInfo implements Parcelable {
parcel.writeParcelable(mResolveInfo, 0);
parcel.writeString(mSettingsActivityName);
parcel.writeInt(mCapabilities);
+ parcel.writeInt(mSummaryResId);
+ parcel.writeString(mNonLocalizedSummary);
parcel.writeInt(mDescriptionResId);
parcel.writeString(mNonLocalizedDescription);
}
@@ -740,6 +782,8 @@ public class AccessibilityServiceInfo implements Parcelable {
mResolveInfo = parcel.readParcelable(null);
mSettingsActivityName = parcel.readString();
mCapabilities = parcel.readInt();
+ mSummaryResId = parcel.readInt();
+ mNonLocalizedSummary = parcel.readString();
mDescriptionResId = parcel.readInt();
mNonLocalizedDescription = parcel.readString();
}
@@ -790,6 +834,8 @@ public class AccessibilityServiceInfo implements Parcelable {
stringBuilder.append(", ");
stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName);
stringBuilder.append(", ");
+ stringBuilder.append("summary: ").append(mNonLocalizedSummary);
+ stringBuilder.append(", ");
appendCapabilities(stringBuilder, mCapabilities);
return stringBuilder.toString();
}
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index fca26f87ce4f..86adbb002dc4 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -714,8 +714,8 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
mReversing = inReverse;
// Now that all dependencies are set up, start the animations that should be started.
- boolean isZeroDuration = ValueAnimator.getDurationScale() == 0f || isEmptySet(this);
- if (!isZeroDuration) {
+ boolean isEmptySet = isEmptySet(this);
+ if (!isEmptySet) {
startAnimation();
}
@@ -727,7 +727,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
tmpListeners.get(i).onAnimationStart(this, inReverse);
}
}
- if (isZeroDuration) {
+ if (isEmptySet) {
// In the case of empty AnimatorSet, or 0 duration scale, we will trigger the
// onAnimationEnd() right away.
forceToEnd();
@@ -979,7 +979,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
public boolean doAnimationFrame(long frameTime) {
float durationScale = ValueAnimator.getDurationScale();
if (durationScale == 0f) {
- // Duration scale changed to 0 amid animation, end the animation right away.
+ // Duration scale is 0, end the animation right away.
forceToEnd();
return true;
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 64e2d49d274b..97992cafee0e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -65,6 +65,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -2129,10 +2130,15 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public void getPackageSizeInfoAsUser(String packageName, int userHandle,
IPackageStatsObserver observer) {
- try {
- mPM.getPackageSizeInfo(packageName, userHandle, observer);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
+ throw new UnsupportedOperationException(
+ "Shame on you for calling a hidden API. Shame!");
+ } else if (observer != null) {
+ Log.d(TAG, "Shame on you for calling a hidden API. Shame!");
+ try {
+ observer.onGetStatsCompleted(null, false);
+ } catch (RemoteException ignored) {
+ }
}
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 7cdd45fe8329..be38f42490e5 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -749,19 +749,17 @@ public final class LoadedApk {
}
final File profileFile = getPrimaryProfileFile(mPackageName);
- final File foreignDexProfilesFile =
- Environment.getDataProfilesDeForeignDexDirectory(UserHandle.myUserId());
- VMRuntime.registerAppInfo(profileFile.getPath(), mApplicationInfo.dataDir,
- codePaths.toArray(new String[codePaths.size()]), foreignDexProfilesFile.getPath());
+ VMRuntime.registerAppInfo(profileFile.getPath(),
+ codePaths.toArray(new String[codePaths.size()]));
// Setup the reporter to notify package manager of any relevant dex loads.
// At this point the primary apk is loaded and will not be reported.
// Anything loaded from now on will be tracked as a potential secondary
// or foreign dex file. The goal is to enable:
// 1) monitoring and compilation of secondary dex file
- // 2) track foreign dex file usage (used to determined the
- // compilation filter of apks).
+ // 2) track whether or not a dex file is used by other apps (used to
+ // determined the compilation filter of apks).
if (BaseDexClassLoader.getReporter() != DexLoadReporter.INSTANCE) {
// Set the dex load reporter if not already set.
// Note that during the app's life cycle different LoadedApks may be
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3fc459e33594..a098591c8f44 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -72,6 +72,7 @@ import android.widget.RemoteViews;
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.NotificationColorUtil;
+import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -4068,7 +4069,7 @@ public class Notification implements Parcelable
public RemoteViews makeAmbientNotification() {
RemoteViews ambient = applyStandardTemplateWithActions(
R.layout.notification_template_material_ambient,
- mParams.reset().fillTextsFrom(this).hasProgress(false).ambient(true));
+ mParams.reset().ambient(true).fillTextsFrom(this).hasProgress(false));
return ambient;
}
@@ -4381,7 +4382,13 @@ public class Notification implements Parcelable
}
private CharSequence processLegacyText(CharSequence charSequence) {
- if (isLegacy() || textColorsNeedInversion()) {
+ return processLegacyText(charSequence, false /* ambient */);
+ }
+
+ private CharSequence processLegacyText(CharSequence charSequence, boolean ambient) {
+ boolean isAlreadyLightText = isLegacy() || textColorsNeedInversion();
+ boolean wantLightText = ambient;
+ if (isAlreadyLightText != wantLightText) {
return getColorUtil().invertCharSequenceColors(charSequence);
} else {
return charSequence;
@@ -7853,14 +7860,15 @@ public class Notification implements Parcelable
}
final StandardTemplateParams ambient(boolean ambient) {
+ Preconditions.checkState(title == null && text == null, "must set ambient before text");
this.ambient = ambient;
return this;
}
final StandardTemplateParams fillTextsFrom(Builder b) {
Bundle extras = b.mN.extras;
- title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE));
- text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
+ title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
+ text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
return this;
}
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 85e6b85c2beb..16c85f587956 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -275,6 +275,13 @@ public final class NotificationChannel implements Parcelable {
mDeleted = deleted;
}
+ /**
+ * @hide
+ */
+ public void setNameResId(@StringRes int nameResId) {
+ this.mNameResId = nameResId;
+ }
+
// Modifiable by a notification ranker.
/**
diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java
index a38fd4387e90..56338f58f980 100644
--- a/core/java/android/app/QueuedWork.java
+++ b/core/java/android/app/QueuedWork.java
@@ -21,9 +21,11 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
+import android.os.StrictMode;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ExponentiallyBucketedHistogram;
import java.util.LinkedList;
@@ -46,11 +48,14 @@ import java.util.LinkedList;
*/
public class QueuedWork {
private static final String LOG_TAG = QueuedWork.class.getSimpleName();
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
/** Delay for delayed runnables, as big as possible but low enough to be barely perceivable */
private static final long DELAY = 100;
+ /** If a {@link #waitToFinish()} takes more than {@value #MAX_WAIT_TIME_MILLIS} ms, warn */
+ private static final long MAX_WAIT_TIME_MILLIS = 512;
+
/** Lock for this class */
private static final Object sLock = new Object();
@@ -79,6 +84,13 @@ public class QueuedWork {
@GuardedBy("sLock")
private static boolean sCanDelay = true;
+ /** Time (and number of instances) waited for work to get processed */
+ @GuardedBy("sLock")
+ private final static ExponentiallyBucketedHistogram
+ mWaitTimes = new ExponentiallyBucketedHistogram(
+ 16);
+ private static int mNumWaits = 0;
+
/**
* Lazily create a handler on a separate thread.
*
@@ -135,13 +147,9 @@ public class QueuedWork {
* after Service command handling, etc. (so async work is never lost)
*/
public static void waitToFinish() {
- long startTime = 0;
+ long startTime = System.currentTimeMillis();
boolean hadMessages = false;
- if (DEBUG) {
- startTime = System.currentTimeMillis();
- }
-
Handler handler = getHandler();
synchronized (sLock) {
@@ -159,7 +167,12 @@ public class QueuedWork {
sCanDelay = false;
}
- processPendingWork();
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+ try {
+ processPendingWork();
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
try {
while (true) {
@@ -179,11 +192,16 @@ public class QueuedWork {
sCanDelay = true;
}
- if (DEBUG) {
+ synchronized (sLock) {
long waitTime = System.currentTimeMillis() - startTime;
if (waitTime > 0 || hadMessages) {
- Log.d(LOG_TAG, "waited " + waitTime + " ms");
+ mWaitTimes.add(Long.valueOf(waitTime).intValue());
+ mNumWaits++;
+
+ if (DEBUG || mNumWaits % 1024 == 0 || waitTime > MAX_WAIT_TIME_MILLIS) {
+ mWaitTimes.log(LOG_TAG, "waited: ");
+ }
}
}
}
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index 11ba7eec9745..063ad24cd947 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -28,6 +28,7 @@ import android.util.Log;
import com.google.android.collect.Maps;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ExponentiallyBucketedHistogram;
import com.android.internal.util.XmlUtils;
import dalvik.system.BlockGuard;
@@ -53,9 +54,12 @@ import libcore.io.IoUtils;
final class SharedPreferencesImpl implements SharedPreferences {
private static final String TAG = "SharedPreferencesImpl";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final Object CONTENT = new Object();
+ /** If a fsync takes more than {@value #MAX_FSYNC_DURATION_MILLIS} ms, warn */
+ private static final long MAX_FSYNC_DURATION_MILLIS = 256;
+
// Lock ordering rules:
// - acquire SharedPreferencesImpl.mLock before EditorImpl.mLock
// - acquire mWritingToDiskLock before EditorImpl.mLock
@@ -93,6 +97,11 @@ final class SharedPreferencesImpl implements SharedPreferences {
@GuardedBy("mWritingToDiskLock")
private long mDiskStateGeneration;
+ /** Time (and number of instances) of file-system sync requests */
+ @GuardedBy("mWritingToDiskLock")
+ private final ExponentiallyBucketedHistogram mSyncTimes = new ExponentiallyBucketedHistogram(16);
+ private int mNumSync = 0;
+
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
@@ -139,8 +148,8 @@ final class SharedPreferencesImpl implements SharedPreferences {
str = new BufferedInputStream(
new FileInputStream(mFile), 16*1024);
map = XmlUtils.readMapXml(str);
- } catch (XmlPullParserException | IOException e) {
- Log.w(TAG, "getSharedPreferences", e);
+ } catch (Exception e) {
+ Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
} finally {
IoUtils.closeQuietly(str);
}
@@ -719,15 +728,11 @@ final class SharedPreferencesImpl implements SharedPreferences {
}
XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
- if (DEBUG) {
- writeTime = System.currentTimeMillis();
- }
+ writeTime = System.currentTimeMillis();
FileUtils.sync(str);
- if (DEBUG) {
- fsyncTime = System.currentTimeMillis();
- }
+ fsyncTime = System.currentTimeMillis();
str.close();
ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
@@ -761,14 +766,24 @@ final class SharedPreferencesImpl implements SharedPreferences {
mcr.setDiskWriteResult(true, true);
- Log.d(TAG, "write: " + (existsTime - startTime) + "/"
- + (backupExistsTime - startTime) + "/"
- + (outputStreamCreateTime - startTime) + "/"
- + (writeTime - startTime) + "/"
- + (fsyncTime - startTime) + "/"
- + (setPermTime - startTime) + "/"
- + (fstatTime - startTime) + "/"
- + (deleteTime - startTime));
+ if (DEBUG) {
+ Log.d(TAG, "write: " + (existsTime - startTime) + "/"
+ + (backupExistsTime - startTime) + "/"
+ + (outputStreamCreateTime - startTime) + "/"
+ + (writeTime - startTime) + "/"
+ + (fsyncTime - startTime) + "/"
+ + (setPermTime - startTime) + "/"
+ + (fstatTime - startTime) + "/"
+ + (deleteTime - startTime));
+ }
+
+ long fsyncDuration = fsyncTime - writeTime;
+ mSyncTimes.add(Long.valueOf(fsyncDuration).intValue());
+ mNumSync++;
+
+ if (DEBUG || mNumSync % 1024 == 0 || fsyncDuration > MAX_FSYNC_DURATION_MILLIS) {
+ mSyncTimes.log(TAG, "Time required to fsync " + mFile + ": ");
+ }
return;
} catch (XmlPullParserException e) {
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 34a0c305a6d8..0fb59668a201 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -779,6 +779,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* become affiliated again (even if security logging is enabled).
* See {@link DevicePolicyManager#setAffiliationIds}
*
+ * <p>This callback will be re-triggered if the logs are not retrieved.
+ *
* <p>This callback is only applicable to device owners.
*
* @param context The running context as per {@link #onReceive}.
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 8e0098f12071..2e0ca02024dc 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -538,7 +538,7 @@ public class AssistStructure implements Parcelable {
// TODO(b/33197203): once we have more flags, it might be better to store the individual
// fields (viewId and childId) of the field.
AutoFillId mAutoFillId;
- AutoFillType mAutoFillType;
+ @View.AutofillType int mAutofillType;
@View.AutoFillHint int mAutoFillHint;
AutoFillValue mAutoFillValue;
String[] mAutoFillOptions;
@@ -623,7 +623,7 @@ public class AssistStructure implements Parcelable {
if ((flags&FLAGS_HAS_AUTO_FILL_DATA) != 0) {
mSanitized = in.readInt() == 1;
mAutoFillId = in.readParcelable(null);
- mAutoFillType = in.readParcelable(null);
+ mAutofillType = in.readInt();
mAutoFillHint = in.readInt();
mAutoFillValue = in.readParcelable(null);
mAutoFillOptions = in.readStringArray();
@@ -757,7 +757,7 @@ public class AssistStructure implements Parcelable {
writeSensitive = mSanitized || !sanitizeOnWrite;
out.writeInt(mSanitized ? 1 : 0);
out.writeParcelable(mAutoFillId, 0);
- out.writeParcelable(mAutoFillType, 0);
+ out.writeInt(mAutofillType);
out.writeInt(mAutoFillHint);
final AutoFillValue sanitizedValue = writeSensitive ? mAutoFillValue : null;
out.writeParcelable(sanitizedValue, 0);
@@ -851,14 +851,32 @@ public class AssistStructure implements Parcelable {
}
/**
+ * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype()
+ */
+ @Deprecated
+ public AutoFillType getAutoFillType() {
+ switch (getAutofillType()) {
+ case View.AUTOFILL_TYPE_TEXT:
+ return AutoFillType.forText();
+ case View.AUTOFILL_TYPE_TOGGLE:
+ return AutoFillType.forToggle();
+ case View.AUTOFILL_TYPE_LIST:
+ return AutoFillType.forList();
+ case View.AUTOFILL_TYPE_DATE:
+ return AutoFillType.forDate();
+ default:
+ return null;
+ }
+ }
+
+ /**
* Gets the the type of value that can be used to auto-fill the view contents.
*
* <p>It's only set when the {@link AssistStructure} is used for auto-filling purposes, not
* for assist.
*/
- // TODO(b/33197203, b/33802548): add CTS/unit test
- public AutoFillType getAutoFillType() {
- return mAutoFillType;
+ public @View.AutofillType int getAutofillType() {
+ return mAutofillType;
}
/**
@@ -1562,7 +1580,22 @@ public class AssistStructure implements Parcelable {
@Override
public void setAutoFillType(AutoFillType type) {
- mNode.mAutoFillType = type;
+ if (type == null) return;
+
+ if (type.isText()) {
+ mNode.mAutofillType = View.AUTOFILL_TYPE_TEXT;
+ } else if (type.isToggle()) {
+ mNode.mAutofillType = View.AUTOFILL_TYPE_TOGGLE;
+ } else if (type.isList()) {
+ mNode.mAutofillType = View.AUTOFILL_TYPE_LIST;
+ } else if (type.isDate()) {
+ mNode.mAutofillType = View.AUTOFILL_TYPE_DATE;
+ }
+ }
+
+ @Override
+ public void setAutofillType(@View.AutofillType int type) {
+ mNode.mAutofillType = type;
}
@Override
@@ -1711,7 +1744,7 @@ public class AssistStructure implements Parcelable {
Log.i(TAG, prefix + " NO AUTO-FILL ID");
} else {
Log.i(TAG, prefix + "AutoFill info: id= " + autoFillId
- + ", type=" + node.getAutoFillType()
+ + ", type=" + node.getAutofillType()
+ ", options=" + Arrays.toString(node.getAutoFillOptions())
+ ", inputType=" + node.getInputType()
+ ", hint=" + Integer.toHexString(node.getAutoFillHint())
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 3887556ebe7d..6652eee12337 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -425,8 +425,8 @@ public class JobInfo implements Parcelable {
private JobInfo(JobInfo.Builder b) {
jobId = b.mJobId;
- extras = b.mExtras.deepcopy();
- transientExtras = b.mTransientExtras.deepcopy();
+ extras = b.mExtras.deepCopy();
+ transientExtras = b.mTransientExtras.deepCopy();
service = b.mJobService;
constraintFlags = b.mConstraintFlags;
triggerContentUris = b.mTriggerContentUris != null
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 453476772430..d36692a74a0e 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -26,6 +26,7 @@ import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.PeriodicAdvertisingManager;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanRecord;
@@ -525,6 +526,7 @@ public final class BluetoothAdapter {
private static BluetoothLeScanner sBluetoothLeScanner;
private static BluetoothLeAdvertiser sBluetoothLeAdvertiser;
+ private static PeriodicAdvertisingManager sPeriodicAdvertisingManager;
private final IBluetoothManager mManagerService;
private IBluetooth mService;
@@ -630,6 +632,30 @@ public final class BluetoothAdapter {
}
/**
+ * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising
+ * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic
+ * Advertising is not supported on this device.
+ * <p>
+ * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is
+ * supported on this device before calling this method.
+ */
+ public PeriodicAdvertisingManager getPeriodicAdvertisingManager() {
+ if (!getLeAccess())
+ return null;
+
+ if (!isLePeriodicAdvertisingSupported())
+ return null;
+
+ synchronized (mLock) {
+ if (sPeriodicAdvertisingManager == null) {
+ sPeriodicAdvertisingManager =
+ new PeriodicAdvertisingManager(mManagerService);
+ }
+ }
+ return sPeriodicAdvertisingManager;
+ }
+
+ /**
* Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
*/
public BluetoothLeScanner getBluetoothLeScanner() {
@@ -1385,6 +1411,78 @@ public final class BluetoothAdapter {
}
/**
+ * Return true if LE 2M PHY feature is supported.
+ *
+ * @return true if chipset supports LE 2M PHY feature
+ */
+ public boolean isLe2MPhySupported() {
+ if (!getLeAccess()) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isLe2MPhySupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
+ * Return true if LE Coded PHY feature is supported.
+ *
+ * @return true if chipset supports LE Coded PHY feature
+ */
+ public boolean isLeCodedPhySupported() {
+ if (!getLeAccess()) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isLeCodedPhySupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
+ * Return true if LE Periodic Advertising feature is supported.
+ *
+ * @return true if chipset supports LE Periodic Advertising feature
+ */
+ public boolean isLeExtendedAdvertisingSupported() {
+ if (!getLeAccess()) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isLeExtendedAdvertisingSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
+ * Return true if LE Periodic Advertising feature is supported.
+ *
+ * @return true if chipset supports LE Periodic Advertising feature
+ */
+ public boolean isLePeriodicAdvertisingSupported() {
+ if (!getLeAccess()) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isLePeriodicAdvertisingSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
* Return true if hardware has entries available for matching beacons
*
* @return true if there are hw entries available for matching beacons
@@ -1858,6 +1956,35 @@ public final class BluetoothAdapter {
return listenUsingL2capOn(port, false, false);
}
+
+ /**
+ * Construct an insecure L2CAP server socket.
+ * Call #accept to retrieve connections to this socket.
+ * <p>To auto assign a port without creating a SDP record use
+ * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
+ * @param port the PSM to listen on
+ * @return An L2CAP BluetoothServerSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_L2CAP, false, false, port, false, false);
+ int errno = socket.mSocket.bindListen();
+ if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+ socket.setChannel(socket.mSocket.getPort());
+ }
+ if (errno != 0) {
+ //TODO(BT): Throw the same exception error code
+ // that the previous code was using.
+ //socket.mSocket.throwErrnoNative(errno);
+ throw new IOException("Error: " + errno);
+ }
+ return socket;
+
+ }
+
/**
* Read the local Out of Band Pairing Data
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 5c9e2ee4fc42..31fc294e3181 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -592,6 +592,42 @@ public final class BluetoothDevice implements Parcelable {
*/
public static final int TRANSPORT_LE = 2;
+ /**
+ * 1M initiating PHY.
+ */
+ public static final int PHY_LE_1M = 1;
+
+ /**
+ * 2M initiating PHY.
+ */
+ public static final int PHY_LE_2M = 2;
+
+ /**
+ * LE Coded initiating PHY.
+ */
+ public static final int PHY_LE_CODED = 4;
+
+ /**
+ * Any LE PHY.
+ */
+ public static final int PHY_LE_ANY = PHY_LE_1M | PHY_LE_2M | PHY_LE_CODED;
+
+ /**
+ * No preferred coding when transmitting on the LE Coded PHY.
+ */
+ public static final int PHY_OPTION_NO_PREFERRED = 0;
+
+ /**
+ * Prefer the S=2 coding to be used when transmitting on the LE Coded PHY.
+ */
+ public static final int PHY_OPTION_S2 = 1;
+
+ /**
+ * Prefer the S=8 coding to be used when transmitting on the LE Coded PHY.
+ */
+ public static final int PHY_OPTION_S8 = 2;
+
+
/** @hide */
public static final String EXTRA_MAS_INSTANCE =
"android.bluetooth.device.extra.MAS_INSTANCE";
@@ -1412,6 +1448,27 @@ public final class BluetoothDevice implements Parcelable {
}
/**
+ * Create an L2cap {@link BluetoothSocket} ready to start an insecure
+ * outgoing connection to this remote device on given channel.
+ * <p>The remote device will be not authenticated and communication on this
+ * socket will not be encrypted.
+ * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
+ * connection.
+ * <p>Valid L2CAP PSM channels are in range 1 to 2^16.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @param channel L2cap PSM/channel to connect to
+ * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions
+ * @hide
+ */
+ public BluetoothSocket createInsecureL2capSocket(int channel) throws IOException {
+ return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, this, channel,
+ null);
+ }
+
+ /**
* Create an RFCOMM {@link BluetoothSocket} ready to start a secure
* outgoing connection to this remote device using SDP lookup of uuid.
* <p>This is designed to be used with {@link
@@ -1594,6 +1651,67 @@ public final class BluetoothDevice implements Parcelable {
*/
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport) {
+ return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M));
+ }
+
+ /**
+ * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as any further GATT client operations.
+ * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+ * GATT client operations.
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @param autoConnect Whether to directly connect to the remote device (false)
+ * or to automatically connect as soon as the remote
+ * device becomes available (true).
+ * @throws IllegalArgumentException if callback is null
+ */
+ public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+ BluetoothGattCallbackExt callback) {
+ return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO));
+ }
+
+ /**
+ * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as any further GATT client operations.
+ * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+ * GATT client operations.
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @param autoConnect Whether to directly connect to the remote device (false)
+ * or to automatically connect as soon as the remote
+ * device becomes available (true).
+ * @param transport preferred transport for GATT connections to remote dual-mode devices
+ * {@link BluetoothDevice#TRANSPORT_AUTO} or
+ * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE}
+ * @throws IllegalArgumentException if callback is null
+ */
+ public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+ BluetoothGattCallbackExt callback, int transport) {
+ return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M));
+ }
+
+ /**
+ * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as any further GATT client operations.
+ * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+ * GATT client operations.
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @param autoConnect Whether to directly connect to the remote device (false)
+ * or to automatically connect as soon as the remote
+ * device becomes available (true).
+ * @param transport preferred transport for GATT connections to remote dual-mode devices
+ * {@link BluetoothDevice#TRANSPORT_AUTO} or
+ * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE}
+ * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of
+ * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M},
+ * and {@link BluetoothDevice#PHY_LE_CODED}. This option does not take effect if
+ * {@code autoConnect} is set to true.
+ * @throws IllegalArgumentException if callback is null
+ */
+ public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+ BluetoothGattCallbackExt callback, int transport, int phy) {
// TODO(Bluetooth) check whether platform support BLE
// Do the check here or in GattServer?
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1604,7 +1722,7 @@ public final class BluetoothDevice implements Parcelable {
// BLE is not supported
return null;
}
- BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport);
+ BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, phy);
gatt.connect(autoConnect, callback);
return gatt;
} catch (RemoteException e) {Log.e(TAG, "", e);}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 12ebdac76d4f..11dbf70e1f30 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -31,7 +31,7 @@ import java.util.UUID;
* <p>This class provides Bluetooth GATT functionality to enable communication
* with Bluetooth Smart or Smart Ready devices.
*
- * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
+ * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallbackExt}
* and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
* GATT capable devices can be discovered using the Bluetooth device discovery or BLE
* scan process.
@@ -42,7 +42,7 @@ public final class BluetoothGatt implements BluetoothProfile {
private static final boolean VDBG = false;
private IBluetoothGatt mService;
- private BluetoothGattCallback mCallback;
+ private BluetoothGattCallbackExt mCallback;
private int mClientIf;
private BluetoothDevice mDevice;
private boolean mAutoConnect;
@@ -51,6 +51,7 @@ public final class BluetoothGatt implements BluetoothProfile {
private final Object mStateLock = new Object();
private Boolean mDeviceBusy = false;
private int mTransport;
+ private int mPhy;
private static final int AUTH_RETRY_STATE_IDLE = 0;
private static final int AUTH_RETRY_STATE_NO_MITM = 1;
@@ -132,10 +133,10 @@ public final class BluetoothGatt implements BluetoothProfile {
/*package*/ static final int AUTHENTICATION_MITM = 2;
/**
- * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
+ * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallbackExt implementation.
*/
- private final IBluetoothGattCallback mBluetoothGattCallback =
- new IBluetoothGattCallback.Stub() {
+ private final IBluetoothGattCallbackExt mBluetoothGattCallbackExt =
+ new IBluetoothGattCallbackExt.Stub() {
/**
* Application interface registered - app is ready to go
* @hide
@@ -161,13 +162,51 @@ public final class BluetoothGatt implements BluetoothProfile {
}
try {
mService.clientConnect(mClientIf, mDevice.getAddress(),
- !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
+ !mAutoConnect, mTransport, mPhy); // autoConnect is inverse of "isDirect"
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
/**
+ * Phy update callback
+ * @hide
+ */
+ @Override
+ public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
+ if (DBG) Log.d(TAG, "onPhyUpdate() - status=" + status
+ + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+
+ try {
+ mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception in callback", ex);
+ }
+ }
+
+ /**
+ * Phy read callback
+ * @hide
+ */
+ @Override
+ public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
+ if (DBG) Log.d(TAG, "onPhyRead() - status=" + status
+ + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+
+ try {
+ mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception in callback", ex);
+ }
+ }
+
+ /**
* Client connection state changed
* @hide
*/
@@ -503,10 +542,11 @@ public final class BluetoothGatt implements BluetoothProfile {
};
/*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
- int transport) {
+ int transport, int phy) {
mService = iGatt;
mDevice = device;
mTransport = transport;
+ mPhy = phy;
mServices = new ArrayList<BluetoothGattService>();
mConnState = CONN_STATE_IDLE;
@@ -578,7 +618,7 @@ public final class BluetoothGatt implements BluetoothProfile {
/**
* Register an application callback to start using GATT.
*
- * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
+ * <p>This is an asynchronous call. The callback {@link BluetoothGattCallbackExt#onAppRegistered}
* is used to notify success or failure if the function returns true.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -587,7 +627,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* @return If true, the callback will be called to notify success or failure,
* false on immediate error
*/
- private boolean registerApp(BluetoothGattCallback callback) {
+ private boolean registerApp(BluetoothGattCallbackExt callback) {
if (DBG) Log.d(TAG, "registerApp()");
if (mService == null) return false;
@@ -596,7 +636,7 @@ public final class BluetoothGatt implements BluetoothProfile {
if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
try {
- mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
+ mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallbackExt);
} catch (RemoteException e) {
Log.e(TAG,"",e);
return false;
@@ -626,7 +666,7 @@ public final class BluetoothGatt implements BluetoothProfile {
*
* <p>The connection may not be established right away, but will be
* completed when the remote device is available. A
- * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
+ * {@link BluetoothGattCallbackExt#onConnectionStateChange} callback will be
* invoked when the connection state changes as a result of this function.
*
* <p>The autoConnect parameter determines whether to actively connect to
@@ -644,7 +684,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* device becomes available (true).
* @return true, if the connection attempt was initiated successfully
*/
- /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
+ /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallbackExt callback) {
if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
synchronized(mStateLock) {
if (mConnState != CONN_STATE_IDLE) {
@@ -696,7 +736,7 @@ public final class BluetoothGatt implements BluetoothProfile {
public boolean connect() {
try {
mService.clientConnect(mClientIf, mDevice.getAddress(),
- false, mTransport); // autoConnect is inverse of "isDirect"
+ false, mTransport, mPhy); // autoConnect is inverse of "isDirect"
return true;
} catch (RemoteException e) {
Log.e(TAG,"",e);
@@ -705,6 +745,45 @@ public final class BluetoothGatt implements BluetoothProfile {
}
/**
+ * Set the preferred connection PHY for this app. Please note that this is just a
+ * recommendation, wether the PHY change will happen depends on other applications peferences,
+ * local and remote controller capabilities. Controller can override these settings.
+ * <p>
+ * {@link BluetoothGattCallbackExt#onPhyUpdate} will be triggered as a result of this call, even
+ * if no PHY change happens. It is also triggered when remote device updates the PHY.
+ *
+ * @param txPhy preferred transmitter PHY. Bitwise OR of any of
+ * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
+ * {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param rxPhy preferred receiver PHY. Bitwise OR of any of
+ * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
+ * {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
+ * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED},
+ * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8}
+ */
+ public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
+ try {
+ mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
+ phyOptions);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
+ * in {@link BluetoothGattCallbackExt#onPhyRead}
+ */
+ public void readPhy() {
+ try {
+ mService.clientReadPhy(mClientIf, mDevice.getAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
* Return the remote bluetooth device this GATT client targets to
*
* @return remote bluetooth device
@@ -718,7 +797,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* characteristics and descriptors.
*
* <p>This is an asynchronous operation. Once service discovery is completed,
- * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
+ * the {@link BluetoothGattCallbackExt#onServicesDiscovered} callback is
* triggered. If the discovery was successful, the remote services can be
* retrieved using the {@link #getServices} function.
*
@@ -797,7 +876,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* Reads the requested characteristic from the associated remote device.
*
* <p>This is an asynchronous operation. The result of the read operation
- * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
+ * is reported by the {@link BluetoothGattCallbackExt#onCharacteristicRead}
* callback.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -839,7 +918,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* Writes a given characteristic and its values to the associated remote device.
*
* <p>Once the write operation has been completed, the
- * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
+ * {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback is invoked,
* reporting the result of the operation.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -883,7 +962,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* Reads the value for a given descriptor from the associated remote device.
*
* <p>Once the read operation has been completed, the
- * {@link BluetoothGattCallback#onDescriptorRead} callback is
+ * {@link BluetoothGattCallbackExt#onDescriptorRead} callback is
* triggered, signaling the result of the operation.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -924,7 +1003,7 @@ public final class BluetoothGatt implements BluetoothProfile {
/**
* Write the value of a given descriptor to the associated remote device.
*
- * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
+ * <p>A {@link BluetoothGattCallbackExt#onDescriptorWrite} callback is
* triggered to report the result of the write operation.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -968,7 +1047,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* <p>Once a reliable write transaction has been initiated, all calls
* to {@link #writeCharacteristic} are sent to the remote device for
* verification and queued up for atomic execution. The application will
- * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
+ * receive an {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback
* in response to every {@link #writeCharacteristic} call and is responsible
* for verifying if the value has been transmitted accurately.
*
@@ -1002,7 +1081,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* <p>This function will commit all queued up characteristic write
* operations for a given remote device.
*
- * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
+ * <p>A {@link BluetoothGattCallbackExt#onReliableWriteCompleted} callback is
* invoked to indicate whether the transaction has been executed correctly.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -1060,7 +1139,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* Enable or disable notifications/indications for a given characteristic.
*
* <p>Once notifications are enabled for a characteristic, a
- * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
+ * {@link BluetoothGattCallbackExt#onCharacteristicChanged} callback will be
* triggered if the remote device indicates that the given characteristic
* has changed.
*
@@ -1115,7 +1194,7 @@ public final class BluetoothGatt implements BluetoothProfile {
/**
* Read the RSSI for a connected remote device.
*
- * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
+ * <p>The {@link BluetoothGattCallbackExt#onReadRemoteRssi} callback will be
* invoked when the RSSI value has been read.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -1143,7 +1222,7 @@ public final class BluetoothGatt implements BluetoothProfile {
* the data sent is truncated to the MTU size. This function may be used
* to request a larger MTU size to be able to send more data at once.
*
- * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
+ * <p>A {@link BluetoothGattCallbackExt#onMtuChanged} callback will indicate
* whether this operation was successful.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index a9156205afca..4da106df610c 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -18,138 +18,22 @@ package android.bluetooth;
/**
* This abstract class is used to implement {@link BluetoothGatt} callbacks.
+ * @deprecated use {@link BluetoothGattCallbackExt}
*/
-public abstract class BluetoothGattCallback {
+public abstract class BluetoothGattCallback extends BluetoothGattCallbackExt {
/**
- * Callback indicating when GATT client has connected/disconnected to/from a remote
- * GATT server.
- *
- * @param gatt GATT client
- * @param status Status of the connect or disconnect operation.
- * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
- * @param newState Returns the new connection state. Can be one of
- * {@link BluetoothProfile#STATE_DISCONNECTED} or
- * {@link BluetoothProfile#STATE_CONNECTED}
+ * @hide
*/
- public void onConnectionStateChange(BluetoothGatt gatt, int status,
- int newState) {
+ @Override
+ public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
}
/**
- * Callback invoked when the list of remote services, characteristics and descriptors
- * for the remote device have been updated, ie new services have been discovered.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices}
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device
- * has been explored successfully.
+ * @hide
*/
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ @Override
+ public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
}
- /**
- * Callback reporting the result of a characteristic read operation.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic}
- * @param characteristic Characteristic that was read from the associated
- * remote device.
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
- * was completed successfully.
- */
- public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
- int status) {
- }
-
- /**
- * Callback indicating the result of a characteristic write operation.
- *
- * <p>If this callback is invoked while a reliable write transaction is
- * in progress, the value of the characteristic represents the value
- * reported by the remote device. An application should compare this
- * value to the desired value to be written. If the values don't match,
- * the application must abort the reliable write transaction.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic}
- * @param characteristic Characteristic that was written to the associated
- * remote device.
- * @param status The result of the write operation
- * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
- */
- public void onCharacteristicWrite(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic, int status) {
- }
-
- /**
- * Callback triggered as a result of a remote characteristic notification.
- *
- * @param gatt GATT client the characteristic is associated with
- * @param characteristic Characteristic that has been updated as a result
- * of a remote notification event.
- */
- public void onCharacteristicChanged(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic) {
- }
-
- /**
- * Callback reporting the result of a descriptor read operation.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
- * @param descriptor Descriptor that was read from the associated
- * remote device.
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
- * was completed successfully
- */
- public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
- int status) {
- }
-
- /**
- * Callback indicating the result of a descriptor write operation.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
- * @param descriptor Descriptor that was writte to the associated
- * remote device.
- * @param status The result of the write operation
- * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
- */
- public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
- int status) {
- }
-
- /**
- * Callback invoked when a reliable write transaction has been completed.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite}
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write
- * transaction was executed successfully
- */
- public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
- }
-
- /**
- * Callback reporting the RSSI for a remote device connection.
- *
- * This callback is triggered in response to the
- * {@link BluetoothGatt#readRemoteRssi} function.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi}
- * @param rssi The RSSI value for the remote device
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully
- */
- public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
- }
-
- /**
- * Callback indicating the MTU for a given device connection has changed.
- *
- * This callback is triggered in response to the
- * {@link BluetoothGatt#requestMtu} function, or in response to a connection
- * event.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu}
- * @param mtu The new MTU size
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully
- */
- public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
- }
}
diff --git a/core/java/android/bluetooth/BluetoothGattCallbackExt.java b/core/java/android/bluetooth/BluetoothGattCallbackExt.java
new file mode 100644
index 000000000000..63774c8fbb65
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattCallbackExt.java
@@ -0,0 +1,182 @@
+/*
+ * 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 android.bluetooth;
+
+/**
+ * This abstract class is used to implement {@link BluetoothGatt} callbacks.
+ */
+public abstract class BluetoothGattCallbackExt {
+
+ /**
+ * Callback triggered as result of {@link BluetoothGatt#setPreferredPhy}, or as a result of
+ * remote device changing the PHY.
+ *
+ * @param gatt GATT client
+ * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param status status of the operation
+ */
+ public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
+ }
+
+ /**
+ * Callback triggered as result of {@link BluetoothGatt#readPhy}
+ *
+ * @param gatt GATT client
+ * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param status status of the operation
+ */
+ public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
+ }
+
+ /**
+ * Callback indicating when GATT client has connected/disconnected to/from a remote
+ * GATT server.
+ *
+ * @param gatt GATT client
+ * @param status Status of the connect or disconnect operation.
+ * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
+ * @param newState Returns the new connection state. Can be one of
+ * {@link BluetoothProfile#STATE_DISCONNECTED} or
+ * {@link BluetoothProfile#STATE_CONNECTED}
+ */
+ public void onConnectionStateChange(BluetoothGatt gatt, int status,
+ int newState) {
+ }
+
+ /**
+ * Callback invoked when the list of remote services, characteristics and descriptors
+ * for the remote device have been updated, ie new services have been discovered.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices}
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device
+ * has been explored successfully.
+ */
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ }
+
+ /**
+ * Callback reporting the result of a characteristic read operation.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic}
+ * @param characteristic Characteristic that was read from the associated
+ * remote device.
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
+ * was completed successfully.
+ */
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
+ int status) {
+ }
+
+ /**
+ * Callback indicating the result of a characteristic write operation.
+ *
+ * <p>If this callback is invoked while a reliable write transaction is
+ * in progress, the value of the characteristic represents the value
+ * reported by the remote device. An application should compare this
+ * value to the desired value to be written. If the values don't match,
+ * the application must abort the reliable write transaction.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic}
+ * @param characteristic Characteristic that was written to the associated
+ * remote device.
+ * @param status The result of the write operation
+ * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
+ */
+ public void onCharacteristicWrite(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic, int status) {
+ }
+
+ /**
+ * Callback triggered as a result of a remote characteristic notification.
+ *
+ * @param gatt GATT client the characteristic is associated with
+ * @param characteristic Characteristic that has been updated as a result
+ * of a remote notification event.
+ */
+ public void onCharacteristicChanged(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic) {
+ }
+
+ /**
+ * Callback reporting the result of a descriptor read operation.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
+ * @param descriptor Descriptor that was read from the associated
+ * remote device.
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
+ * was completed successfully
+ */
+ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+ int status) {
+ }
+
+ /**
+ * Callback indicating the result of a descriptor write operation.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
+ * @param descriptor Descriptor that was writte to the associated
+ * remote device.
+ * @param status The result of the write operation
+ * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
+ */
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+ int status) {
+ }
+
+ /**
+ * Callback invoked when a reliable write transaction has been completed.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite}
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write
+ * transaction was executed successfully
+ */
+ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+ }
+
+ /**
+ * Callback reporting the RSSI for a remote device connection.
+ *
+ * This callback is triggered in response to the
+ * {@link BluetoothGatt#readRemoteRssi} function.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi}
+ * @param rssi The RSSI value for the remote device
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully
+ */
+ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+ }
+
+ /**
+ * Callback indicating the MTU for a given device connection has changed.
+ *
+ * This callback is triggered in response to the
+ * {@link BluetoothGatt#requestMtu} function, or in response to a connection
+ * event.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu}
+ * @param mtu The new MTU size
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully
+ */
+ public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 5ffceba5e11d..9ee739f04bf8 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -46,7 +46,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
private BluetoothAdapter mAdapter;
private IBluetoothGatt mService;
- private BluetoothGattServerCallback mCallback;
+ private BluetoothGattServerCallbackExt mCallback;
private Object mServerIfLock = new Object();
private int mServerIf;
@@ -59,8 +59,8 @@ public final class BluetoothGattServer implements BluetoothProfile {
/**
* Bluetooth GATT interface callbacks
*/
- private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
- new IBluetoothGattServerCallback.Stub() {
+ private final IBluetoothGattServerCallbackExt mBluetoothGattServerCallback =
+ new IBluetoothGattServerCallbackExt.Stub() {
/**
* Application interface registered - app is ready to go
* @hide
@@ -292,6 +292,42 @@ public final class BluetoothGattServer implements BluetoothProfile {
Log.w(TAG, "Unhandled exception: " + ex);
}
}
+
+ /**
+ * The PHY for a connection was updated
+ * @hide
+ */
+ public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
+ if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
+ + ", rxPHy=" + rxPhy);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (device == null) return;
+
+ try {
+ mCallback.onPhyUpdate(device, txPhy, rxPhy, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * The PHY for a connection was read
+ * @hide
+ */
+ public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
+ if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
+ + ", rxPHy=" + rxPhy);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (device == null) return;
+
+ try {
+ mCallback.onPhyRead(device, txPhy, rxPhy, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
};
/**
@@ -360,7 +396,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
* @return true, the callback will be called to notify success or failure,
* false on immediate error
*/
- /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
+ /*package*/ boolean registerCallback(BluetoothGattServerCallbackExt callback) {
if (DBG) Log.d(TAG, "registerCallback()");
if (mService == null) {
Log.e(TAG, "GATT service not available");
@@ -436,7 +472,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
*
* <p>The connection may not be established right away, but will be
* completed when the remote device is available. A
- * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
+ * {@link BluetoothGattServerCallbackExt#onConnectionStateChange} callback will be
* invoked when the connection state changes as a result of this function.
*
* <p>The autoConnect paramter determines whether to actively connect to
@@ -488,16 +524,58 @@ public final class BluetoothGattServer implements BluetoothProfile {
}
/**
+ * Set the preferred connection PHY for this app. Please note that this is just a
+ * recommendation, wether the PHY change will happen depends on other applications peferences,
+ * local and remote controller capabilities. Controller can override these settings.
+ * <p>
+ * {@link BluetoothGattServerCallbackExt#onPhyUpdate} will be triggered as a result of this call, even
+ * if no PHY change happens. It is also triggered when remote device updates the PHY.
+ *
+ * @param device The remote device to send this response to
+ * @param txPhy preferred transmitter PHY. Bitwise OR of any of
+ * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
+ * {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param rxPhy preferred receiver PHY. Bitwise OR of any of
+ * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
+ * {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
+ * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED},
+ * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8}
+ */
+ public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) {
+ try {
+ mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy,
+ phyOptions);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
+ * in {@link BluetoothGattServerCallbackExt#onPhyRead}
+ *
+ * @param device The remote device to send this response to
+ */
+ public void readPhy(BluetoothDevice device) {
+ try {
+ mService.serverReadPhy(mServerIf, device.getAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
* Send a response to a read or write request to a remote device.
*
* <p>This function must be invoked in when a remote read/write request
* is received by one of these callback methods:
*
* <ul>
- * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
- * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
- * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
- * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
+ * <li>{@link BluetoothGattServerCallbackExt#onCharacteristicReadRequest}
+ * <li>{@link BluetoothGattServerCallbackExt#onCharacteristicWriteRequest}
+ * <li>{@link BluetoothGattServerCallbackExt#onDescriptorReadRequest}
+ * <li>{@link BluetoothGattServerCallbackExt#onDescriptorWriteRequest}
* </ul>
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java
index 2afcf9a322bb..75ceb52c2d50 100644
--- a/core/java/android/bluetooth/BluetoothGattServerCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattServerCallback.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -20,141 +20,21 @@ import android.bluetooth.BluetoothDevice;
/**
* This abstract class is used to implement {@link BluetoothGattServer} callbacks.
+ * @deprecated please use {@link BluetoothGattServerCallbackExt}
*/
-public abstract class BluetoothGattServerCallback {
+public abstract class BluetoothGattServerCallback extends BluetoothGattServerCallbackExt {
/**
- * Callback indicating when a remote device has been connected or disconnected.
- *
- * @param device Remote device that has been connected or disconnected.
- * @param status Status of the connect or disconnect operation.
- * @param newState Returns the new connection state. Can be one of
- * {@link BluetoothProfile#STATE_DISCONNECTED} or
- * {@link BluetoothProfile#STATE_CONNECTED}
+ * @hide
*/
- public void onConnectionStateChange(BluetoothDevice device, int status,
- int newState) {
+ @Override
+ public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) {
}
/**
- * Indicates whether a local service has been added successfully.
- *
- * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service
- * was added successfully.
- * @param service The service that has been added
+ * @hide
*/
- public void onServiceAdded(int status, BluetoothGattService service) {
- }
-
- /**
- * A remote client has requested to read a local characteristic.
- *
- * <p>An application must call {@link BluetoothGattServer#sendResponse}
- * to complete the request.
- *
- * @param device The remote device that has requested the read operation
- * @param requestId The Id of the request
- * @param offset Offset into the value of the characteristic
- * @param characteristic Characteristic to be read
- */
- public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
- int offset, BluetoothGattCharacteristic characteristic) {
- }
-
- /**
- * A remote client has requested to write to a local characteristic.
- *
- * <p>An application must call {@link BluetoothGattServer#sendResponse}
- * to complete the request.
- *
- * @param device The remote device that has requested the write operation
- * @param requestId The Id of the request
- * @param characteristic Characteristic to be written to.
- * @param preparedWrite true, if this write operation should be queued for
- * later execution.
- * @param responseNeeded true, if the remote device requires a response
- * @param offset The offset given for the value
- * @param value The value the client wants to assign to the characteristic
- */
- public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
- BluetoothGattCharacteristic characteristic,
- boolean preparedWrite, boolean responseNeeded,
- int offset, byte[] value) {
- }
-
- /**
- * A remote client has requested to read a local descriptor.
- *
- * <p>An application must call {@link BluetoothGattServer#sendResponse}
- * to complete the request.
- *
- * @param device The remote device that has requested the read operation
- * @param requestId The Id of the request
- * @param offset Offset into the value of the characteristic
- * @param descriptor Descriptor to be read
- */
- public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
- int offset, BluetoothGattDescriptor descriptor) {
- }
-
- /**
- * A remote client has requested to write to a local descriptor.
- *
- * <p>An application must call {@link BluetoothGattServer#sendResponse}
- * to complete the request.
- *
- * @param device The remote device that has requested the write operation
- * @param requestId The Id of the request
- * @param descriptor Descriptor to be written to.
- * @param preparedWrite true, if this write operation should be queued for
- * later execution.
- * @param responseNeeded true, if the remote device requires a response
- * @param offset The offset given for the value
- * @param value The value the client wants to assign to the descriptor
- */
- public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
- BluetoothGattDescriptor descriptor,
- boolean preparedWrite, boolean responseNeeded,
- int offset, byte[] value) {
- }
-
- /**
- * Execute all pending write operations for this device.
- *
- * <p>An application must call {@link BluetoothGattServer#sendResponse}
- * to complete the request.
- *
- * @param device The remote device that has requested the write operations
- * @param requestId The Id of the request
- * @param execute Whether the pending writes should be executed (true) or
- * cancelled (false)
- */
- public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
- }
-
- /**
- * Callback invoked when a notification or indication has been sent to
- * a remote device.
- *
- * <p>When multiple notifications are to be sent, an application must
- * wait for this callback to be received before sending additional
- * notifications.
- *
- * @param device The remote device the notification has been sent to
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful
- */
- public void onNotificationSent(BluetoothDevice device, int status) {
- }
-
- /**
- * Callback indicating the MTU for a given device connection has changed.
- *
- * <p>This callback will be invoked if a remote client has requested to change
- * the MTU for a given connection.
- *
- * @param device The remote device that requested the MTU change
- * @param mtu The new MTU size
- */
- public void onMtuChanged(BluetoothDevice device, int mtu) {
+ @Override
+ public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) {
}
}
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallbackExt.java b/core/java/android/bluetooth/BluetoothGattServerCallbackExt.java
new file mode 100644
index 000000000000..455cce04c58c
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattServerCallbackExt.java
@@ -0,0 +1,187 @@
+/*
+ * 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 android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * This abstract class is used to implement {@link BluetoothGattServer} callbacks.
+ */
+public abstract class BluetoothGattServerCallbackExt {
+
+ /**
+ * Callback indicating when a remote device has been connected or disconnected.
+ *
+ * @param device Remote device that has been connected or disconnected.
+ * @param status Status of the connect or disconnect operation.
+ * @param newState Returns the new connection state. Can be one of
+ * {@link BluetoothProfile#STATE_DISCONNECTED} or
+ * {@link BluetoothProfile#STATE_CONNECTED}
+ */
+ public void onConnectionStateChange(BluetoothDevice device, int status,
+ int newState) {
+ }
+
+ /**
+ * Indicates whether a local service has been added successfully.
+ *
+ * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service
+ * was added successfully.
+ * @param service The service that has been added
+ */
+ public void onServiceAdded(int status, BluetoothGattService service) {
+ }
+
+ /**
+ * A remote client has requested to read a local characteristic.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the read operation
+ * @param requestId The Id of the request
+ * @param offset Offset into the value of the characteristic
+ * @param characteristic Characteristic to be read
+ */
+ public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
+ int offset, BluetoothGattCharacteristic characteristic) {
+ }
+
+ /**
+ * A remote client has requested to write to a local characteristic.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the write operation
+ * @param requestId The Id of the request
+ * @param characteristic Characteristic to be written to.
+ * @param preparedWrite true, if this write operation should be queued for
+ * later execution.
+ * @param responseNeeded true, if the remote device requires a response
+ * @param offset The offset given for the value
+ * @param value The value the client wants to assign to the characteristic
+ */
+ public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
+ BluetoothGattCharacteristic characteristic,
+ boolean preparedWrite, boolean responseNeeded,
+ int offset, byte[] value) {
+ }
+
+ /**
+ * A remote client has requested to read a local descriptor.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the read operation
+ * @param requestId The Id of the request
+ * @param offset Offset into the value of the characteristic
+ * @param descriptor Descriptor to be read
+ */
+ public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
+ int offset, BluetoothGattDescriptor descriptor) {
+ }
+
+ /**
+ * A remote client has requested to write to a local descriptor.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the write operation
+ * @param requestId The Id of the request
+ * @param descriptor Descriptor to be written to.
+ * @param preparedWrite true, if this write operation should be queued for
+ * later execution.
+ * @param responseNeeded true, if the remote device requires a response
+ * @param offset The offset given for the value
+ * @param value The value the client wants to assign to the descriptor
+ */
+ public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
+ BluetoothGattDescriptor descriptor,
+ boolean preparedWrite, boolean responseNeeded,
+ int offset, byte[] value) {
+ }
+
+ /**
+ * Execute all pending write operations for this device.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the write operations
+ * @param requestId The Id of the request
+ * @param execute Whether the pending writes should be executed (true) or
+ * cancelled (false)
+ */
+ public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
+ }
+
+ /**
+ * Callback invoked when a notification or indication has been sent to
+ * a remote device.
+ *
+ * <p>When multiple notifications are to be sent, an application must
+ * wait for this callback to be received before sending additional
+ * notifications.
+ *
+ * @param device The remote device the notification has been sent to
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful
+ */
+ public void onNotificationSent(BluetoothDevice device, int status) {
+ }
+
+ /**
+ * Callback indicating the MTU for a given device connection has changed.
+ *
+ * <p>This callback will be invoked if a remote client has requested to change
+ * the MTU for a given connection.
+ *
+ * @param device The remote device that requested the MTU change
+ * @param mtu The new MTU size
+ */
+ public void onMtuChanged(BluetoothDevice device, int mtu) {
+ }
+
+ /**
+ * Callback triggered as result of {@link BluetoothGattServer#setPreferredPhy}, or as a result
+ * of remote device changing the PHY.
+ *
+ * @param device The remote device
+ * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
+ * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
+ * @param status status of the operation
+ */
+ public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) {
+ }
+
+ /**
+ * Callback triggered as result of {@link BluetoothGattServer#readPhy}
+ *
+ * @param device The remote device that requested the PHY read
+ * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
+ * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
+ * @param status status of the operation
+ */
+ public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) {
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 7d8459cb48cf..ccab3cdf0b6f 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -50,7 +50,10 @@ public final class BluetoothMapClient implements BluetoothProfile {
public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
"android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
- /* Extras used in ACTION_MESSAGE_RECEIVED intent */
+ /* Extras used in ACTION_MESSAGE_RECEIVED intent.
+ * NOTE: HANDLE is only valid for a single session with the device. */
+ public static final String EXTRA_MESSAGE_HANDLE =
+ "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE";
public static final String EXTRA_SENDER_CONTACT_URI =
"android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI";
public static final String EXTRA_SENDER_CONTACT_NAME =
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 53fef2add344..76ca554e5984 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -104,6 +104,10 @@ interface IBluetooth
boolean isOffloadedFilteringSupported();
boolean isOffloadedScanBatchingSupported();
boolean isActivityAndEnergyReportingSupported();
+ boolean isLe2MPhySupported();
+ boolean isLeCodedPhySupported();
+ boolean isLeExtendedAdvertisingSupported();
+ boolean isLePeriodicAdvertisingSupported();
BluetoothActivityEnergyInfo reportActivityInfo();
/**
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index aa2291e072dd..33fedc718980 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -20,15 +20,20 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.bluetooth.le.ResultStorageDescriptor;
import android.os.ParcelUuid;
import android.os.WorkSource;
-import android.bluetooth.IBluetoothGattCallback;
-import android.bluetooth.IBluetoothGattServerCallback;
+import android.bluetooth.IBluetoothGattCallbackExt;
+import android.bluetooth.IBluetoothGattServerCallbackExt;
import android.bluetooth.le.IAdvertiserCallback;
+import android.bluetooth.le.IAdvertisingSetCallback;
+import android.bluetooth.le.IPeriodicAdvertisingCallback;
import android.bluetooth.le.IScannerCallback;
/**
@@ -53,10 +58,29 @@ interface IBluetoothGatt {
in AdvertiseSettings settings);
void stopMultiAdvertising(in int advertiserId);
- void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
+ void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData,
+ in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters,
+ in AdvertiseData periodicData, in IAdvertisingSetCallback callback);
+ void stopAdvertisingSet(in IAdvertisingSetCallback callback);
+
+ void enableAdverisingSet(in int advertiserId, in boolean enable);
+ void setAdvertisingData(in int advertiserId, in AdvertiseData data);
+ void setScanResponseData(in int advertiserId, in AdvertiseData data);
+ void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters);
+ void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters);
+ void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data);
+ void periodicAdvertisingEnable(in int advertiserId, in boolean enable);
+
+ void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback);
+ void unregisterSync(in IPeriodicAdvertisingCallback callback);
+
+ void registerClient(in ParcelUuid appId, in IBluetoothGattCallbackExt callback);
+
void unregisterClient(in int clientIf);
- void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport);
+ void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in int phy);
void clientDisconnect(in int clientIf, in String address);
+ void clientSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions);
+ void clientReadPhy(in int clientIf, in String address);
void refreshDevice(in int clientIf, in String address);
void discoverServices(in int clientIf, in String address);
void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq);
@@ -72,10 +96,12 @@ interface IBluetoothGatt {
void configureMTU(in int clientIf, in String address, in int mtu);
void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority);
- void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback);
+ void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallbackExt callback);
void unregisterServer(in int serverIf);
void serverConnect(in int serverIf, in String address, in boolean isDirect, in int transport);
void serverDisconnect(in int serverIf, in String address);
+ void serverSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions);
+ void serverReadPhy(in int clientIf, in String address);
void addService(in int serverIf, in BluetoothGattService service);
void removeService(in int serverIf, in int handle);
void clearServices(in int serverIf);
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallbackExt.aidl
index 72cb61827737..736f4b2b048f 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallbackExt.aidl
@@ -22,10 +22,12 @@ import android.bluetooth.BluetoothGattService;
* Callback definitions for interacting with BLE / GATT
* @hide
*/
-oneway interface IBluetoothGattCallback {
+oneway interface IBluetoothGattCallbackExt {
void onClientRegistered(in int status, in int clientIf);
void onClientConnectionState(in int status, in int clientIf,
in boolean connected, in String address);
+ void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status);
+ void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status);
void onSearchComplete(in String address, in List<BluetoothGattService> services, in int status);
void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value);
void onCharacteristicWrite(in String address, in int status, in int handle);
diff --git a/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/core/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl
index 1a924fb4e315..091ffb3fe987 100644
--- a/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * 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.
@@ -21,7 +21,7 @@ import android.bluetooth.BluetoothGattService;
* Callback definitions for interacting with BLE / GATT
* @hide
*/
-oneway interface IBluetoothGattServerCallback {
+oneway interface IBluetoothGattServerCallbackExt {
void onServerRegistered(in int status, in int serverIf);
void onServerConnectionState(in int status, in int serverIf,
in boolean connected, in String address);
@@ -40,4 +40,6 @@ oneway interface IBluetoothGattServerCallback {
void onExecuteWrite(in String address, in int transId, in boolean execWrite);
void onNotificationSent(in String address, in int status);
void onMtuChanged(in String address, in int mtu);
+ void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status);
+ void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status);
}
diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java
new file mode 100644
index 000000000000..1524022b1f0f
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertisingSet.java
@@ -0,0 +1,162 @@
+/*
+ * 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 android.bluetooth.le;
+
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothManager;
+import android.bluetooth.le.IAdvertisingSetCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * This class provides a way to control single Bluetooth LE advertising instance.
+ * <p>
+ * To get an instance of {@link AdvertisingSet}, call the
+ * {@link BluetoothLeAdvertiser#startAdvertisingSet} method.
+ * <p>
+ * <b>Note:</b> Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @see AdvertiseData
+ */
+public final class AdvertisingSet {
+ private static final String TAG = "AdvertisingSet";
+
+ private final IBluetoothGatt gatt;
+ private int advertiserId;
+
+ /* package */ AdvertisingSet(int advertiserId,
+ IBluetoothManager bluetoothManager) {
+ this.advertiserId = advertiserId;
+
+ try {
+ this.gatt = bluetoothManager.getBluetoothGatt();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
+ throw new IllegalStateException("Failed to get Bluetooth");
+ }
+ }
+
+ /* package */ void setAdvertiserId(int advertiserId) {
+ this.advertiserId = advertiserId;
+ }
+
+ /**
+ * Enables Advertising. This method returns immediately, the operation status is
+ * delivered
+ * through {@code callback.onAdvertisingEnabled()}.
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ */
+ public void enableAdvertising(boolean enable) {
+ try {
+ gatt.enableAdverisingSet(this.advertiserId, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Set/update data being Advertised. Make sure that data doesn't exceed the size limit for
+ * specified AdvertisingSetParameters. This method returns immediately, the operation status is
+ * delivered through {@code callback.onAdvertisingDataSet()}.
+ * <p>
+ * Advertising data must be empty if non-legacy scannable advertising is used.
+ */
+ public void setAdvertisingData(AdvertiseData data) {
+ try {
+ gatt.setAdvertisingData(this.advertiserId, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Set/update scan response data. Make sure that data doesn't exceed the size limit for
+ * specified AdvertisingSetParameters. This method returns immediately, the operation status
+ * is delivered through {@code callback.onScanResponseDataSet()}.
+ */
+ public void setScanResponseData(AdvertiseData data) {
+ try {
+ gatt.setScanResponseData(this.advertiserId, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Update advertising parameters associated with this AdvertisingSet. Must be called when
+ * advertising is not active. This method returns immediately, the operation status is delivered
+ * through {@code callback.onAdvertisingParametersUpdated}.
+ */
+ public void setAdvertisingParameters(AdvertisingSetParameters parameters) {
+ try {
+ gatt.setAdvertisingParameters(this.advertiserId, parameters);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Update periodic advertising parameters associated with this set. Must be called when
+ * periodic advertising is not enabled. This method returns immediately, the operation
+ * status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}.
+ */
+ public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) {
+ try {
+ gatt.setPeriodicAdvertisingParameters(this.advertiserId, parameters);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Used to set periodic advertising data, must be called after setPeriodicAdvertisingParameters,
+ * or after advertising was started with periodic advertising data set. This method returns
+ * immediately, the operation status is delivered through
+ * {@code callback.onPeriodicAdvertisingDataSet()}.
+ */
+ public void setPeriodicAdvertisingData(AdvertiseData data) {
+ try {
+ gatt.setPeriodicAdvertisingData(this.advertiserId, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Used to enable/disable periodic advertising. This method returns immediately, the operation
+ * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}.
+ */
+ public void periodicAdvertisingEnable(boolean enable) {
+ try {
+ gatt.periodicAdvertisingEnable(this.advertiserId, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Returns advertiserId associated with thsi advertising set.
+ *
+ * @hide
+ */
+ public int getAdvertiserId(){
+ return advertiserId;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/bluetooth/le/AdvertisingSetCallback.java b/core/java/android/bluetooth/le/AdvertisingSetCallback.java
new file mode 100644
index 000000000000..ceed8d9e3c60
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertisingSetCallback.java
@@ -0,0 +1,144 @@
+/*
+ * 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 android.bluetooth.le;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * Bluetooth LE advertising set callbacks, used to deliver advertising operation
+ * status.
+ */
+public abstract class AdvertisingSetCallback {
+
+ /**
+ * The requested operation was successful.
+ */
+ public static final int ADVERTISE_SUCCESS = 0;
+
+ /**
+ * Failed to start advertising as the advertise data to be broadcasted is too
+ * large.
+ */
+ public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1;
+
+ /**
+ * Failed to start advertising because no advertising instance is available.
+ */
+ public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2;
+
+ /**
+ * Failed to start advertising as the advertising is already started.
+ */
+ public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3;
+
+ /**
+ * Operation failed due to an internal error.
+ */
+ public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4;
+
+ /**
+ * This feature is not supported on this platform.
+ */
+ public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5;
+
+ /**
+ * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet}
+ * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertisingSet
+ * contains the started set and it is advertising. If error occured, advertisingSet is
+ * null, and status will be set to proper error code.
+ *
+ * @param advertisingSet The advertising set that was started or null if error.
+ * @param status Status of the operation.
+ */
+ public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int status) {}
+
+ /**
+ * Callback triggered in response to {@link BluetoothLeAdvertiser#stopAdvertisingSet}
+ * indicating advertising set is stopped.
+ *
+ * @param advertisingSet The advertising set.
+ */
+ public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {}
+
+ /**
+ * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} indicating
+ * result of the operation. If status is ADVERTISE_SUCCESS, then advertising set is advertising.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating
+ * result of the operation. If status is ADVERTISE_SUCCESS, then data was changed.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating
+ * result of the operation.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#setAdvertisingParameters}
+ * indicating result of the operation.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet,
+ int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingParameters}
+ * indicating result of the operation.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void
+ onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet,
+ int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingData}
+ * indicating result of the operation.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onPeriodicAdvertisingDataSet(AdvertisingSet advertisingSet,
+ int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#periodicAdvertisingEnable}
+ * indicating result of the operation.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onPeriodicAdvertisingEnable(AdvertisingSet advertisingSet, boolean enable,
+ int status) {}
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/logging/legacy/Util.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.aidl
index 99f71caf5d29..39034a001faa 100644
--- a/core/java/com/android/internal/logging/legacy/Util.java
+++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.aidl
@@ -13,13 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.internal.logging.legacy;
-/**
- * Created by cwren on 11/21/16.
- */
-public class Util {
- public static boolean debug() {
- return false;
- }
-}
+package android.bluetooth.le;
+
+parcelable AdvertisingSetParameters;
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
new file mode 100644
index 000000000000..03a01e171ba4
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
@@ -0,0 +1,409 @@
+/*
+ * 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 android.bluetooth.le;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The {@link AdvertisingSetParameters} provide a way to adjust advertising
+ * preferences for each
+ * Bluetooth LE advertising set. Use {@link AdvertisingSetParameters.Builder} to
+ * create an
+ * instance of this class.
+ */
+public final class AdvertisingSetParameters implements Parcelable {
+
+ /**
+ * 1M advertiser PHY.
+ */
+ public static final int PHY_LE_1M = 1;
+
+ /**
+ * 2M advertiser PHY.
+ */
+ public static final int PHY_LE_2M = 2;
+
+ /**
+ * LE Coded advertiser PHY.
+ */
+ public static final int PHY_LE_CODED = 3;
+
+ /**
+ * Advertise on low frequency, around every 1000ms. This is the default and
+ * preferred advertising mode as it consumes the least power.
+ */
+ public static final int INTERVAL_LOW = 1600;
+
+ /**
+ * Advertise on medium frequency, around every 250ms. This is balanced
+ * between advertising frequency and power consumption.
+ */
+ public static final int INTERVAL_MEDIUM = 400;
+
+ /**
+ * Perform high frequency, low latency advertising, around every 100ms. This
+ * has the highest power consumption and should not be used for continuous
+ * background advertising.
+ */
+ public static final int INTERVAL_HIGH = 160;
+
+ /**
+ * Minimum value for advertising interval.
+ */
+ public static final int INTERVAL_MIN = 160;
+
+ /**
+ * Maximum value for advertising interval.
+ */
+ public static final int INTERVAL_MAX = 16777215;
+
+ /**
+ * Advertise using the lowest transmission (TX) power level. Low transmission
+ * power can be used to restrict the visibility range of advertising packets.
+ */
+ public static final int TX_POWER_ULTRA_LOW = -21;
+
+ /**
+ * Advertise using low TX power level.
+ */
+ public static final int TX_POWER_LOW = -15;
+
+ /**
+ * Advertise using medium TX power level.
+ */
+ public static final int TX_POWER_MEDIUM = -7;
+
+ /**
+ * Advertise using high TX power level. This corresponds to largest visibility
+ * range of the advertising packet.
+ */
+ public static final int TX_POWER_HIGH = 1;
+
+ /**
+ * Minimum value for TX power.
+ */
+ public static final int TX_POWER_MIN = -127;
+
+ /**
+ * Maximum value for TX power.
+ */
+ public static final int TX_POWER_MAX = 1;
+
+ /**
+ * The maximum limited advertisement duration as specified by the Bluetooth
+ * SIG
+ */
+ private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
+
+ private final boolean isLegacy;
+ private final boolean isAnonymous;
+ private final boolean includeTxPower;
+ private final int primaryPhy;
+ private final int secondaryPhy;
+ private final boolean connectable;
+ private final int interval;
+ private final int txPowerLevel;
+ private final int timeoutMillis;
+
+ private AdvertisingSetParameters(boolean connectable, boolean isLegacy,
+ boolean isAnonymous, boolean includeTxPower,
+ int primaryPhy, int secondaryPhy,
+ int interval, int txPowerLevel,
+ int timeoutMillis) {
+ this.connectable = connectable;
+ this.isLegacy = isLegacy;
+ this.isAnonymous = isAnonymous;
+ this.includeTxPower = includeTxPower;
+ this.primaryPhy = primaryPhy;
+ this.secondaryPhy = secondaryPhy;
+ this.interval = interval;
+ this.txPowerLevel = txPowerLevel;
+ this.timeoutMillis = timeoutMillis;
+ }
+
+ private AdvertisingSetParameters(Parcel in) {
+ connectable = in.readInt() != 0 ? true : false;
+ isLegacy = in.readInt() != 0 ? true : false;
+ isAnonymous = in.readInt() != 0 ? true : false;
+ includeTxPower = in.readInt() != 0 ? true : false;
+ primaryPhy = in.readInt();
+ secondaryPhy = in.readInt();
+ interval = in.readInt();
+ txPowerLevel = in.readInt();
+ timeoutMillis = in.readInt();
+ }
+
+ /**
+ * Returns whether the advertisement will be connectable.
+ */
+ public boolean isConnectable() { return connectable; }
+
+ /**
+ * Returns whether the legacy advertisement will be used.
+ */
+ public boolean isLegacy() { return isLegacy; }
+
+ /**
+ * Returns whether the advertisement will be anonymous.
+ */
+ public boolean isAnonymous() { return isAnonymous; }
+
+ /**
+ * Returns whether the TX Power will be included.
+ */
+ public boolean includeTxPower() { return includeTxPower; }
+
+ /**
+ * Returns the primary advertising phy.
+ */
+ public int getPrimaryPhy() { return primaryPhy; }
+
+ /**
+ * Returns the secondary advertising phy.
+ */
+ public int getSecondaryPhy() { return secondaryPhy; }
+
+ /**
+ * Returns the advertising interval.
+ */
+ public int getInterval() { return interval; }
+
+ /**
+ * Returns the TX power level for advertising.
+ */
+ public int getTxPowerLevel() { return txPowerLevel; }
+
+ /**
+ * Returns the advertising time limit in milliseconds.
+ */
+ public int getTimeout() { return timeoutMillis; }
+
+ @Override
+ public String toString() {
+ return "AdvertisingSetParameters [connectable=" + connectable
+ + ", isLegacy=" + isLegacy
+ + ", isAnonymous=" + isAnonymous
+ + ", includeTxPower=" + includeTxPower
+ + ", primaryPhy=" + primaryPhy
+ + ", secondaryPhy=" + secondaryPhy
+ + ", interval=" + interval
+ + ", txPowerLevel=" + txPowerLevel
+ + ", timeoutMillis=" + timeoutMillis + "]";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(connectable ? 1 : 0);
+ dest.writeInt(isLegacy ? 1 : 0);
+ dest.writeInt(isAnonymous ? 1 : 0);
+ dest.writeInt(includeTxPower ? 1 : 0);
+ dest.writeInt(primaryPhy);
+ dest.writeInt(secondaryPhy);
+ dest.writeInt(interval);
+ dest.writeInt(txPowerLevel);
+ dest.writeInt(timeoutMillis);
+ }
+
+ public static final Parcelable.Creator<AdvertisingSetParameters> CREATOR =
+ new Creator<AdvertisingSetParameters>() {
+ @Override
+ public AdvertisingSetParameters[] newArray(int size) {
+ return new AdvertisingSetParameters[size];
+ }
+
+ @Override
+ public AdvertisingSetParameters createFromParcel(Parcel in) {
+ return new AdvertisingSetParameters(in);
+ }
+ };
+
+ /**
+ * Builder class for {@link AdvertisingSetParameters}.
+ */
+ public static final class Builder {
+
+ private boolean connectable = true;
+ private boolean isLegacy = false;
+ private boolean isAnonymous = false;
+ private boolean includeTxPower = false;
+ private int primaryPhy = PHY_LE_1M;
+ private int secondaryPhy = PHY_LE_1M;
+ private int interval = INTERVAL_LOW;
+ private int txPowerLevel = TX_POWER_MEDIUM;
+ private int timeoutMillis = 0;
+
+ /**
+ * Set whether the advertisement type should be connectable or
+ * non-connectable.
+ * Legacy advertisements can be both connectable and scannable. Other
+ * advertisements can be connectable only if not scannable.
+ * @param connectable Controls whether the advertisment type will be
+ * connectable (true) or non-connectable (false).
+ */
+ public Builder setConnectable(boolean connectable) {
+ this.connectable = connectable;
+ return this;
+ }
+
+ /**
+ * When set to true, advertising set will advertise 4.x Spec compliant
+ * advertisements.
+ *
+ * @param isLegacy wether legacy advertising mode should be used.
+ */
+ public Builder setLegacyMode(boolean isLegacy) {
+ this.isLegacy = isLegacy;
+ return this;
+ }
+
+ /**
+ * Set wether advertiser address should be ommited from all packets. If this
+ * mode is used, periodic advertising can't be enabled for this set.
+ *
+ * This is used only if legacy mode is not used.
+ *
+ * @param isAnonymous wether anonymous advertising should be used.
+ */
+ public Builder setAnonymouus(boolean isAnonymous) {
+ this.isAnonymous = isAnonymous;
+ return this;
+ }
+
+ /**
+ * Set wether TX power should be included in the extended header.
+ *
+ * This is used only if legacy mode is not used.
+ *
+ * @param includeTxPower wether TX power should be included in extended
+ * header
+ */
+ public Builder setIncludeTxPower(boolean includeTxPower) {
+ this.includeTxPower = includeTxPower;
+ return this;
+ }
+
+ /**
+ * Set the primary physical channel used for this advertising set.
+ *
+ * This is used only if legacy mode is not used.
+ *
+ * @param primaryPhy Primary advertising physical channel, can only be
+ * {@link AdvertisingSetParameters#PHY_LE_1M} or
+ * {@link AdvertisingSetParameters#PHY_LE_CODED}.
+ * @throws IllegalArgumentException If the primaryPhy is invalid.
+ */
+ public Builder setPrimaryPhy(int primaryPhy) {
+ if (primaryPhy != PHY_LE_1M && primaryPhy != PHY_LE_CODED) {
+ throw new IllegalArgumentException("bad primaryPhy " + primaryPhy);
+ }
+ this.primaryPhy = primaryPhy;
+ return this;
+ }
+
+ /**
+ * Set the secondary physical channel used for this advertising set.
+ *
+ * This is used only if legacy mode is not used.
+ *
+ * @param secondaryPhy Secondary advertising physical channel, can only be
+ * one of {@link AdvertisingSetParameters#PHY_LE_1M},
+ * {@link AdvertisingSetParameters#PHY_LE_2M} or
+ * {@link AdvertisingSetParameters#PHY_LE_CODED}.
+ * @throws IllegalArgumentException If the secondaryPhy is invalid.
+ */
+ public Builder setSecondaryPhy(int secondaryPhy) {
+ if (secondaryPhy != PHY_LE_1M && secondaryPhy !=PHY_LE_2M &&
+ secondaryPhy != PHY_LE_CODED) {
+ throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy);
+ }
+ this.secondaryPhy = secondaryPhy;
+ return this;
+ }
+
+ /**
+ * Set advertising interval.
+ *
+ * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid
+ * range is from 160 (100ms) to 16777215 (10,485.759375 s).
+ * Recommended values are:
+ * {@link AdvertisingSetParameters#INTERVAL_LOW},
+ * {@link AdvertisingSetParameters#INTERVAL_MEDIUM}, or
+ * {@link AdvertisingSetParameters#INTERVAL_HIGH}.
+ * @throws IllegalArgumentException If the interval is invalid.
+ */
+ public Builder setInterval(int interval) {
+ if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
+ throw new IllegalArgumentException("unknown interval " + interval);
+ }
+ this.interval = interval;
+ return this;
+ }
+
+ /**
+ * Set the transmission power level for the advertising.
+ * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in
+ * dBm. The valid range is [-127, 1] Recommended values are:
+ * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW},
+ * {@link AdvertisingSetParameters#TX_POWER_LOW},
+ * {@link AdvertisingSetParameters#TX_POWER_MEDIUM}, or
+ * {@link AdvertisingSetParameters#TX_POWER_HIGH}.
+ *
+ * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
+ */
+ public Builder setTxPowerLevel(int txPowerLevel) {
+ if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) {
+ throw new IllegalArgumentException("unknown txPowerLevel " +
+ txPowerLevel);
+ }
+ this.txPowerLevel = txPowerLevel;
+ return this;
+ }
+
+ /**
+ * Limit advertising to a given amount of time.
+ * @param timeoutMillis Advertising time limit. May not exceed 180000
+ * milliseconds. A value of 0 will disable the time limit.
+ * @throws IllegalArgumentException If the provided timeout is over 180000
+ * ms.
+ */
+ public Builder setTimeout(int timeoutMillis) {
+ if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) {
+ throw new IllegalArgumentException("timeoutMillis invalid (must be 0-" +
+ LIMITED_ADVERTISING_MAX_MILLIS +
+ " milliseconds)");
+ }
+ this.timeoutMillis = timeoutMillis;
+ return this;
+ }
+
+ /**
+ * Build the {@link AdvertisingSetParameters} object.
+ */
+ public AdvertisingSetParameters build() {
+ return new AdvertisingSetParameters(connectable, isLegacy, isAnonymous,
+ includeTxPower, primaryPhy,
+ secondaryPhy, interval, txPowerLevel,
+ timeoutMillis);
+ }
+ }
+} \ No newline at end of file
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 94d03e533dff..e03c9477a6a3 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -62,6 +62,9 @@ public final class BluetoothLeAdvertiser {
private BluetoothAdapter mBluetoothAdapter;
private final Map<AdvertiseCallback, AdvertiseCallbackWrapper>
mLeAdvertisers = new HashMap<AdvertiseCallback, AdvertiseCallbackWrapper>();
+ private final Map<AdvertisingSetCallback, IAdvertisingSetCallback>
+ advertisingSetCallbackWrappers = new HashMap<>();
+ private final Map<Integer, AdvertisingSet> advertisingSets = new HashMap<>();
/**
* Use BluetoothAdapter.getLeAdvertiser() instead.
@@ -156,6 +159,93 @@ public final class BluetoothLeAdvertiser {
}
/**
+ * Creates a new advertising set. If operation succeed, device will start advertising. This
+ * method returns immediately, the operation status is delivered through
+ * {@code callback.onNewAdvertisingSet()}.
+ * <p>
+ * @param parameters advertising set parameters.
+ * @param advertiseData Advertisement data to be broadcasted.
+ * @param scanResponse Scan response associated with the advertisement data.
+ * @param periodicData Periodic advertising data.
+ * @param callback Callback for advertising set.
+ */
+ public void startAdvertisingSet(AdvertisingSetParameters parameters,
+ AdvertiseData advertiseData, AdvertiseData scanResponse,
+ PeriodicAdvertisingParameters periodicParameters,
+ AdvertiseData periodicData, AdvertisingSetCallback callback) {
+ startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
+ periodicData, callback, new Handler(Looper.getMainLooper()));
+ }
+
+ /**
+ * Creates a new advertising set. If operation succeed, device will start advertising. This
+ * method returns immediately, the operation status is delivered through
+ * {@code callback.onNewAdvertisingSet()}.
+ * <p>
+ * @param parameters advertising set parameters.
+ * @param advertiseData Advertisement data to be broadcasted.
+ * @param scanResponse Scan response associated with the advertisement data.
+ * @param periodicData Periodic advertising data.
+ * @param callback Callback for advertising set.
+ * @param handler thread upon which the callbacks will be invoked.
+ */
+ public void startAdvertisingSet(AdvertisingSetParameters parameters,
+ AdvertiseData advertiseData, AdvertiseData scanResponse,
+ PeriodicAdvertisingParameters periodicParameters,
+ AdvertiseData periodicData, AdvertisingSetCallback callback,
+ Handler handler) {
+ BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
+
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
+ throw new IllegalStateException("Failed to get Bluetooth");
+ }
+
+ IAdvertisingSetCallback wrapped = wrap(callback, handler);
+ advertisingSetCallbackWrappers.put(callback, wrapped);
+
+ try {
+ gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
+ periodicData, wrapped);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to start advertising set - ", e);
+ throw new IllegalStateException("Failed to start advertising set");
+ }
+ }
+
+ /**
+ * Used to dispose of a {@link AdvertisingSet} object, obtained with {@link
+ * BluetoothLeAdvertiser#startAdvertisingSet}.
+ */
+ public void stopAdvertisingSet(AdvertisingSetCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+
+ IAdvertisingSetCallback wrapped = advertisingSetCallbackWrappers.remove(callback);
+ if (wrapped == null) {
+ throw new IllegalArgumentException(
+ "callback does not represent valid registered callback.");
+ }
+
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ gatt.stopAdvertisingSet(wrapped);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to stop advertising - ", e);
+ throw new IllegalStateException("Failed to stop advertising");
+ }
+ }
+
+ /**
* Cleans up advertisers. Should be called when bluetooth is down.
*
* @hide
@@ -219,6 +309,110 @@ public final class BluetoothLeAdvertiser {
return array == null ? 0 : array.length;
}
+ IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) {
+ return new IAdvertisingSetCallback.Stub() {
+ public void onAdvertisingSetStarted(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
+ callback.onAdvertisingSetStarted(null, status);
+ advertisingSetCallbackWrappers.remove(callback);
+ return;
+ }
+
+ AdvertisingSet advertisingSet =
+ new AdvertisingSet(advertiserId, mBluetoothManager);
+ advertisingSets.put(advertiserId, advertisingSet);
+ callback.onAdvertisingSetStarted(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onAdvertisingSetStopped(int advertiserId) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onAdvertisingSetStopped(advertisingSet);
+ advertisingSets.remove(advertiserId);
+ advertisingSetCallbackWrappers.remove(callback);
+ }
+ });
+ }
+
+ public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onAdvertisingEnabled(advertisingSet, enabled, status);
+ }
+ });
+ }
+
+ public void onAdvertisingDataSet(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onAdvertisingDataSet(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onScanResponseDataSet(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onScanResponseDataSet(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onAdvertisingParametersUpdated(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onAdvertisingParametersUpdated(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onPeriodicAdvertisingParametersUpdated(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onPeriodicAdvertisingDataSet(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onPeriodicAdvertisingDataSet(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onPeriodicAdvertisingEnable(int advertiserId, boolean enable, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onPeriodicAdvertisingEnable(advertisingSet, enable, status);
+ }
+ });
+ }
+ };
+ }
+
/**
* Bluetooth GATT interface callbacks for advertising.
*/
diff --git a/core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl
new file mode 100644
index 000000000000..4b0a111fa3d8
--- /dev/null
+++ b/core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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 android.bluetooth.le;
+
+/**
+ * Callback definitions for interacting with Advertiser
+ * @hide
+ */
+oneway interface IAdvertisingSetCallback {
+ void onAdvertisingSetStarted(in int advertiserId, in int status);
+ void onAdvertisingSetStopped(in int advertiserId);
+ void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status);
+ void onAdvertisingDataSet(in int advertiserId, in int status);
+ void onScanResponseDataSet(in int advertiserId, in int status);
+ void onAdvertisingParametersUpdated(in int advertiserId, in int status);
+ void onPeriodicAdvertisingParametersUpdated(in int advertiserId, in int status);
+ void onPeriodicAdvertisingDataSet(in int advertiserId, in int status);
+ void onPeriodicAdvertisingEnable(in int advertiserId, in boolean enable, in int status);
+}
diff --git a/core/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl b/core/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl
new file mode 100644
index 000000000000..a76c54d4ab49
--- /dev/null
+++ b/core/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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 android.bluetooth.le;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.PeriodicAdvertisingReport;
+
+/**
+ * Callback definitions for interacting with Periodic Advertising
+ * @hide
+ */
+oneway interface IPeriodicAdvertisingCallback {
+
+ void onSyncEstablished(in int syncHandle, in BluetoothDevice device, in int advertisingSid,
+ in int skip, in int timeout, in int status);
+ void onPeriodicAdvertisingReport(in PeriodicAdvertisingReport report);
+ void onSyncLost(in int syncHandle);
+}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java
new file mode 100644
index 000000000000..6616231bcdf8
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java
@@ -0,0 +1,77 @@
+/*
+ * 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 android.bluetooth.le;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * Bluetooth LE periodic advertising callbacks, used to deliver periodic
+ * advertising operation status.
+ *
+ * @see PeriodicAdvertisingManager#createSync
+ */
+public abstract class PeriodicAdvertisingCallback {
+
+ /**
+ * The requested operation was successful.
+ *
+ * @hide
+ */
+ public static final int SYNC_SUCCESS = 0;
+
+ /**
+ * Sync failed to be established because remote device did not respond.
+ */
+ public static final int SYNC_NO_RESPONSE = 1;
+
+ /**
+ * Sync failed to be established because controller can't support more syncs.
+ */
+ public static final int SYNC_NO_RESOURCES = 2;
+
+
+ /**
+ * Callback when synchronization was established.
+ *
+ * @param syncHandle handle used to identify this synchronization.
+ * @param device remote device.
+ * @param advertisingSid synchronized advertising set id.
+ * @param skip The number of periodic advertising packets that can be skipped
+ * after a successful receive in force. @see PeriodicAdvertisingManager#createSync
+ * @param timeout Synchronization timeout for the periodic advertising in force. One
+ * unit is 10ms. @see PeriodicAdvertisingManager#createSync
+ * @param timeout
+ * @param status operation status.
+ */
+ public void onSyncEstablished(int syncHandle, BluetoothDevice device,
+ int advertisingSid, int skip, int timeout,
+ int status) {}
+
+ /**
+ * Callback when periodic advertising report is received.
+ *
+ * @param report periodic advertising report.
+ */
+ public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {}
+
+ /**
+ * Callback when periodic advertising synchronization was lost.
+ *
+ * @param syncHandle handle used to identify this synchronization.
+ */
+ public void onSyncLost(int syncHandle) {}
+}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
new file mode 100644
index 000000000000..12c8a8c8ffd4
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
@@ -0,0 +1,237 @@
+/*
+ * 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 android.bluetooth.le;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * This class provides methods to perform periodic advertising related
+ * operations. An application can register for periodic advertisements using
+ * {@link PeriodicAdvertisingManager#registerSync}.
+ * <p>
+ * Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an
+ * instance of {@link PeriodicAdvertisingManager}.
+ * <p>
+ * <b>Note:</b> Most of the methods here require
+ * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ */
+public final class PeriodicAdvertisingManager {
+
+ private static final String TAG = "PeriodicAdvertisingManager";
+
+ private static final int SKIP_MIN = 0;
+ private static final int SKIP_MAX = 499;
+ private static final int TIMEOUT_MIN = 10;
+ private static final int TIMEOUT_MAX = 16384;
+
+ private static final int SYNC_STARTING = -1;
+
+ private final IBluetoothManager mBluetoothManager;
+ private BluetoothAdapter mBluetoothAdapter;
+
+ /* maps callback, to callback wrapper and sync handle */
+ Map<PeriodicAdvertisingCallback,
+ IPeriodicAdvertisingCallback /* callbackWrapper */> callbackWrappers;
+
+ /**
+ * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
+ *
+ * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management.
+ * @hide
+ */
+ public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) {
+ mBluetoothManager = bluetoothManager;
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ callbackWrappers = new IdentityHashMap<>();
+ }
+
+ /**
+ * Synchronize with periodic advertising pointed to by the {@code scanResult}.
+ * The {@code scanResult} used must contain a valid advertisingSid. First
+ * call to registerSync will use the {@code skip} and {@code timeout} provided.
+ * Subsequent calls from other apps, trying to sync with same set will reuse
+ * existing sync, thus {@code skip} and {@code timeout} values will not take
+ * effect. The values in effect will be returned in
+ * {@link PeriodicAdvertisingCallback#onSyncEstablished}.
+ *
+ * @param scanResult Scan result containing advertisingSid.
+ * @param skip The number of periodic advertising packets that can be skipped
+ * after a successful receive. Must be between 0 and 499.
+ * @param timeout Synchronization timeout for the periodic advertising. One
+ * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s).
+ * @param callback Callback used to deliver all operations status.
+ * @throws IllegalArgumentException if {@code scanResult} is null or {@code
+ * skip} is invalid or {@code timeout} is invalid or {@code callback} is null.
+ */
+ public void registerSync(ScanResult scanResult, int skip, int timeout,
+ PeriodicAdvertisingCallback callback) {
+ registerSync(scanResult, skip, timeout, callback, null);
+ }
+
+ /**
+ * Synchronize with periodic advertising pointed to by the {@code scanResult}.
+ * The {@code scanResult} used must contain a valid advertisingSid. First
+ * call to registerSync will use the {@code skip} and {@code timeout} provided.
+ * Subsequent calls from other apps, trying to sync with same set will reuse
+ * existing sync, thus {@code skip} and {@code timeout} values will not take
+ * effect. The values in effect will be returned in
+ * {@link PeriodicAdvertisingCallback#onSyncEstablished}.
+ *
+ * @param scanResult Scan result containing advertisingSid.
+ * @param skip The number of periodic advertising packets that can be skipped
+ * after a successful receive. Must be between 0 and 499.
+ * @param timeout Synchronization timeout for the periodic advertising. One
+ * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s).
+ * @param callback Callback used to deliver all operations status.
+ * @param handler thread upon which the callbacks will be invoked.
+ * @throws IllegalArgumentException if {@code scanResult} is null or {@code
+ * skip} is invalid or {@code timeout} is invalid or {@code callback} is null.
+ */
+ public void registerSync(ScanResult scanResult, int skip, int timeout,
+ PeriodicAdvertisingCallback callback, Handler handler) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback can't be null");
+ }
+
+ if (scanResult == null) {
+ throw new IllegalArgumentException("scanResult can't be null");
+ }
+
+ if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) {
+ throw new IllegalArgumentException("scanResult must contain a valid sid");
+ }
+
+ if (skip < SKIP_MIN || skip > SKIP_MAX) {
+ throw new IllegalArgumentException(
+ "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX);
+ }
+
+ if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) {
+ throw new IllegalArgumentException(
+ "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX);
+ }
+
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
+ callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(),
+ skip, timeout,
+ PeriodicAdvertisingCallback.SYNC_NO_RESOURCES);
+ return;
+ }
+
+ if (handler == null)
+ handler = new Handler(Looper.getMainLooper());
+
+ IPeriodicAdvertisingCallback wrapped = wrap(callback, handler);
+ callbackWrappers.put(callback, wrapped);
+
+ try {
+ gatt.registerSync(scanResult, skip, timeout, wrapped);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register sync - ", e);
+ return;
+ }
+ }
+
+ /**
+ * Cancel pending attempt to create sync, or terminate existing sync.
+ *
+ * @param callback Callback used to deliver all operations status.
+ * @throws IllegalArgumentException if {@code callback} is null, or not a properly
+ * registered callback.
+ */
+ public void unregisterSync(PeriodicAdvertisingCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback can't be null");
+ }
+
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
+ return;
+ }
+
+ IPeriodicAdvertisingCallback wrapper = callbackWrappers.remove(callback);
+ if (wrapper == null) {
+ throw new IllegalArgumentException("callback was not properly registered");
+ }
+
+ try {
+ gatt.unregisterSync(wrapper);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to cancel sync creation - ", e);
+ return;
+ }
+ }
+
+ private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, Handler handler) {
+ return new IPeriodicAdvertisingCallback.Stub() {
+ public void onSyncEstablished(int syncHandle, BluetoothDevice device,
+ int advertisingSid, int skip, int timeout, int status) {
+
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onSyncEstablished(syncHandle, device, advertisingSid, skip, timeout,
+ status);
+
+ if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) {
+ // App can still unregister the sync until notified it failed. Remove callback
+ // after app was notifed.
+ callbackWrappers.remove(callback);
+ }
+ }
+ });
+ }
+
+ public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onPeriodicAdvertisingReport(report);
+ }
+ });
+ }
+
+ public void onSyncLost(int syncHandle) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onSyncLost(syncHandle);
+ // App can still unregister the sync until notified it's lost. Remove callback after
+ // app was notifed.
+ callbackWrappers.remove(callback);
+ }
+ });
+ }
+ };
+ }
+}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl
new file mode 100644
index 000000000000..f4bea22a12f2
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.bluetooth.le;
+
+parcelable PeriodicAdvertisingParameters;
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
new file mode 100644
index 000000000000..ebc92bd0bcf8
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
@@ -0,0 +1,134 @@
+/*
+ * 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 android.bluetooth.le;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The {@link PeriodicAdvertisingParameters} provide a way to adjust periodic
+ * advertising preferences for each Bluetooth LE advertising set. Use {@link
+ * AdvertisingSetParameters.Builder} to create an instance of this class.
+ */
+public final class PeriodicAdvertisingParameters implements Parcelable {
+
+ private static final int INTERVAL_MAX = 80;
+ private static final int INTERVAL_MIN = 65519;
+
+ private final boolean enable;
+ private final boolean includeTxPower;
+ private final int interval;
+
+ private PeriodicAdvertisingParameters(boolean enable, boolean includeTxPower, int interval) {
+ this.enable = enable;
+ this.includeTxPower = includeTxPower;
+ this.interval = interval;
+ }
+
+ private PeriodicAdvertisingParameters(Parcel in) {
+ enable = in.readInt() != 0 ? true : false;
+ includeTxPower = in.readInt() != 0 ? true : false;
+ interval = in.readInt();
+ }
+
+ /**
+ * Returns whether the periodic advertising shall be enabled.
+ */
+ public boolean getEnable() { return enable; }
+
+ /**
+ * Returns whether the TX Power will be included.
+ */
+ public boolean getIncludeTxPower() { return includeTxPower; }
+
+ /**
+ * Returns the periodic advertising interval, in 1.25ms unit.
+ * Valid values are from 80 (100ms) to 65519 (81.89875s).
+ */
+ public int getInterval() { return interval; }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(enable ? 1 : 0);
+ dest.writeInt(includeTxPower ? 1 : 0);
+ dest.writeInt(interval);
+ }
+
+ public static final Parcelable
+ .Creator<PeriodicAdvertisingParameters> CREATOR =
+ new Creator<PeriodicAdvertisingParameters>() {
+ @Override
+ public PeriodicAdvertisingParameters[] newArray(int size) {
+ return new PeriodicAdvertisingParameters[size];
+ }
+
+ @Override
+ public PeriodicAdvertisingParameters createFromParcel(Parcel in) {
+ return new PeriodicAdvertisingParameters(in);
+ }
+ };
+
+ public static final class Builder {
+ private boolean includeTxPower = false;
+ private boolean enable = false;
+ private int interval = INTERVAL_MAX;
+
+ /**
+ * Set wether the Periodic Advertising should be enabled for this set.
+ */
+ public Builder setEnable(boolean enable) {
+ this.enable = enable;
+ return this;
+ }
+
+ /**
+ * Whether the transmission power level should be included in the periodic
+ * packet.
+ */
+ public Builder setIncludeTxPower(boolean includeTxPower) {
+ this.includeTxPower = includeTxPower;
+ return this;
+ }
+
+ /**
+ * Set advertising interval for periodic advertising, in 1.25ms unit.
+ * Valid values are from 80 (100ms) to 65519 (81.89875s).
+ * Value from range [interval, interval+20ms] will be picked as the actual value.
+ * @throws IllegalArgumentException If the interval is invalid.
+ */
+ public Builder setInterval(int interval) {
+ if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
+ throw new IllegalArgumentException("Invalid interval (must be " + INTERVAL_MIN +
+ "-" + INTERVAL_MAX + ")");
+ }
+ this.interval = interval;
+ return this;
+ }
+
+ /**
+ * Build the {@link AdvertisingSetParameters} object.
+ */
+ public PeriodicAdvertisingParameters build() {
+ return new PeriodicAdvertisingParameters(enable, includeTxPower, interval);
+ }
+ }
+}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl
new file mode 100644
index 000000000000..547d09611fdb
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.bluetooth.le;
+
+parcelable PeriodicAdvertisingReport;
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
new file mode 100644
index 000000000000..3ff4ca580069
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
@@ -0,0 +1,184 @@
+/*
+ * 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 android.bluetooth.le;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * PeriodicAdvertisingReport for Bluetooth LE synchronized advertising.
+ */
+public final class PeriodicAdvertisingReport implements Parcelable {
+
+ /**
+ * The data returned is complete
+ */
+ public static final int DATA_COMPLETE = 0;
+
+ /**
+ * The data returned is incomplete. The controller was unsuccessfull to
+ * receive all chained packets, returning only partial data.
+ */
+ public static final int DATA_INCOMPLETE_TRUNCATED = 2;
+
+ private int syncHandle;
+ private int txPower;
+ private int rssi;
+ private int dataStatus;
+
+ // periodic advertising data.
+ @Nullable
+ private ScanRecord data;
+
+ // Device timestamp when the result was last seen.
+ private long timestampNanos;
+
+ /**
+ * Constructor of periodic advertising result.
+ *
+ */
+ public PeriodicAdvertisingReport(int syncHandle, int txPower, int rssi,
+ int dataStatus, ScanRecord data) {
+ this.syncHandle = syncHandle;
+ this.txPower = txPower;
+ this.rssi = rssi;
+ this.dataStatus = dataStatus;
+ this.data = data;
+ }
+
+ private PeriodicAdvertisingReport(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(syncHandle);
+ dest.writeLong(txPower);
+ dest.writeInt(rssi);
+ dest.writeInt(dataStatus);
+ if (data != null) {
+ dest.writeInt(1);
+ dest.writeByteArray(data.getBytes());
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ private void readFromParcel(Parcel in) {
+ syncHandle = in.readInt();
+ txPower = in.readInt();
+ rssi = in.readInt();
+ dataStatus = in.readInt();
+ if (in.readInt() == 1) {
+ data = ScanRecord.parseFromBytes(in.createByteArray());
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the synchronization handle.
+ */
+ public int getSyncHandle() {
+ return syncHandle;
+ }
+
+ /**
+ * Returns the transmit power in dBm. The valid range is [-127, 126]. Value
+ * of 127 means information was not available.
+ */
+ public int getTxPower() {
+ return txPower;
+ }
+
+ /**
+ * Returns the received signal strength in dBm. The valid range is [-127, 20].
+ */
+ public int getRssi() {
+ return rssi;
+ }
+
+ /**
+ * Returns the data status. Can be one of {@link PeriodicAdvertisingReport#DATA_COMPLETE}
+ * or {@link PeriodicAdvertisingReport#DATA_INCOMPLETE_TRUNCATED}.
+ */
+ public int getDataStatus() {
+ return dataStatus;
+ }
+
+ /**
+ * Returns the data contained in this periodic advertising report.
+ */
+ @Nullable
+ public ScanRecord getData() {
+ return data;
+ }
+
+ /**
+ * Returns timestamp since boot when the scan record was observed.
+ */
+ public long getTimestampNanos() {
+ return timestampNanos;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(syncHandle, txPower, rssi, dataStatus, data, timestampNanos);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ PeriodicAdvertisingReport other = (PeriodicAdvertisingReport) obj;
+ return (syncHandle == other.syncHandle) &&
+ (txPower == other.txPower) &&
+ (rssi == other.rssi) &&
+ (dataStatus == other.dataStatus) &&
+ Objects.equals(data, other.data) &&
+ (timestampNanos == other.timestampNanos);
+ }
+
+ @Override
+ public String toString() {
+ return "PeriodicAdvertisingReport{syncHandle=" + syncHandle +
+ ", txPower=" + txPower + ", rssi=" + rssi + ", dataStatus=" + dataStatus +
+ ", data=" + Objects.toString(data) + ", timestampNanos=" + timestampNanos + '}';
+ }
+
+ public static final Parcelable.Creator<PeriodicAdvertisingReport> CREATOR = new Creator<PeriodicAdvertisingReport>() {
+ @Override
+ public PeriodicAdvertisingReport createFromParcel(Parcel source) {
+ return new PeriodicAdvertisingReport(source);
+ }
+
+ @Override
+ public PeriodicAdvertisingReport[] newArray(int size) {
+ return new PeriodicAdvertisingReport[size];
+ }
+ };
+}
diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java
index 2fdfe7f8a83f..583ddd20fd7f 100644
--- a/core/java/android/bluetooth/le/ScanResult.java
+++ b/core/java/android/bluetooth/le/ScanResult.java
@@ -27,7 +27,56 @@ import java.util.Objects;
* ScanResult for Bluetooth LE scan.
*/
public final class ScanResult implements Parcelable {
- // Remote bluetooth device.
+
+ /**
+ * For chained advertisements, inidcates tha the data contained in this
+ * scan result is complete.
+ */
+ public static final int DATA_COMPLETE = 0x00;
+
+ /**
+ * For chained advertisements, indicates that the controller was
+ * unable to receive all chained packets and the scan result contains
+ * incomplete truncated data.
+ */
+ public static final int DATA_TRUNCATED = 0x02;
+
+ /**
+ * Indicates that the secondary physical layer was not used.
+ */
+ public static final int PHY_UNUSED = 0x00;
+
+ /**
+ * Bluetooth LE 1Mbit advertiser PHY.
+ */
+ public static final int PHY_LE_1M = 0x01;
+
+ /**
+ * Bluetooth LE 2Mbit advertiser PHY.
+ */
+ public static final int PHY_LE_2M = 0x02;
+
+ /**
+ * Bluetooth LE Coded advertiser PHY.
+ */
+ public static final int PHY_LE_CODED = 0x03;
+
+ /**
+ * Advertising Set ID is not present in the packet.
+ */
+ public static final int SID_NOT_PRESENT = 0xFF;
+
+ /**
+ * Mask for checking wether event type represents legacy advertisement.
+ */
+ private static final int ET_LEGACY_MASK = 0x10;
+
+ /**
+ * Mask for checking wether event type represents connectable advertisement.
+ */
+ private static final int ET_CONNECTABLE_MASK = 0x01;
+
+ // Remote Bluetooth device.
private BluetoothDevice mDevice;
// Scan record, including advertising data and scan response data.
@@ -40,13 +89,21 @@ public final class ScanResult implements Parcelable {
// Device timestamp when the result was last seen.
private long mTimestampNanos;
+ private int mEventType;
+ private int mPrimaryPhy;
+ private int mSecondaryPhy;
+ private int mAdvertisingSid;
+ private int mTxPower;
+ private int mPeriodicAdvertisingInterval;
+
/**
- * Constructor of scan result.
+ * Constructs a new ScanResult.
*
- * @param device Remote bluetooth device that is found.
+ * @param device Remote Bluetooth device found.
* @param scanRecord Scan record including both advertising data and scan response data.
* @param rssi Received signal strength.
- * @param timestampNanos Device timestamp when the scan result was observed.
+ * @param timestampNanos Timestamp at which the scan result was observed.
+ * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int, ScanRecord, long)}
*/
public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi,
long timestampNanos) {
@@ -54,6 +111,41 @@ public final class ScanResult implements Parcelable {
mScanRecord = scanRecord;
mRssi = rssi;
mTimestampNanos = timestampNanos;
+ mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK;
+ mPrimaryPhy = PHY_LE_1M;
+ mSecondaryPhy = PHY_UNUSED;
+ mAdvertisingSid = SID_NOT_PRESENT;
+ mTxPower = 127;
+ mPeriodicAdvertisingInterval = 0;
+ }
+
+ /**
+ * Constructs a new ScanResult.
+ *
+ * @param device Remote Bluetooth device found.
+ * @param eventType Event type.
+ * @param primaryPhy Primary advertising phy.
+ * @param secondaryPhy Secondary advertising phy.
+ * @param advertisingSid Advertising set ID.
+ * @param txPower Transmit power.
+ * @param rssi Received signal strength.
+ * @param periodicAdvertisingInterval Periodic advertising interval.
+ * @param scanRecord Scan record including both advertising data and scan response data.
+ * @param timestampNanos Timestamp at which the scan result was observed.
+ */
+ public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy,
+ int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval,
+ ScanRecord scanRecord, long timestampNanos) {
+ mDevice = device;
+ mEventType = eventType;
+ mPrimaryPhy = primaryPhy;
+ mSecondaryPhy = secondaryPhy;
+ mAdvertisingSid = advertisingSid;
+ mTxPower = txPower;
+ mRssi = rssi;
+ mPeriodicAdvertisingInterval = periodicAdvertisingInterval;
+ mScanRecord = scanRecord;
+ mTimestampNanos = timestampNanos;
}
private ScanResult(Parcel in) {
@@ -76,6 +168,12 @@ public final class ScanResult implements Parcelable {
}
dest.writeInt(mRssi);
dest.writeLong(mTimestampNanos);
+ dest.writeInt(mEventType);
+ dest.writeInt(mPrimaryPhy);
+ dest.writeInt(mSecondaryPhy);
+ dest.writeInt(mAdvertisingSid);
+ dest.writeInt(mTxPower);
+ dest.writeInt(mPeriodicAdvertisingInterval);
}
private void readFromParcel(Parcel in) {
@@ -87,6 +185,12 @@ public final class ScanResult implements Parcelable {
}
mRssi = in.readInt();
mTimestampNanos = in.readLong();
+ mEventType = in.readInt();
+ mPrimaryPhy = in.readInt();
+ mSecondaryPhy = in.readInt();
+ mAdvertisingSid = in.readInt();
+ mTxPower = in.readInt();
+ mPeriodicAdvertisingInterval = in.readInt();
}
@Override
@@ -95,7 +199,7 @@ public final class ScanResult implements Parcelable {
}
/**
- * Returns the remote bluetooth device identified by the bluetooth device address.
+ * Returns the remote Bluetooth device identified by the Bluetooth device address.
*/
public BluetoothDevice getDevice() {
return mDevice;
@@ -110,7 +214,7 @@ public final class ScanResult implements Parcelable {
}
/**
- * Returns the received signal strength in dBm. The valid range is [-127, 127].
+ * Returns the received signal strength in dBm. The valid range is [-127, 126].
*/
public int getRssi() {
return mRssi;
@@ -123,9 +227,79 @@ public final class ScanResult implements Parcelable {
return mTimestampNanos;
}
+ /**
+ * Returns true if this object represents legacy scan result.
+ * Legacy scan results do not contain advanced advertising information
+ * as specified in the Bluetooth Core Specification v5.
+ */
+ public boolean isLegacy() {
+ return (mEventType & ET_LEGACY_MASK) != 0;
+ }
+
+ /**
+ * Returns true if this object represents connectable scan result.
+ */
+ public boolean isConnectable() {
+ return (mEventType & ET_CONNECTABLE_MASK) != 0;
+ }
+
+ /**
+ * Returns the data status.
+ * Can be one of {@link ScanResult#DATA_COMPLETE} or
+ * {@link ScanResult#DATA_TRUNCATED}.
+ */
+ public int getDataStatus() {
+ // return bit 5 and 6
+ return (mEventType >> 5) & 0x03;
+ }
+
+ /**
+ * Returns the primary Physical Layer
+ * on which this advertisment was received.
+ * Can be one of {@link ScanResult#PHY_LE_1M} or
+ * {@link ScanResult#PHY_LE_CODED}.
+ */
+ public int getPrimaryPhy() { return mPrimaryPhy; }
+
+ /**
+ * Returns the secondary Physical Layer
+ * on which this advertisment was received.
+ * Can be one of {@link ScanResult#PHY_LE_1M},
+ * {@link ScanResult#PHY_LE_2M}, {@link ScanResult#PHY_LE_CODED}
+ * or {@link ScanResult#PHY_UNUSED} - if the advertisement
+ * was not received on a secondary physical channel.
+ */
+ public int getSecondaryPhy() { return mSecondaryPhy; }
+
+ /**
+ * Returns the advertising set id.
+ * May return {@link ScanResult#SID_NOT_PRESENT} if
+ * no set id was is present.
+ */
+ public int getAdvertisingSid() { return mAdvertisingSid; }
+
+ /**
+ * Returns the transmit power in dBm.
+ * Valid range is [-127, 126]. A value of 127 indicates that the
+ * advertisement did not indicate TX power.
+ */
+ public int getTxPower() { return mTxPower; }
+
+ /**
+ * Returns the periodic advertising interval in units of 1.25ms.
+ * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of 0 means
+ * periodic advertising is not used for this scan result.
+ */
+ public int getPeriodicAdvertisingInterval() {
+ return mPeriodicAdvertisingInterval;
+ }
+
@Override
public int hashCode() {
- return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos);
+ return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos,
+ mEventType, mPrimaryPhy, mSecondaryPhy,
+ mAdvertisingSid, mTxPower,
+ mPeriodicAdvertisingInterval);
}
@Override
@@ -138,15 +312,24 @@ public final class ScanResult implements Parcelable {
}
ScanResult other = (ScanResult) obj;
return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) &&
- Objects.equals(mScanRecord, other.mScanRecord)
- && (mTimestampNanos == other.mTimestampNanos);
+ Objects.equals(mScanRecord, other.mScanRecord) &&
+ (mTimestampNanos == other.mTimestampNanos) &&
+ mEventType == other.mEventType &&
+ mPrimaryPhy == other.mPrimaryPhy &&
+ mSecondaryPhy == other.mSecondaryPhy &&
+ mAdvertisingSid == other.mAdvertisingSid &&
+ mTxPower == other.mTxPower &&
+ mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval;
}
@Override
public String toString() {
- return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord="
- + Objects.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampNanos="
- + mTimestampNanos + '}';
+ return "ScanResult{" + "device=" + mDevice + ", scanRecord=" +
+ Objects.toString(mScanRecord) + ", rssi=" + mRssi +
+ ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType +
+ ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy +
+ ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower +
+ ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}';
}
public static final Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index d61662469b6c..69c9a8cece3a 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -122,6 +122,24 @@ public final class ScanSettings implements Parcelable {
@SystemApi
public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1;
+ /**
+ * Use the Bluetooth LE 1Mbit PHY for scanning.
+ */
+ public static final int PHY_LE_1M = 1;
+
+ /**
+ * Use Bluetooth LE Coded PHY for scanning.
+ */
+ public static final int PHY_LE_CODED = 3;
+
+ /**
+ * Use all supported PHYs for scanning.
+ * This will check the controller capabilities, and start
+ * the scan on 1Mbit and LE Coded PHYs if supported, or on
+ * the 1Mbit PHY only.
+ */
+ public static final int PHY_LE_ALL_SUPPORTED = 255;
+
// Bluetooth LE scan mode.
private int mScanMode;
@@ -138,6 +156,11 @@ public final class ScanSettings implements Parcelable {
private int mNumOfMatchesPerFilter;
+ // Include only legacy advertising results
+ private boolean mLegacy;
+
+ private int mPhy;
+
public int getScanMode() {
return mScanMode;
}
@@ -165,6 +188,22 @@ public final class ScanSettings implements Parcelable {
}
/**
+ * Returns whether only legacy advertisements will be returned.
+ * Legacy advertisements include advertisements as specified
+ * by the Bluetooth core specification 4.2 and below.
+ */
+ public boolean getLegacy() {
+ return mLegacy;
+ }
+
+ /**
+ * Returns the physical layer used during a scan.
+ */
+ public int getPhy() {
+ return mPhy;
+ }
+
+ /**
* Returns report delay timestamp based on the device clock.
*/
public long getReportDelayMillis() {
@@ -172,13 +211,16 @@ public final class ScanSettings implements Parcelable {
}
private ScanSettings(int scanMode, int callbackType, int scanResultType,
- long reportDelayMillis, int matchMode, int numOfMatchesPerFilter) {
+ long reportDelayMillis, int matchMode,
+ int numOfMatchesPerFilter, boolean legacy, int phy) {
mScanMode = scanMode;
mCallbackType = callbackType;
mScanResultType = scanResultType;
mReportDelayMillis = reportDelayMillis;
mNumOfMatchesPerFilter = numOfMatchesPerFilter;
mMatchMode = matchMode;
+ mLegacy = legacy;
+ mPhy = phy;
}
private ScanSettings(Parcel in) {
@@ -188,6 +230,8 @@ public final class ScanSettings implements Parcelable {
mReportDelayMillis = in.readLong();
mMatchMode = in.readInt();
mNumOfMatchesPerFilter = in.readInt();
+ mLegacy = in.readInt() != 0 ? true : false;
+ mPhy = in.readInt();
}
@Override
@@ -198,6 +242,8 @@ public final class ScanSettings implements Parcelable {
dest.writeLong(mReportDelayMillis);
dest.writeInt(mMatchMode);
dest.writeInt(mNumOfMatchesPerFilter);
+ dest.writeInt(mLegacy ? 1 : 0);
+ dest.writeInt(mPhy);
}
@Override
@@ -228,6 +274,9 @@ public final class ScanSettings implements Parcelable {
private long mReportDelayMillis = 0;
private int mMatchMode = MATCH_MODE_AGGRESSIVE;
private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT;
+ private boolean mLegacy = true;
+ private int mPhy = PHY_LE_ALL_SUPPORTED;
+
/**
* Set scan mode for Bluetooth LE scan.
*
@@ -341,11 +390,44 @@ public final class ScanSettings implements Parcelable {
}
/**
+ * Set whether only legacy advertisments should be returned in scan results.
+ * Legacy advertisements include advertisements as specified by the
+ * Bluetooth core specification 4.2 and below. This is true by default
+ * for compatibility with older apps.
+ *
+ * @param legacy true if only legacy advertisements will be returned
+ */
+ public Builder setLegacy(boolean legacy) {
+ mLegacy = legacy;
+ return this;
+ }
+
+ /**
+ * Set the Physical Layer to use during this scan.
+ * This is used only if {@link ScanSettings.Builder#setLegacy}
+ * is set to false.
+ * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}
+ * may be used to check whether LE Coded phy is supported by calling
+ * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}.
+ * Selecting an unsupported phy will result in failure to start scan.
+ *
+ * @param phy Can be one of
+ * {@link ScanSettings#PHY_LE_1M},
+ * {@link ScanSettings#PHY_LE_CODED} or
+ * {@link ScanSettings#PHY_LE_ALL_SUPPORTED}
+ */
+ public Builder setPhy(int phy) {
+ mPhy = phy;
+ return this;
+ }
+
+ /**
* Build {@link ScanSettings}.
*/
public ScanSettings build() {
return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
- mReportDelayMillis, mMatchMode, mNumOfMatchesPerFilter);
+ mReportDelayMillis, mMatchMode,
+ mNumOfMatchesPerFilter, mLegacy, mPhy);
}
}
}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index c0c1a4dd198b..6fa32b4b6944 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -26,6 +26,8 @@ import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import java.util.List;
+
/**
* System level service for managing companion devices
*
@@ -102,6 +104,11 @@ public final class CompanionDeviceManager {
* special capabilities have a negative effect on the device's battery and user's data
* usage, therefore you should requested them when absolutely necessary.</p>
*
+ * <p>You can call {@link #getAssociations} to get the list of currently associated
+ * devices, and {@link #disassociate} to remove an association. Consider doing so when the
+ * association is no longer relevant to avoid unnecessary battery and/or data drain resulting
+ * from special privileges that the association provides</p>
+ *
* @param request specific details about this request
* @param callback will be called once there's at least one device found for user to choose from
* @param handler A handler to control which thread the callback will be delivered on, or null,
@@ -119,6 +126,8 @@ public final class CompanionDeviceManager {
try {
mService.associate(
request,
+ //TODO implicit pointer to outer class -> =null onDestroy
+ //TODO onStop if isFinishing -> stopScan
new IFindDeviceCallback.Stub() {
@Override
public void onSuccess(PendingIntent launcher) {
@@ -138,6 +147,38 @@ public final class CompanionDeviceManager {
}
}
+ /**
+ * @return a list of MAC addresses of devices that have been previously associated with the
+ * current app. You can use these with {@link #disassociate}
+ */
+ @NonNull
+ public List<String> getAssociations() {
+ try {
+ return mService.getAssociations(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove the association between this app and the device with the given mac address.
+ *
+ * <p>Any privileges provided via being associated with a given device will be revoked</p>
+ *
+ * <p>Consider doing so when the
+ * association is no longer relevant to avoid unnecessary battery and/or data drain resulting
+ * from special privileges that the association provides</p>
+ *
+ * @param deviceMacAddress the MAC address of device to disassociate from this app
+ */
+ public void disassociate(@NonNull String deviceMacAddress) {
+ try {
+ mService.disassociate(deviceMacAddress, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public void requestNotificationAccess() {
//TODO implement
diff --git a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
index 4d779639888e..5398c3cea68d 100644
--- a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
+++ b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl
@@ -20,6 +20,7 @@ import android.companion.AssociationRequest;
import android.companion.ICompanionDeviceDiscoveryServiceCallback;
import android.companion.IFindDeviceCallback;
+
/** @hide */
interface ICompanionDeviceDiscoveryService {
void startDiscovery(
diff --git a/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl b/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl
index 7af708e94d97..6bbb58da9938 100644
--- a/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl
+++ b/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl
@@ -18,5 +18,5 @@ package android.companion;
/** @hide */
interface ICompanionDeviceDiscoveryServiceCallback {
- void onDeviceSelected(String packageName, int userId);
+ oneway void onDeviceSelected(String packageName, int userId, String deviceAddress);
}
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 1d30ada25c62..495141d75a44 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -29,6 +29,9 @@ interface ICompanionDeviceManager {
in IFindDeviceCallback callback,
in String callingPackage);
+ List<String> getAssociations(String callingPackage);
+ void disassociate(String deviceMacAddress, String callingPackage);
+
//TODO add these
// boolean haveNotificationAccess(String packageName);
// oneway void requestNotificationAccess(String packageName);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e0dc10daf64d..d6306e001516 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1465,7 +1465,7 @@ public class Intent implements Parcelable, Cloneable {
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SHOW_KEYBOARD_SHORTCUTS =
- "android.intent.action.SHOW_KEYBOARD_SHORTCUTS";
+ "com.android.intent.action.SHOW_KEYBOARD_SHORTCUTS";
/**
* Activity Action: Dismiss the Keyboard Shortcuts Helper screen.
@@ -1475,7 +1475,7 @@ public class Intent implements Parcelable, Cloneable {
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_DISMISS_KEYBOARD_SHORTCUTS =
- "android.intent.action.DISMISS_KEYBOARD_SHORTCUTS";
+ "com.android.intent.action.DISMISS_KEYBOARD_SHORTCUTS";
/**
* Activity Action: Show settings for managing network data usage of a
@@ -3124,7 +3124,7 @@ public class Intent implements Parcelable, Cloneable {
* @hide
*/
public static final String ACTION_SHOW_BRIGHTNESS_DIALOG =
- "android.intent.action.SHOW_BRIGHTNESS_DIALOG";
+ "com.android.intent.action.SHOW_BRIGHTNESS_DIALOG";
/**
* Broadcast Action: A global button was pressed. Includes a single
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 56609eb57b0d..93204d1264eb 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -16,6 +16,7 @@
package android.content;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -138,6 +139,7 @@ import java.util.Set;
* will only match an Intent that does not have any categories.
*/
public class IntentFilter implements Parcelable {
+ private static final String AGLOB_STR = "aglob";
private static final String SGLOB_STR = "sglob";
private static final String PREFIX_STR = "prefix";
private static final String LITERAL_STR = "literal";
@@ -482,11 +484,13 @@ public class IntentFilter implements Parcelable {
}
/** @hide */
+ @SystemApi
public final void setOrder(int order) {
mOrder = order;
}
/** @hide */
+ @SystemApi
public final int getOrder() {
return mOrder;
}
@@ -1594,6 +1598,9 @@ public class IntentFilter implements Parcelable {
case PatternMatcher.PATTERN_SIMPLE_GLOB:
serializer.attribute(null, SGLOB_STR, pe.getPath());
break;
+ case PatternMatcher.PATTERN_ADVANCED_GLOB:
+ serializer.attribute(null, AGLOB_STR, pe.getPath());
+ break;
}
serializer.endTag(null, SSP_STR);
}
@@ -1621,6 +1628,9 @@ public class IntentFilter implements Parcelable {
case PatternMatcher.PATTERN_SIMPLE_GLOB:
serializer.attribute(null, SGLOB_STR, pe.getPath());
break;
+ case PatternMatcher.PATTERN_ADVANCED_GLOB:
+ serializer.attribute(null, AGLOB_STR, pe.getPath());
+ break;
}
serializer.endTag(null, PATH_STR);
}
@@ -1673,6 +1683,8 @@ public class IntentFilter implements Parcelable {
addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_PREFIX);
} else if ((ssp=parser.getAttributeValue(null, SGLOB_STR)) != null) {
addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SIMPLE_GLOB);
+ } else if ((ssp=parser.getAttributeValue(null, AGLOB_STR)) != null) {
+ addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_ADVANCED_GLOB);
}
} else if (tagName.equals(AUTH_STR)) {
String host = parser.getAttributeValue(null, HOST_STR);
@@ -1688,6 +1700,8 @@ public class IntentFilter implements Parcelable {
addDataPath(path, PatternMatcher.PATTERN_PREFIX);
} else if ((path=parser.getAttributeValue(null, SGLOB_STR)) != null) {
addDataPath(path, PatternMatcher.PATTERN_SIMPLE_GLOB);
+ } else if ((path=parser.getAttributeValue(null, AGLOB_STR)) != null) {
+ addDataPath(path, PatternMatcher.PATTERN_ADVANCED_GLOB);
}
} else {
Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName);
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 4f5d96038d1e..86c1aa8228f1 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -89,6 +89,11 @@ interface IOverlayManager {
boolean setEnabled(in String packageName, in boolean enable, in int userId);
/**
+ * Version of setEnabled that will also disable any other overlays for the target package.
+ */
+ boolean setEnabledExclusive(in String packageName, in boolean enable, in int userId);
+
+ /**
* Change the priority of the given overlay to be just higher than the
* overlay with package name newParentPackageName. Both overlay packages
* must have the same target and user.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b3b5bcfe79c0..3a875bc79aa8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -34,6 +34,7 @@ import android.annotation.XmlRes;
import android.app.PackageDeleteObserver;
import android.app.PackageInstallObserver;
import android.app.admin.DevicePolicyManager;
+import android.app.usage.StorageStatsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -133,6 +134,7 @@ public abstract class PackageManager {
MATCH_SYSTEM_ONLY,
MATCH_FACTORY_ONLY,
MATCH_DEBUG_TRIAGED_MISSING,
+ MATCH_INSTANT,
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
@@ -148,6 +150,7 @@ public abstract class PackageManager {
MATCH_SYSTEM_ONLY,
MATCH_DEBUG_TRIAGED_MISSING,
MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+ MATCH_INSTANT,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
})
@@ -167,6 +170,7 @@ public abstract class PackageManager {
MATCH_DIRECT_BOOT_UNAWARE,
MATCH_SYSTEM_ONLY,
MATCH_UNINSTALLED_PACKAGES,
+ MATCH_INSTANT,
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
@@ -188,6 +192,7 @@ public abstract class PackageManager {
MATCH_DIRECT_BOOT_UNAWARE,
MATCH_SYSTEM_ONLY,
MATCH_UNINSTALLED_PACKAGES,
+ MATCH_INSTANT,
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
@@ -444,6 +449,7 @@ public abstract class PackageManager {
* instant app. By default, instant app components are not matched.
* @hide
*/
+ @SystemApi
public static final int MATCH_INSTANT = 0x00800000;
/**
@@ -3739,6 +3745,7 @@ public abstract class PackageManager {
*
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.ACCESS_INSTANT_APPS)
public abstract @NonNull List<InstantAppInfo> getInstantApps();
@@ -3749,6 +3756,7 @@ public abstract class PackageManager {
*
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.ACCESS_INSTANT_APPS)
public abstract @Nullable Drawable getInstantAppIcon(String packageName);
@@ -5459,8 +5467,10 @@ public abstract class PackageManager {
* the status of the operation. observer may be null to indicate that
* no callback is desired.
*
+ * @deprecated use {@link StorageStatsManager} instead.
* @hide
*/
+ @Deprecated
public abstract void getPackageSizeInfoAsUser(String packageName, @UserIdInt int userId,
IPackageStatsObserver observer);
@@ -5468,8 +5478,10 @@ public abstract class PackageManager {
* Like {@link #getPackageSizeInfoAsUser(String, int, IPackageStatsObserver)}, but
* returns the size for the calling user.
*
+ * @deprecated use {@link StorageStatsManager} instead.
* @hide
*/
+ @Deprecated
public void getPackageSizeInfo(String packageName, IPackageStatsObserver observer) {
getPackageSizeInfoAsUser(packageName, UserHandle.myUserId(), observer);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 60cc6b06bf11..a1c325ac26ea 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4867,6 +4867,13 @@ public class PackageParser {
PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission);
}
+ path = sa.getNonConfigurationString(
+ com.android.internal.R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0);
+ if (path != null) {
+ pa = new PathPermission(path,
+ PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission, writePermission);
+ }
+
sa.recycle();
if (pa != null) {
@@ -5389,6 +5396,16 @@ public class PackageParser {
outInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
}
+ str = sa.getNonConfigurationString(
+ com.android.internal.R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
+ if (str != null) {
+ if (!allowGlobs) {
+ outError[0] = "pathAdvancedPattern not allowed here; path must be literal";
+ return false;
+ }
+ outInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
+ }
+
sa.recycle();
XmlUtils.skipCurrentTag(parser);
} else if (!RIGID_PARSER) {
diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java
index cb9039b046ee..27b3506f49a7 100644
--- a/core/java/android/content/pm/PackageStats.java
+++ b/core/java/android/content/pm/PackageStats.java
@@ -16,14 +16,22 @@
package android.content.pm;
+import android.app.usage.StorageStatsManager;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.util.Objects;
/**
- * implementation of PackageStats associated with a
- * application package.
+ * implementation of PackageStats associated with a application package.
+ *
+ * @deprecated this class is an orphan that could never be obtained from a valid
+ * public API. If you need package storage statistics use the new
+ * {@link StorageStatsManager} APIs.
*/
+@Deprecated
public class PackageStats implements Parcelable {
/** Name of the package to which this stats applies. */
public String packageName;
@@ -173,4 +181,31 @@ public class PackageStats implements Parcelable {
dest.writeLong(externalMediaSize);
dest.writeLong(externalObbSize);
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PackageStats)) {
+ return false;
+ }
+
+ final PackageStats otherStats = (PackageStats) obj;
+ return ((TextUtils.equals(packageName, otherStats.packageName))
+ && userHandle == otherStats.userHandle
+ && codeSize == otherStats.codeSize
+ && dataSize == otherStats.dataSize
+ && cacheSize == otherStats.cacheSize
+ && externalCodeSize == otherStats.externalCodeSize
+ && externalDataSize == otherStats.externalDataSize
+ && externalCacheSize == otherStats.externalCacheSize
+ && externalMediaSize == otherStats.externalMediaSize
+ && externalObbSize == otherStats.externalObbSize);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(packageName, userHandle, codeSize, dataSize,
+ cacheSize, externalCodeSize, externalDataSize, externalCacheSize, externalMediaSize,
+ externalObbSize);
+ }
+
}
diff --git a/core/java/android/content/pm/SELinuxUtil.java b/core/java/android/content/pm/SELinuxUtil.java
index 55b5e297e18f..025c0fe30877 100644
--- a/core/java/android/content/pm/SELinuxUtil.java
+++ b/core/java/android/content/pm/SELinuxUtil.java
@@ -32,11 +32,10 @@ public final class SELinuxUtil {
/** @hide */
public static String assignSeinfoUser(PackageUserState userState) {
- String seInfo = "";
- if (userState.instantApp)
- seInfo += INSTANT_APP_STR;
- seInfo += COMPLETE_STR;
- return seInfo;
+ if (userState.instantApp) {
+ return INSTANT_APP_STR + COMPLETE_STR;
+ }
+ return COMPLETE_STR;
}
}
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index 3f8f90ebf054..50fc3443f5a6 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -67,13 +67,14 @@ public class FontResourcesParser {
AttributeSet attrs = Xml.asAttributeSet(parser);
TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamily);
String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority);
+ String providerPackage = array.getString(R.styleable.FontFamily_fontProviderPackage);
String query = array.getString(R.styleable.FontFamily_fontProviderQuery);
array.recycle();
- if (authority != null && query != null) {
+ if (authority != null && providerPackage != null && query != null) {
while (parser.next() != XmlPullParser.END_TAG) {
skip(parser);
}
- return new FontConfig.Family(authority, query);
+ return new FontConfig.Family(authority, providerPackage, query);
}
List<FontConfig.Font> fonts = new ArrayList<>();
while (parser.next() != XmlPullParser.END_TAG) {
diff --git a/core/java/android/hardware/SensorDirectChannel.java b/core/java/android/hardware/SensorDirectChannel.java
index 0efd62b463f8..a65d57daa12c 100644
--- a/core/java/android/hardware/SensorDirectChannel.java
+++ b/core/java/android/hardware/SensorDirectChannel.java
@@ -135,8 +135,8 @@ public final class SensorDirectChannel implements AutoCloseable {
}
/**
- * This function encode handle information in {@link android.os.Memory} into a long array to be
- * passed down to native methods.
+ * This function encode handle information in {@link android.os.MemoryFile} into a long array to
+ * be passed down to native methods.
*
* @hide */
static long[] encodeData(MemoryFile ashmem) {
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index cfda2f4fa8b4..a6930b035853 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -895,7 +895,7 @@ public abstract class SensorManager {
* @see #configureDirectChannel(SensorDirectChannel, Sensor, int)
*/
public SensorDirectChannel createDirectChannel(MemoryFile mem) {
- return createDirectChannelImpl(mem.length(), mem, null);
+ return createDirectChannelImpl(mem, null);
}
/**
@@ -913,12 +913,12 @@ public abstract class SensorManager {
* @see #configureDirectChannel(SensorDirectChannel, Sensor, int)
*/
public SensorDirectChannel createDirectChannel(HardwareBuffer mem) {
- return null;
+ return createDirectChannelImpl(null, mem);
}
/** @hide */
- protected abstract SensorDirectChannel createDirectChannelImpl(long size,
- MemoryFile ashmemFile, HardwareBuffer hardwareBuffer);
+ protected abstract SensorDirectChannel createDirectChannelImpl(
+ MemoryFile memoryFile, HardwareBuffer hardwareBuffer);
/** @hide */
void destroyDirectChannel(SensorDirectChannel channel) {
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 4992def7de15..70298475043d 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -34,6 +34,7 @@ import dalvik.system.CloseGuard;
import com.android.internal.annotations.GuardedBy;
+import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
@@ -50,6 +51,7 @@ import java.util.Map;
public class SystemSensorManager extends SensorManager {
//TODO: disable extra logging before release
private static boolean DEBUG_DYNAMIC_SENSOR = true;
+ private static int MIN_DIRECT_CHANNEL_BUFFER_SIZE = 104;
private static native void nativeClassInit();
private static native long nativeCreate(String opPackageName);
@@ -59,7 +61,7 @@ public class SystemSensorManager extends SensorManager {
private static native boolean nativeIsDataInjectionEnabled(long nativeInstance);
private static native int nativeCreateDirectChannel(
- long nativeInstance, long size, int channelType, long [] channelData);
+ long nativeInstance, long size, int channelType, int fd, HardwareBuffer buffer);
private static native void nativeDestroyDirectChannel(
long nativeInstance, int channelHandle);
private static native int nativeConfigDirectChannel(
@@ -525,24 +527,54 @@ public class SystemSensorManager extends SensorManager {
}
/** @hide */
- protected SensorDirectChannel createDirectChannelImpl(long size,
- MemoryFile ashmemFile, HardwareBuffer grallocMemObject) {
+ protected SensorDirectChannel createDirectChannelImpl(
+ MemoryFile memoryFile, HardwareBuffer hardwareBuffer) {
SensorDirectChannel ch = null;
+ long size;
+ if (memoryFile != null) {
+ int fd;
+ try {
+ fd = memoryFile.getFileDescriptor().getInt$();
+ } catch (IOException e) {
+ throw new IllegalArgumentException("MemoryFile object is not valid");
+ }
- if (size <= 0) throw new IllegalArgumentException("size has to be greater than 0");
-
- if (ashmemFile != null) {
- if (size != ashmemFile.length()) {
- throw new IllegalArgumentException("size has to match MemoryFile.length()");
+ if (memoryFile.length() < MIN_DIRECT_CHANNEL_BUFFER_SIZE) {
+ throw new IllegalArgumentException(
+ "Size of MemoryFile has to be greater than "
+ + MIN_DIRECT_CHANNEL_BUFFER_SIZE);
}
+
+ size = memoryFile.length();
int id = nativeCreateDirectChannel(
- mNativeInstance, size, SensorDirectChannel.TYPE_ASHMEM,
- SensorDirectChannel.encodeData(ashmemFile));
+ mNativeInstance, size, SensorDirectChannel.TYPE_ASHMEM, fd, null);
if (id > 0) {
ch = new SensorDirectChannel(this, id, SensorDirectChannel.TYPE_ASHMEM, size);
}
- } else if (grallocMemObject != null) {
- Log.wtf(TAG, "Implement GRALLOC or remove GRALLOC support entirely");
+ } else if (hardwareBuffer != null) {
+ if (hardwareBuffer.getFormat() != HardwareBuffer.BLOB) {
+ throw new IllegalArgumentException("Format of HardwareBuffer must be BLOB");
+ }
+ if (hardwareBuffer.getHeight() != 1) {
+ throw new IllegalArgumentException("Height of HardwareBuffer must be 1");
+ }
+ if (hardwareBuffer.getWidth() < MIN_DIRECT_CHANNEL_BUFFER_SIZE) {
+ throw new IllegalArgumentException(
+ "Width if HaradwareBuffer must be greater than "
+ + MIN_DIRECT_CHANNEL_BUFFER_SIZE);
+ }
+ if ((hardwareBuffer.getUsage() & HardwareBuffer.USAGE0_SENSOR_DIRECT_DATA) == 0) {
+ throw new IllegalArgumentException(
+ "HardwareBuffer must set usage flag USAGE0_SENSOR_DIRECT_DATA");
+ }
+ size = hardwareBuffer.getWidth();
+ int id = nativeCreateDirectChannel(
+ mNativeInstance, size, SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+ -1, hardwareBuffer);
+ if (id > 0) {
+ ch = new SensorDirectChannel(
+ this, id, SensorDirectChannel.TYPE_HARDWARE_BUFFER, size);
+ }
} else {
throw new IllegalArgumentException("Invalid parameter");
}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 45cd084d5268..7bfb5d08df7b 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -19,6 +19,10 @@ package android.hardware.camera2;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import static android.hardware.camera2.ICameraDeviceUser.NORMAL_MODE;
+import static android.hardware.camera2.ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.params.OutputConfiguration;
@@ -719,6 +723,84 @@ public abstract class CameraDevice implements AutoCloseable {
throws CameraAccessException;
/**
+ * Standard camera operation mode.
+ *
+ * @see #createCustomCaptureSession
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int SESSION_OPERATION_MODE_NORMAL =
+ 0; // ICameraDeviceUser.NORMAL_MODE;
+
+ /**
+ * Constrained high-speed operation mode.
+ *
+ * @see #createCustomCaptureSession
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED =
+ 1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE;
+
+ /**
+ * First vendor-specific operating mode
+ *
+ * @see #createCustomCaptureSession
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int SESSION_OPERATION_MODE_VENDOR_START =
+ 0x8000; // ICameraDeviceUser.VENDOR_MODE_START;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ {SESSION_OPERATION_MODE_NORMAL,
+ SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED,
+ SESSION_OPERATION_MODE_VENDOR_START})
+ public @interface SessionOperatingMode {};
+
+ /**
+ * Create a new camera capture session with a custom operating mode.
+ *
+ * @param inputConfig The configuration for the input {@link Surface} if a reprocessing session
+ * is desired, or {@code null} otherwise.
+ * @param outputs The new set of {@link OutputConfiguration OutputConfigurations} that should be
+ * made available as targets for captured image data.
+ * @param operatingMode The custom operating mode to use; a nonnegative value, either a custom
+ * vendor value or one of the SESSION_OPERATION_MODE_* values.
+ * @param callback The callback to notify about the status of the new capture session.
+ * @param handler The handler on which the callback should be invoked, or {@code null} to use
+ * the current thread's {@link android.os.Looper looper}.
+ *
+ * @throws IllegalArgumentException if the input configuration is null or not supported, the set
+ * of output Surfaces do not meet the requirements, the
+ * callback is null, or the handler is null but the current
+ * thread has no looper.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see #createCaptureSession
+ * @see #createReprocessableCaptureSession
+ * @see CameraCaptureSession
+ * @see OutputConfiguration
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public abstract void createCustomCaptureSession(
+ InputConfiguration inputConfig,
+ @NonNull List<OutputConfiguration> outputs,
+ @SessionOperatingMode int operatingMode,
+ @NonNull CameraCaptureSession.StateCallback callback,
+ @Nullable Handler handler)
+ throws CameraAccessException;
+
+ /**
* <p>Create a {@link CaptureRequest.Builder} for new capture requests,
* initialized with template for a target use case. The settings are chosen
* to be the best options for the specific camera device, so it is not
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 891df6362d29..16ffee02b94d 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -19,6 +19,7 @@ import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
import android.hardware.camera2.dispatch.BroadcastDispatcher;
import android.hardware.camera2.dispatch.DuckTypingDispatcher;
@@ -742,7 +743,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession
try {
// begin transition to unconfigured
mDeviceImpl.configureStreamsChecked(/*inputConfig*/null, /*outputs*/null,
- /*isConstrainedHighSpeed*/false);
+ /*operatingMode*/ ICameraDeviceUser.NORMAL_MODE);
} catch (CameraAccessException e) {
// OK: do not throw checked exceptions.
Log.e(TAG, mIdString + "Exception while unconfiguring outputs: ", e);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 2364ebe0f891..8bc65af65ef1 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -356,7 +356,7 @@ public class CameraDeviceImpl extends CameraDevice
outputConfigs.add(new OutputConfiguration(s));
}
configureStreamsChecked(/*inputConfig*/null, outputConfigs,
- /*isConstrainedHighSpeed*/false);
+ /*operatingMode*/ICameraDeviceUser.NORMAL_MODE);
}
@@ -374,13 +374,14 @@ public class CameraDeviceImpl extends CameraDevice
*
* @param inputConfig input configuration or {@code null} for no input
* @param outputs a list of one or more surfaces, or {@code null} to unconfigure
- * @param isConstrainedHighSpeed If the streams configuration is for constrained high speed output.
+ * @param operatingMode If the stream configuration is for a normal session,
+ * a constrained high speed session, or something else.
* @return whether or not the configuration was successful
*
* @throws CameraAccessException if there were any unexpected problems during configuration
*/
public boolean configureStreamsChecked(InputConfiguration inputConfig,
- List<OutputConfiguration> outputs, boolean isConstrainedHighSpeed)
+ List<OutputConfiguration> outputs, int operatingMode)
throws CameraAccessException {
// Treat a null input the same an empty list
if (outputs == null) {
@@ -456,7 +457,7 @@ public class CameraDeviceImpl extends CameraDevice
}
}
- mRemoteDevice.endConfigure(isConstrainedHighSpeed);
+ mRemoteDevice.endConfigure(operatingMode);
success = true;
} catch (IllegalArgumentException e) {
@@ -492,7 +493,7 @@ public class CameraDeviceImpl extends CameraDevice
outConfigurations.add(new OutputConfiguration(surface));
}
createCaptureSessionInternal(null, outConfigurations, callback, handler,
- /*isConstrainedHighSpeed*/false);
+ /*operatingMode*/ICameraDeviceUser.NORMAL_MODE);
}
@Override
@@ -508,7 +509,7 @@ public class CameraDeviceImpl extends CameraDevice
List<OutputConfiguration> currentOutputs = new ArrayList<>(outputConfigurations);
createCaptureSessionInternal(null, currentOutputs, callback, handler,
- /*isConstrainedHighSpeed*/false);
+ /*operatingMode*/ICameraDeviceUser.NORMAL_MODE);
}
@Override
@@ -528,7 +529,7 @@ public class CameraDeviceImpl extends CameraDevice
outConfigurations.add(new OutputConfiguration(surface));
}
createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler,
- /*isConstrainedHighSpeed*/false);
+ /*operatingMode*/ICameraDeviceUser.NORMAL_MODE);
}
@Override
@@ -556,7 +557,7 @@ public class CameraDeviceImpl extends CameraDevice
currentOutputs.add(new OutputConfiguration(output));
}
createCaptureSessionInternal(inputConfig, currentOutputs,
- callback, handler, /*isConstrainedHighSpeed*/false);
+ callback, handler, /*operatingMode*/ICameraDeviceUser.NORMAL_MODE);
}
@Override
@@ -576,13 +577,26 @@ public class CameraDeviceImpl extends CameraDevice
outConfigurations.add(new OutputConfiguration(surface));
}
createCaptureSessionInternal(null, outConfigurations, callback, handler,
- /*isConstrainedHighSpeed*/true);
+ /*operatingMode*/ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
+ }
+
+ @Override
+ public void createCustomCaptureSession(InputConfiguration inputConfig,
+ List<OutputConfiguration> outputs,
+ int operatingMode,
+ android.hardware.camera2.CameraCaptureSession.StateCallback callback,
+ Handler handler) throws CameraAccessException {
+ List<OutputConfiguration> currentOutputs = new ArrayList<OutputConfiguration>();
+ for (OutputConfiguration output : outputs) {
+ currentOutputs.add(new OutputConfiguration(output));
+ }
+ createCaptureSessionInternal(inputConfig, currentOutputs, callback, handler, operatingMode);
}
private void createCaptureSessionInternal(InputConfiguration inputConfig,
List<OutputConfiguration> outputConfigurations,
CameraCaptureSession.StateCallback callback, Handler handler,
- boolean isConstrainedHighSpeed) throws CameraAccessException {
+ int operatingMode) throws CameraAccessException {
synchronized(mInterfaceLock) {
if (DEBUG) {
Log.d(TAG, "createCaptureSessionInternal");
@@ -590,6 +604,8 @@ public class CameraDeviceImpl extends CameraDevice
checkIfCameraClosedOrInError();
+ boolean isConstrainedHighSpeed =
+ (operatingMode == ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE);
if (isConstrainedHighSpeed && inputConfig != null) {
throw new IllegalArgumentException("Constrained high speed session doesn't support"
+ " input configuration yet.");
@@ -608,7 +624,7 @@ public class CameraDeviceImpl extends CameraDevice
try {
// configure streams and then block until IDLE
configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,
- isConstrainedHighSpeed);
+ operatingMode);
if (configureSuccess == true && inputConfig != null) {
input = mRemoteDevice.getInputSurface();
}
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index d9f666e54330..27087a2e4881 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -106,9 +106,9 @@ public class ICameraDeviceUserWrapper {
}
}
- public void endConfigure(boolean isConstrainedHighSpeed) throws CameraAccessException {
+ public void endConfigure(int operatingMode) throws CameraAccessException {
try {
- mRemoteDevice.endConfigure(isConstrainedHighSpeed);
+ mRemoteDevice.endConfigure(operatingMode);
} catch (Throwable t) {
CameraManager.throwAsPublicException(t);
throw new UnsupportedOperationException("Unexpected exception", t);
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index d8ec4df504bb..f87d8c1401c5 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -497,7 +497,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
}
@Override
- public void endConfigure(boolean isConstrainedHighSpeed) {
+ public void endConfigure(int operatingMode) {
if (DEBUG) {
Log.d(TAG, "endConfigure called.");
}
@@ -507,6 +507,12 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
throw new ServiceSpecificException(ICameraService.ERROR_DISCONNECTED, err);
}
+ if (operatingMode != ICameraDeviceUser.NORMAL_MODE) {
+ String err = "LEGACY devices do not support this operating mode";
+ Log.e(TAG, err);
+ throw new ServiceSpecificException(ICameraService.ERROR_ILLEGAL_ARGUMENT, err);
+ }
+
SparseArray<Surface> surfaces = null;
synchronized(mConfigureLock) {
if (!mConfiguring) {
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index ff87b67b136f..27e2a5072287 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -338,6 +338,20 @@ public final class HdmiControlManager {
}
/**
+ * Controls standby mode of the system. It will also try to turn on/off the connected devices if
+ * necessary.
+ *
+ * @param isStandbyModeOn target status of the system's standby mode
+ */
+ public void setStandbyMode(boolean isStandbyModeOn) {
+ try {
+ mService.setStandbyMode(isStandbyModeOn);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Listener used to get hotplug event from HDMI port.
*/
public interface HotplugEventListener {
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index c1e924e59735..67e2d1868465 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -71,4 +71,5 @@ interface IHdmiControlService {
void clearTimerRecording(int recorderAddress, int sourceType, in byte[] recordSource);
void sendMhlVendorCommand(int portId, int offset, int length, in byte[] data);
void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener);
+ void setStandbyMode(boolean isStandbyModeOn);
}
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 0ee25744488c..612b1356e306 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -60,26 +60,51 @@ public class LogMaker {
return this;
}
+ public LogMaker clearCategory() {
+ entries.remove(MetricsEvent.RESERVED_FOR_LOGBUILDER_CATEGORY);
+ return this;
+ }
+
public LogMaker setType(int type) {
entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_TYPE, type);
return this;
}
+ public LogMaker clearType() {
+ entries.remove(MetricsEvent.RESERVED_FOR_LOGBUILDER_TYPE);
+ return this;
+ }
+
public LogMaker setSubtype(int subtype) {
entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_SUBTYPE, subtype);
return this;
}
+ public LogMaker clearSubtype() {
+ entries.remove(MetricsEvent.RESERVED_FOR_LOGBUILDER_SUBTYPE);
+ return this;
+ }
+
public LogMaker setTimestamp(long timestamp) {
entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_TIMESTAMP, timestamp);
return this;
}
+ public LogMaker clearTimestamp() {
+ entries.remove(MetricsEvent.RESERVED_FOR_LOGBUILDER_TIMESTAMP);
+ return this;
+ }
+
public LogMaker setPackageName(String packageName) {
entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_PACKAGENAME, packageName);
return this;
}
+ public LogMaker clearPackageName() {
+ entries.remove(MetricsEvent.RESERVED_FOR_LOGBUILDER_PACKAGENAME);
+ return this;
+ }
+
public LogMaker setCounterName(String name) {
entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_NAME, name);
return this;
diff --git a/core/java/android/metrics/MetricsReader.java b/core/java/android/metrics/MetricsReader.java
index 079c2c93c05b..dd8a74d1bfae 100644
--- a/core/java/android/metrics/MetricsReader.java
+++ b/core/java/android/metrics/MetricsReader.java
@@ -16,10 +16,15 @@
package android.metrics;
import android.annotation.SystemApi;
+import android.util.EventLog;
+import android.util.EventLog.Event;
+import android.util.Log;
-import com.android.internal.logging.legacy.LegacyConversionLogger;
-import com.android.internal.logging.legacy.EventLogCollector;
+import com.android.internal.logging.MetricsLogger;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.Queue;
/**
@@ -28,41 +33,87 @@ import java.util.Queue;
*/
@SystemApi
public class MetricsReader {
- private EventLogCollector mReader;
- private Queue<LogMaker> mEventQueue;
+ private Queue<LogMaker> mEventQueue = new LinkedList<>();
private long mLastEventMs;
private long mCheckpointMs;
+ private int[] LOGTAGS = { MetricsLogger.LOGTAG };
- /** Open a new session and start reading logs.
+ /**
+ * Read the available logs into a new session.
*
- * Starts reading from the oldest log not already read by this reader object.
- * On first invocation starts from the oldest available log ion the system.
+ * The session will contain events starting from the oldest available
+ * log on the system up to the most recent at the time of this call.
+ *
+ * A call to {@link #checkpoint()} will cause the session to contain
+ * only events that occured after that call.
+ *
+ * This call will not return until the system buffer overflows the
+ * specified timestamp. If the specified timestamp is 0, then the
+ * call will return immediately since any logs 1970 have already been
+ * overwritten (n.b. if the underlying system has the capability to
+ * store many decades of system logs, this call may fail in
+ * interesting ways.)
+ *
+ * @param horizonMs block until this timestamp is overwritten, 0 for non-blocking read.
*/
- public void read(long startMs) {
- EventLogCollector reader = EventLogCollector.getInstance();
- LegacyConversionLogger logger = new LegacyConversionLogger();
- mLastEventMs = reader.collect(logger, startMs);
- mEventQueue = logger.getEvents();
+ public void read(long horizonMs) {
+ ArrayList<Event> nativeEvents = new ArrayList<>();
+ try {
+ EventLog.readEventsOnWrapping(LOGTAGS, horizonMs, nativeEvents);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ mEventQueue.clear();
+ for (EventLog.Event event : nativeEvents) {
+ final long eventTimestampMs = event.getTimeNanos() / 1000000;
+ if (eventTimestampMs > mCheckpointMs) {
+ Object data = event.getData();
+ Object[] objects;
+ if (data instanceof Object[]) {
+ objects = (Object[]) data;
+ } else {
+ // wrap scalar objects
+ objects = new Object[1];
+ objects[0] = data;
+ }
+ mEventQueue.add(new LogMaker(objects)
+ .setTimestamp(eventTimestampMs));
+ mLastEventMs = eventTimestampMs;
+ }
+ }
}
+ /** Cause this session to only contain events that occur after this call. */
public void checkpoint() {
+ // read the log to find the most recent event.
read(0L);
+ // any queued event is now too old, so drop them.
+ mEventQueue.clear();
mCheckpointMs = mLastEventMs;
- mEventQueue = null;
}
+ /**
+ * Rewind the session to the beginning of time and read all available logs.
+ *
+ * A prior call to {@link #checkpoint()} will cause the reader to ignore
+ * any event with a timestamp before the time of that call.
+ *
+ * The underlying log buffer is live: between calls to {@link #reset()}, older
+ * events may be lost from the beginning of the session, and new events may
+ * appear at the end.
+ */
public void reset() {
- read(mCheckpointMs);
+ read(0l);
}
/* Does the current log session have another entry? */
public boolean hasNext() {
- return mEventQueue == null ? false : !mEventQueue.isEmpty();
+ return !mEventQueue.isEmpty();
}
- /* Next entry in the current log session. */
+ /* Return the next entry in the current log session. */
public LogMaker next() {
- return mEventQueue == null ? null : mEventQueue.remove();
+ return mEventQueue.poll();
}
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1259de602f29..14333f732d14 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1069,26 +1069,6 @@ public class ConnectivityManager {
}
/**
- * Request that this callback be invoked at ConnectivityService's earliest
- * convenience with the current satisfying network's LinkProperties.
- * If no such network exists no callback invocation is performed.
- *
- * The callback must have been registered with #requestNetwork() or
- * #registerDefaultNetworkCallback(); callbacks registered with
- * registerNetworkCallback() are not specific to any particular Network so
- * do not cause any updates.
- *
- * @hide
- */
- public void requestLinkProperties(NetworkCallback networkCallback) {
- try {
- mService.requestLinkProperties(networkCallback.networkRequest);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This
* will return {@code null} if the network is unknown.
* <p>This method requires the caller to hold the permission
@@ -1106,26 +1086,6 @@ public class ConnectivityManager {
}
/**
- * Request that this callback be invoked at ConnectivityService's earliest
- * convenience with the current satisfying network's NetworkCapabilities.
- * If no such network exists no callback invocation is performed.
- *
- * The callback must have been registered with #requestNetwork() or
- * #registerDefaultNetworkCallback(); callbacks registered with
- * registerNetworkCallback() are not specific to any particular Network so
- * do not cause any updates.
- *
- * @hide
- */
- public void requestNetworkCapabilities(NetworkCallback networkCallback) {
- try {
- mService.requestNetworkCapabilities(networkCallback.networkRequest);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Gets the URL that should be used for resolving whether a captive portal is present.
* 1. This URL should respond with a 204 response to a GET request to indicate no captive
* portal is present.
@@ -2676,10 +2636,12 @@ public class ConnectivityManager {
public void onLost(Network network) {}
/**
- * Called if no network is found in the given timeout time. If no timeout is given,
- * this will not be called. The associated {@link NetworkRequest} will have already
- * been removed and released, as if {@link #unregisterNetworkCallback} had been called.
- * @hide
+ * Called if no network is found in the timeout time specified in
+ * {@link #requestNetwork(NetworkRequest, int, NetworkCallback)} call. This callback is not
+ * called for the version of {@link #requestNetwork(NetworkRequest, NetworkCallback)}
+ * without timeout. When this callback is invoked the associated
+ * {@link NetworkRequest} will have already been removed and released, as if
+ * {@link #unregisterNetworkCallback(NetworkCallback)} had been called.
*/
public void onUnavailable() {}
@@ -2962,7 +2924,9 @@ public class ConnectivityManager {
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This {@link NetworkRequest} will live until released via
- * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits.
+ * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. A
+ * version of the method which takes a timeout is
+ * {@link #requestNetwork(NetworkRequest, int, NetworkCallback)}.
* Status of the request can be followed by listening to the various
* callbacks described in {@link NetworkCallback}. The {@link Network}
* can be used to direct traffic to the network.
@@ -2995,7 +2959,9 @@ public class ConnectivityManager {
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This {@link NetworkRequest} will live until released via
- * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits.
+ * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. A
+ * version of the method which takes a timeout is
+ * {@link #requestNetwork(NetworkRequest, int, NetworkCallback)}.
* Status of the request can be followed by listening to the various
* callbacks described in {@link NetworkCallback}. The {@link Network}
* can be used to direct traffic to the network.
@@ -3029,13 +2995,25 @@ public class ConnectivityManager {
}
/**
+ * Note: this is a deprecated version of
+ * {@link #requestNetwork(NetworkRequest, int, NetworkCallback)} - please transition code to use
+ * the unhidden version of the function.
+ * TODO: replace all callers with the new version of the API
+ *
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
* by a timeout.
*
- * This function behaves identically to the non-timedout version, but if a suitable
- * network is not found within the given time (in milliseconds) the
- * {@link NetworkCallback#onUnavailable()} callback is called. The request must
- * still be released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)}.
+ * This function behaves identically to the non-timed-out version
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, but if a suitable network
+ * is not found within the given time (in milliseconds) the
+ * {@link NetworkCallback#onUnavailable()} callback is called. The request can still be
+ * released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)} but does
+ * not have to be released if timed-out (it is automatically released). Unregistering a
+ * request that timed out is not an error.
+ *
+ * <p>Do not use this method to poll for the existence of specific networks (e.g. with a small
+ * timeout) - the {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
+ * for that purpose. Calling this method will attempt to bring up the requested network.
*
* <p>This method requires the caller to hold either the
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
@@ -3043,15 +3021,56 @@ public class ConnectivityManager {
* {@link android.provider.Settings.System#canWrite}.</p>
*
* @param request {@link NetworkRequest} describing this request.
- * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
- * the callback must not be shared - it uniquely specifies this request.
- * The callback is invoked on the default internal Handler.
+ * @param networkCallback The callbacks to be utilized for this request. Note
+ * the callbacks must not be shared - they uniquely specify
+ * this request.
* @param timeoutMs The time in milliseconds to attempt looking for a suitable network
- * before {@link NetworkCallback#onUnavailable()} is called.
+ * before {@link NetworkCallback#onUnavailable()} is called. The timeout must
+ * be a positive value (i.e. >0).
* @hide
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
int timeoutMs) {
+ if (timeoutMs <= 0) {
+ throw new IllegalArgumentException("Non-positive timeoutMs: " + timeoutMs);
+ }
+ int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
+ requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
+ }
+
+ /**
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+ * by a timeout.
+ *
+ * This function behaves identically to the non-timed-out version
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, but if a suitable network
+ * is not found within the given time (in milliseconds) the
+ * {@link NetworkCallback#onUnavailable()} callback is called. The request can still be
+ * released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)} but does
+ * not have to be released if timed-out (it is automatically released). Unregistering a
+ * request that timed out is not an error.
+ *
+ * <p>Do not use this method to poll for the existence of specific networks (e.g. with a small
+ * timeout) - {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
+ * for that purpose. Calling this method will attempt to bring up the requested network.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
+ * before {@link NetworkCallback#onUnavailable()} is called. The timeout must
+ * be a positive value (i.e. >0).
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
+ */
+ public void requestNetwork(NetworkRequest request, int timeoutMs,
+ NetworkCallback networkCallback) {
+ if (timeoutMs <= 0) {
+ throw new IllegalArgumentException("Non-positive timeoutMs: " + timeoutMs);
+ }
int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
}
@@ -3063,8 +3082,14 @@ public class ConnectivityManager {
*
* This function behaves identically to the non-timedout version, but if a suitable
* network is not found within the given time (in milliseconds) the
- * {@link NetworkCallback#onUnavailable} callback is called. The request must
- * still be released normally by calling {@link unregisterNetworkCallback(NetworkCallback)}.
+ * {@link NetworkCallback#onUnavailable} callback is called. The request can still be
+ * released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)} but does
+ * not have to be released if timed-out (it is automatically released). Unregistering a
+ * request that timed out is not an error.
+ *
+ * <p>Do not use this method to poll for the existence of specific networks (e.g. with a small
+ * timeout) - {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
+ * for that purpose. Calling this method will attempt to bring up the requested network.
*
* <p>This method requires the caller to hold either the
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
@@ -3072,16 +3097,19 @@ public class ConnectivityManager {
* {@link android.provider.Settings.System#canWrite}.</p>
*
* @param request {@link NetworkRequest} describing this request.
- * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
- * the callback must not be shared - it uniquely specifies this request.
* @param timeoutMs The time in milliseconds to attempt looking for a suitable network
* before {@link NetworkCallback#onUnavailable} is called.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
*
* @hide
*/
- public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
- int timeoutMs, Handler handler) {
+ public void requestNetwork(NetworkRequest request, int timeoutMs,
+ NetworkCallback networkCallback, Handler handler) {
+ if (timeoutMs <= 0) {
+ throw new IllegalArgumentException("Non-positive timeoutMs");
+ }
int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
CallbackHandler cbHandler = new CallbackHandler(handler);
requestNetwork(request, networkCallback, timeoutMs, legacyType, cbHandler);
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 117fa0b476f4..425e49407827 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -156,8 +156,6 @@ interface IConnectivityManager
void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
in PendingIntent operation);
- void requestLinkProperties(in NetworkRequest networkRequest);
- void requestNetworkCapabilities(in NetworkRequest networkRequest);
void releaseNetworkRequest(in NetworkRequest networkRequest);
void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 7b7a21cdae4c..e6fe0d0df76a 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -171,6 +171,43 @@ public class NetworkScoreManager {
*/
public static final int CACHE_FILTER_SCAN_RESULTS = 2;
+ /** @hide */
+ @IntDef({RECOMMENDATIONS_ENABLED_FORCED_OFF, RECOMMENDATIONS_ENABLED_OFF,
+ RECOMMENDATIONS_ENABLED_ON})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RecommendationsEnabledSetting {}
+
+ /**
+ * Recommendations have been forced off.
+ * <p>
+ * This value is never set by any of the NetworkScore classes, it must be set via other means.
+ * This state is also "sticky" and we won't transition out of this state once entered. To move
+ * to a different state this value has to be explicitly set to a different value via
+ * other means.
+ * @hide
+ */
+ public static final int RECOMMENDATIONS_ENABLED_FORCED_OFF = -1;
+
+ /**
+ * Recommendations are not enabled.
+ * <p>
+ * This is a transient state that can be entered when the default recommendation app is enabled
+ * but no longer valid. This state will transition to RECOMMENDATIONS_ENABLED_ON when a valid
+ * recommendation app is enabled.
+ * @hide
+ */
+ public static final int RECOMMENDATIONS_ENABLED_OFF = 0;
+
+ /**
+ * Recommendations are enabled.
+ * <p>
+ * This is a transient state that means a valid recommendation app is active. This state will
+ * transition to RECOMMENDATIONS_ENABLED_OFF if the current and default recommendation apps
+ * become invalid.
+ * @hide
+ */
+ public static final int RECOMMENDATIONS_ENABLED_ON = 1;
+
private final Context mContext;
private final INetworkScoreService mService;
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index bd3231464ccf..2c9ce3f99285 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -839,7 +839,7 @@ public final class NdefRecord implements Parcelable {
if (cf && !inChunk) {
// first chunk
- if (typeLength == 0) {
+ if (typeLength == 0 && tnf != NdefRecord.TNF_UNKNOWN) {
throw new FormatException("expected non-zero type length in first chunk");
}
chunks.clear();
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 14760ab66fbc..e82fe0391430 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -341,7 +341,7 @@ public class BaseBundle {
final int N = fromMap.size();
mMap = new ArrayMap<>(N);
for (int i = 0; i < N; i++) {
- mMap.append(fromMap.keyAt(i), deepcopyValue(fromMap.valueAt(i)));
+ mMap.append(fromMap.keyAt(i), deepCopyValue(fromMap.valueAt(i)));
}
}
} else {
@@ -352,14 +352,14 @@ public class BaseBundle {
}
}
- Object deepcopyValue(Object value) {
+ Object deepCopyValue(Object value) {
if (value == null) {
return null;
}
if (value instanceof Bundle) {
- return ((Bundle)value).deepcopy();
+ return ((Bundle)value).deepCopy();
} else if (value instanceof PersistableBundle) {
- return ((PersistableBundle)value).deepcopy();
+ return ((PersistableBundle)value).deepCopy();
} else if (value instanceof ArrayList) {
return deepcopyArrayList((ArrayList) value);
} else if (value.getClass().isArray()) {
@@ -388,7 +388,7 @@ public class BaseBundle {
final int N = from.size();
ArrayList out = new ArrayList(N);
for (int i=0; i<N; i++) {
- out.add(deepcopyValue(from.get(i)));
+ out.add(deepCopyValue(from.get(i)));
}
return out;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 013972d20e23..dc170ed259de 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2186,7 +2186,11 @@ public abstract class BatteryStats implements Parcelable {
public static final int NETWORK_WIFI_TX_DATA = 3;
public static final int NETWORK_BT_RX_DATA = 4;
public static final int NETWORK_BT_TX_DATA = 5;
- public static final int NUM_NETWORK_ACTIVITY_TYPES = NETWORK_BT_TX_DATA + 1;
+ public static final int NETWORK_MOBILE_BG_RX_DATA = 6;
+ public static final int NETWORK_MOBILE_BG_TX_DATA = 7;
+ public static final int NETWORK_WIFI_BG_RX_DATA = 8;
+ public static final int NETWORK_WIFI_BG_TX_DATA = 9;
+ public static final int NUM_NETWORK_ACTIVITY_TYPES = NETWORK_WIFI_BG_TX_DATA + 1;
public abstract long getNetworkActivityBytes(int type, int which);
public abstract long getNetworkActivityPackets(int type, int which);
@@ -2406,6 +2410,10 @@ public abstract class BatteryStats implements Parcelable {
// Step duration mode: the screen is on, off, dozed, etc; value is Display.STATE_* - 1.
public static final int STEP_LEVEL_MODE_SCREEN_STATE = 0x03;
+ // The largest value for screen state that is tracked in battery states. Any values above
+ // this should be mapped back to one of the tracked values before being tracked here.
+ public static final int MAX_TRACKED_SCREEN_STATE = Display.STATE_DOZE_SUSPEND;
+
// Step duration mode: power save is on.
public static final int STEP_LEVEL_MODE_POWER_SAVE = 0x04;
@@ -3133,8 +3141,8 @@ public abstract class BatteryStats implements Parcelable {
for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
sb.setLength(0);
printWakeLockCheckin(sb, ent.getValue(), rawRealtime, null, which, "");
- dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(),
- sb.toString());
+ dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA,
+ "\"" + ent.getKey() + "\"", sb.toString());
}
}
final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
@@ -3233,16 +3241,39 @@ public abstract class BatteryStats implements Parcelable {
final long wifiWakeup = u.getWifiRadioApWakeupCount(which);
final long btBytesRx = u.getNetworkActivityBytes(NETWORK_BT_RX_DATA, which);
final long btBytesTx = u.getNetworkActivityBytes(NETWORK_BT_TX_DATA, which);
+ // Background data transfers
+ final long mobileBytesBgRx = u.getNetworkActivityBytes(NETWORK_MOBILE_BG_RX_DATA,
+ which);
+ final long mobileBytesBgTx = u.getNetworkActivityBytes(NETWORK_MOBILE_BG_TX_DATA,
+ which);
+ final long wifiBytesBgRx = u.getNetworkActivityBytes(NETWORK_WIFI_BG_RX_DATA, which);
+ final long wifiBytesBgTx = u.getNetworkActivityBytes(NETWORK_WIFI_BG_TX_DATA, which);
+ final long mobilePacketsBgRx = u.getNetworkActivityPackets(NETWORK_MOBILE_BG_RX_DATA,
+ which);
+ final long mobilePacketsBgTx = u.getNetworkActivityPackets(NETWORK_MOBILE_BG_TX_DATA,
+ which);
+ final long wifiPacketsBgRx = u.getNetworkActivityPackets(NETWORK_WIFI_BG_RX_DATA,
+ which);
+ final long wifiPacketsBgTx = u.getNetworkActivityPackets(NETWORK_WIFI_BG_TX_DATA,
+ which);
+
if (mobileBytesRx > 0 || mobileBytesTx > 0 || wifiBytesRx > 0 || wifiBytesTx > 0
|| mobilePacketsRx > 0 || mobilePacketsTx > 0 || wifiPacketsRx > 0
|| wifiPacketsTx > 0 || mobileActiveTime > 0 || mobileActiveCount > 0
- || btBytesRx > 0 || btBytesTx > 0 || mobileWakeup > 0 || wifiWakeup > 0) {
+ || btBytesRx > 0 || btBytesTx > 0 || mobileWakeup > 0 || wifiWakeup > 0
+ || mobileBytesBgRx > 0 || mobileBytesBgTx > 0 || wifiBytesBgRx > 0
+ || wifiBytesBgTx > 0
+ || mobilePacketsBgRx > 0 || mobilePacketsBgTx > 0 || wifiPacketsBgRx > 0
+ || wifiPacketsBgTx > 0) {
dumpLine(pw, uid, category, NETWORK_DATA, mobileBytesRx, mobileBytesTx,
wifiBytesRx, wifiBytesTx,
mobilePacketsRx, mobilePacketsTx,
wifiPacketsRx, wifiPacketsTx,
mobileActiveTime, mobileActiveCount,
- btBytesRx, btBytesTx, mobileWakeup, wifiWakeup);
+ btBytesRx, btBytesTx, mobileWakeup, wifiWakeup,
+ mobileBytesBgRx, mobileBytesBgTx, wifiBytesBgRx, wifiBytesBgTx,
+ mobilePacketsBgRx, mobilePacketsBgTx, wifiPacketsBgRx, wifiPacketsBgTx
+ );
}
// Dump modem controller data, per UID.
@@ -3312,7 +3343,8 @@ public abstract class BatteryStats implements Parcelable {
final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
final int count = timer.getCountLocked(which);
if (totalTime != 0) {
- dumpLine(pw, uid, category, SYNC_DATA, syncs.keyAt(isy), totalTime, count);
+ dumpLine(pw, uid, category, SYNC_DATA, "\"" + syncs.keyAt(isy) + "\"",
+ totalTime, count);
}
}
@@ -3323,7 +3355,8 @@ public abstract class BatteryStats implements Parcelable {
final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
final int count = timer.getCountLocked(which);
if (totalTime != 0) {
- dumpLine(pw, uid, category, JOB_DATA, jobs.keyAt(ij), totalTime, count);
+ dumpLine(pw, uid, category, JOB_DATA, "\"" + jobs.keyAt(ij) + "\"",
+ totalTime, count);
}
}
@@ -3395,8 +3428,8 @@ public abstract class BatteryStats implements Parcelable {
if (userMillis != 0 || systemMillis != 0 || foregroundMillis != 0
|| starts != 0 || numAnrs != 0 || numCrashes != 0) {
- dumpLine(pw, uid, category, PROCESS_DATA, processStats.keyAt(ipr), userMillis,
- systemMillis, foregroundMillis, starts, numAnrs, numCrashes);
+ dumpLine(pw, uid, category, PROCESS_DATA, "\"" + processStats.keyAt(ipr) + "\"",
+ userMillis, systemMillis, foregroundMillis, starts, numAnrs, numCrashes);
}
}
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index d04d6c2e05d3..c1292e7f894b 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -98,9 +98,12 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
/**
* Constructs a Bundle containing a copy of the mappings from the given
- * Bundle.
+ * Bundle. Does only a shallow copy of the original Bundle -- see
+ * {@link #deepCopy()} if that is not what you want.
*
* @param b a Bundle to be copied.
+ *
+ * @see #deepCopy()
*/
public Bundle(Bundle b) {
super(b);
@@ -109,9 +112,10 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
/**
* Constructs a Bundle containing a copy of the mappings from the given
- * PersistableBundle.
+ * PersistableBundle. Does only a shallow copy of the PersistableBundle -- see
+ * {@link PersistableBundle#deepCopy()} if you don't want that.
*
- * @param b a Bundle to be copied.
+ * @param b a PersistableBundle to be copied.
*/
public Bundle(PersistableBundle b) {
super(b);
@@ -209,7 +213,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
* primitive arrays. Other types of objects (such as Parcelable or Serializable)
* are referenced as-is and not copied in any way.
*/
- public Bundle deepcopy() {
+ public Bundle deepCopy() {
Bundle b = new Bundle(false);
b.copyInternal(this, true);
return b;
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 39f4d428a230..5b0e5bbce2f7 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -301,11 +301,6 @@ public class Environment {
}
/** {@hide} */
- public static File getDataProfilesDeForeignDexDirectory(int userId) {
- return buildPath(getDataProfilesDeDirectory(userId), "foreign-dex");
- }
-
- /** {@hide} */
public static File getDataAppDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "app");
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index af05ee7978ce..50b4f8c7facf 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -777,10 +777,15 @@ public class FileUtils {
* "29.5GB" in UI.
*/
public static long roundStorageSize(long size) {
- long res = 1;
- while (res < size) {
- res <<= 1;
+ long val = 1;
+ long pow = 1;
+ while ((val * pow) < size) {
+ val <<= 1;
+ if (val > 512) {
+ val = 1;
+ pow *= 1000;
+ }
}
- return res;
+ return val * pow;
}
}
diff --git a/core/java/android/os/PatternMatcher.java b/core/java/android/os/PatternMatcher.java
index 3890fbfafb32..1f3a1e68c9de 100644
--- a/core/java/android/os/PatternMatcher.java
+++ b/core/java/android/os/PatternMatcher.java
@@ -56,10 +56,8 @@ public class PatternMatcher implements Parcelable {
* with full support for character ranges and the not ({@code ^}) modifier.
* Supported modifiers include star ({@code *}) for zero-or-more, plus ({@code +})
* for one-or-more and full range ({@code {...}}) support. This is a simple
- * evaulation implementation in which matching is done against the pattern in
- * realtime with no backtracking support.
- *
- * {@hide} Pending approval for public API
+ * evaluation implementation in which matching is done against the pattern in
+ * real time with no backtracking support.
*/
public static final int PATTERN_ADVANCED_GLOB = 3;
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index 9b60783737bd..75f9c11873f0 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -76,9 +76,12 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
/**
* Constructs a PersistableBundle containing a copy of the mappings from the given
- * PersistableBundle.
+ * PersistableBundle. Does only a shallow copy of the original PersistableBundle -- see
+ * {@link #deepCopy()} if that is not what you want.
*
* @param b a PersistableBundle to be copied.
+ *
+ * @see #deepCopy()
*/
public PersistableBundle(PersistableBundle b) {
super(b);
@@ -87,7 +90,7 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
/**
- * Constructs a PersistableBundle from a Bundle.
+ * Constructs a PersistableBundle from a Bundle. Does only a shallow copy of the Bundle.
*
* @param b a Bundle to be copied.
*
@@ -167,7 +170,7 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
* primitive arrays. Other types of objects (such as Parcelable or Serializable)
* are referenced as-is and not copied in any way.
*/
- public PersistableBundle deepcopy() {
+ public PersistableBundle deepCopy() {
PersistableBundle b = new PersistableBundle(false);
b.copyInternal(this, true);
return b;
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 819afb4806b0..9db58eec3e1a 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -329,15 +329,38 @@ public class RemoteCallbackList<E extends IInterface> {
}
/**
- * Return the cookies associated with a currently registered callback. Note that this is
+ * Return a currently registered callback. Note that this is
+ * <em>not</em> the same as {@link #getBroadcastItem} and should not be used
+ * interchangeably with it. This method returns the registered callback at the given
+ * index, not the current broadcast state. This means that it is not itself thread-safe:
+ * any call to {@link #register} or {@link #unregister} will change these indices, so you
+ * must do your own thread safety between these to protect from such changes.
+ *
+ * @param index Index of which callback registration to return, from 0 to
+ * {@link #getRegisteredCallbackCount()} - 1.
+ *
+ * @return Returns whatever callback is associated with this index, or null if
+ * {@link #kill()} has been called.
+ */
+ public E getRegisteredCallbackItem(int index) {
+ synchronized (mCallbacks) {
+ if (mKilled) {
+ return null;
+ }
+ return mCallbacks.valueAt(index).mCallback;
+ }
+ }
+
+ /**
+ * Return any cookie associated with a currently registered callback. Note that this is
* <em>not</em> the same as {@link #getBroadcastCookie} and should not be used
- * interchangeably with it. This method returns the current cookied registered at the given
+ * interchangeably with it. This method returns the current cookie registered at the given
* index, not the current broadcast state. This means that it is not itself thread-safe:
* any call to {@link #register} or {@link #unregister} will change these indices, so you
* must do your own thread safety between these to protect from such changes.
*
- * @param index Index of which registration cookie to return from 0 to
- * {@link #getRegisteredCallbackCount()}.
+ * @param index Index of which registration cookie to return, from 0 to
+ * {@link #getRegisteredCallbackCount()} - 1.
*
* @return Returns whatever cookie object is associated with this index, or null if
* {@link #kill()} has been called.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index a1f8dfbf5575..e1fb37b18b77 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -56,6 +56,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.AppFuseMount;
import com.android.internal.os.FuseAppLoop;
import com.android.internal.os.RoSystemProperties;
@@ -1006,7 +1007,8 @@ public class StorageManager {
for (String path : INTERNAL_STORAGE_SIZE_PATHS) {
final long numberBlocks = readLong(path);
if (numberBlocks > 0) {
- return new Pair<>(path, Long.valueOf(numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE));
+ return new Pair<>(path,
+ FileUtils.roundStorageSize(numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE));
}
}
return null;
@@ -1386,6 +1388,7 @@ public class StorageManager {
public @NonNull ParcelFileDescriptor openProxyFileDescriptor(
int mode, ProxyFileDescriptorCallback callback, ThreadFactory factory)
throws IOException {
+ MetricsLogger.count(mContext, "storage_open_proxy_file_descriptor", 1);
// Retry is needed because the mount point mFuseAppLoop is using may be unmounted before
// invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount
// the bridge by calling mountProxyFileDescriptorBridge.
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index c0c5db6ed709..02fa7edb2aae 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -40,6 +40,7 @@ import android.util.AttributeSet;
import android.util.TypedValue;
import android.util.Xml;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -75,12 +76,11 @@ import java.util.List;
* however vary; currently there are two major approaches it may take:
*
* <ul>
- * <li>On a small screen it may display only the headers as a single list
- * when first launched. Selecting one of the header items will re-launch
- * the activity with it only showing the PreferenceFragment of that header.
- * <li>On a large screen in may display both the headers and current
- * PreferenceFragment together as panes. Selecting a header item switches
- * to showing the correct PreferenceFragment for that item.
+ * <li>On a small screen it may display only the headers as a single list when first launched.
+ * Selecting one of the header items will only show the PreferenceFragment of that header (on
+ * Android N and lower a new Activity is launched).
+ * <li>On a large screen in may display both the headers and current PreferenceFragment together as
+ * panes. Selecting a header item switches to showing the correct PreferenceFragment for that item.
* </ul>
*
* <p>Subclasses of PreferenceActivity should implement
@@ -540,6 +540,16 @@ public abstract class PreferenceActivity extends ListActivity implements
}
@Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ // Override home navigation button to call onBackPressed (b/35152749).
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index 90e710f12c08..96dd76b8fe72 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -19,9 +19,12 @@ import android.app.ActivityThread;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
+import android.content.pm.Signature;
import android.database.Cursor;
+import android.graphics.Typeface;
import android.graphics.fonts.FontRequest;
import android.graphics.fonts.FontResult;
import android.net.Uri;
@@ -34,9 +37,13 @@ import android.os.ResultReceiver;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.FileNotFoundException;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/**
* Utility class to deal with Font ContentProviders.
@@ -107,6 +114,13 @@ public class FontsContract {
mPackageManager = mContext.getPackageManager();
}
+ /** @hide */
+ @VisibleForTesting
+ public FontsContract(Context context, PackageManager packageManager) {
+ mContext = context;
+ mPackageManager = packageManager;
+ }
+
// We use a background thread to post the content resolving work for all requests on. This
// thread should be quit/stopped after all requests are done.
private final Runnable mReplaceDispatcherThreadRunnable = new Runnable() {
@@ -133,31 +147,79 @@ public class FontsContract {
mHandler = new Handler(mThread.getLooper());
}
mHandler.post(() -> {
- String providerAuthority = request.getProviderAuthority();
- // TODO: Implement cert checking for non-system apps
- ProviderInfo providerInfo = mPackageManager.resolveContentProvider(
- providerAuthority, PackageManager.MATCH_SYSTEM_ONLY);
+ ProviderInfo providerInfo = getProvider(request);
if (providerInfo == null) {
receiver.send(RESULT_CODE_PROVIDER_NOT_FOUND, null);
return;
}
- Bundle result = getFontFromProvider(request, receiver, providerInfo);
- if (result == null) {
- receiver.send(RESULT_CODE_FONT_NOT_FOUND, null);
- return;
- }
- receiver.send(RESULT_CODE_OK, result);
+ getFontFromProvider(request, receiver, providerInfo.authority);
});
mHandler.removeCallbacks(mReplaceDispatcherThreadRunnable);
mHandler.postDelayed(mReplaceDispatcherThreadRunnable, THREAD_RENEWAL_THRESHOLD_MS);
}
}
- private Bundle getFontFromProvider(FontRequest request, ResultReceiver receiver,
- ProviderInfo providerInfo) {
+ /** @hide */
+ @VisibleForTesting
+ public ProviderInfo getProvider(FontRequest request) {
+ String providerAuthority = request.getProviderAuthority();
+ ProviderInfo info = mPackageManager.resolveContentProvider(providerAuthority, 0);
+ if (info == null) {
+ Log.e(TAG, "Can't find content provider " + providerAuthority);
+ return null;
+ }
+
+ if (!info.packageName.equals(request.getProviderPackage())) {
+ Log.e(TAG, "Found content provider " + providerAuthority + ", but package was not "
+ + request.getProviderPackage());
+ return null;
+ }
+ // Trust system apps without signature checks
+ if (info.applicationInfo.isSystemApp()) {
+ return info;
+ }
+
+ Set<byte[]> signatures;
+ try {
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(info.packageName,
+ PackageManager.GET_SIGNATURES);
+ signatures = convertToSet(packageInfo.signatures);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Can't find content provider " + providerAuthority, e);
+ return null;
+ }
+ List<List<byte[]>> requestCertificatesList = request.getCertificates();
+ for (int i = 0; i < requestCertificatesList.size(); ++i) {
+ final Set<byte[]> requestCertificates = convertToSet(requestCertificatesList.get(i));
+ if (signatures.equals(requestCertificates)) {
+ return info;
+ }
+ }
+ Log.e(TAG, "Certificates don't match for given provider " + providerAuthority);
+ return null;
+ }
+
+ private Set<byte[]> convertToSet(Signature[] signatures) {
+ Set<byte[]> shas = new HashSet<>();
+ for (int i = 0; i < signatures.length; ++i) {
+ shas.add(signatures[i].toByteArray());
+ }
+ return shas;
+ }
+
+ private Set<byte[]> convertToSet(List<byte[]> certs) {
+ Set<byte[]> shas = new HashSet<>();
+ shas.addAll(certs);
+ return shas;
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void getFontFromProvider(FontRequest request, ResultReceiver receiver,
+ String authority) {
ArrayList<FontResult> result = null;
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(providerInfo.authority)
+ .authority(authority)
.build();
try (Cursor cursor = mContext.getContentResolver().query(uri, new String[] { Columns._ID,
Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.STYLE },
@@ -176,13 +238,16 @@ public class FontsContract {
try {
ParcelFileDescriptor pfd =
mContext.getContentResolver().openFileDescriptor(fileUri, "r");
- final int ttcIndex = cursor.getInt(ttcIndexColumnIndex);
- final String variationSettings = cursor.getString(vsColumnIndex);
- final int style = cursor.getInt(styleColumnIndex);
+ final int ttcIndex = ttcIndexColumnIndex != -1
+ ? cursor.getInt(ttcIndexColumnIndex) : 0;
+ final String variationSettings = vsColumnIndex != -1
+ ? cursor.getString(vsColumnIndex) : null;
+ final int style = styleColumnIndex != -1
+ ? cursor.getInt(styleColumnIndex) : Typeface.NORMAL;
result.add(new FontResult(pfd, ttcIndex, variationSettings, style));
} catch (FileNotFoundException e) {
Log.e(TAG, "FileNotFoundException raised when interacting with content "
- + "provider " + providerInfo.authority, e);
+ + "provider " + authority, e);
}
}
}
@@ -190,8 +255,9 @@ public class FontsContract {
if (result != null && !result.isEmpty()) {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(PARCEL_FONT_RESULTS, result);
- return bundle;
+ receiver.send(RESULT_CODE_OK, bundle);
+ return;
}
- return null;
+ receiver.send(RESULT_CODE_FONT_NOT_FOUND, null);
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4e78635580dc..e3a9d8005109 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -295,7 +295,9 @@ public final class Settings {
* In some cases, a matching Activity may not exist, so ensure you
* safeguard against this.
* <p>
- * Input: Nothing.
+ * Input: Optionally, the Intent's data URI can specify the application package name to
+ * directly invoke the management GUI specific to the package name. For example
+ * "package:com.my.app".
* <p>
* Output: Nothing.
*/
@@ -8234,7 +8236,14 @@ public final class Settings {
* Value to specify if network recommendations from
* {@link com.android.server.NetworkScoreService} are enabled.
*
- * Type: int (0 for false, 1 for true)
+ * Type: int
+ * Valid values:
+ * -1 = Forced off
+ * 0 = Disabled
+ * 1 = Enabled
+ *
+ * Most readers of this setting should simply check if value == 1 to determined the
+ * enabled state.
* @hide
*/
@SystemApi
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index ef551ad29623..c457c56a02f8 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -169,35 +169,15 @@ public final class FillResponse implements Parcelable {
private FillResponse(@NonNull Builder builder) {
mDatasets = builder.mDatasets;
- if (false) {
- // TODO(b/33197203, 35727295): this is how mSaveInfo will be set once we don't support
- // FillResponse.addSavableIds()
- mSaveInfo = builder.mSaveInfo;
- if (mSaveInfo != null) {
- mSaveInfo.addSavableIds(mDatasets);
- if (mSaveInfo.getSavableIds() == null) {
- throw new IllegalArgumentException(
- "need to provide at least one savable id on SaveInfo");
- }
+ // TODO(b/33197203, 35727295): this is how mSaveInfo will be set once we don't support
+ // FillResponse.addSavableIds()
+ mSaveInfo = builder.mSaveInfo;
+ if (mSaveInfo != null) {
+ mSaveInfo.addSavableIds(mDatasets);
+ if (mSaveInfo.getSavableIds() == null) {
+ throw new IllegalArgumentException(
+ "need to provide at least one savable id on SaveInfo");
}
- } else {
- // Temporary workaround to support FillResponse.addSavableIds()
- SaveInfo saveInfo = builder.mSaveInfoBuilder != null ? builder.mSaveInfoBuilder.build()
- : builder.mSaveInfo;
-
- // Handle the the case where service didn't call addSavableIds() because it would
- // contain just the ids from the datasets.
- if (saveInfo == null && mDatasets != null) {
- saveInfo = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC).build();
- }
- if (saveInfo != null) {
- saveInfo.addSavableIds(mDatasets);
- if (saveInfo.getSavableIds() == null) {
- throw new IllegalArgumentException(
- "need to provide at least one savable id on SaveInfo");
- }
- }
- mSaveInfo = saveInfo;
}
mExtras = builder.mExtras;
@@ -236,9 +216,6 @@ public final class FillResponse implements Parcelable {
*/
public static final class Builder {
private ArrayList<Dataset> mDatasets;
- // TODO(b/33197203, 35727295): temporary builder use by deprecated addSavableIds() method,
- // should be removed once that method is gone
- private SaveInfo.Builder mSaveInfoBuilder;
private SaveInfo mSaveInfo;
private Bundle mExtras;
private RemoteViews mPresentation;
@@ -316,21 +293,6 @@ public final class FillResponse implements Parcelable {
return this;
}
- /** @hide */
- // TODO(b/33197203, 35727295): remove when not used by clients
- public @NonNull Builder addSavableFields(@Nullable AutoFillId... ids) {
- throwIfDestroyed();
- if (mSaveInfo != null) {
- throw new IllegalStateException("setSaveInfo() already called");
- }
- if (mSaveInfoBuilder == null) {
- mSaveInfoBuilder = new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC);
- }
- mSaveInfoBuilder.addSavableIds(ids);
-
- return this;
- }
-
/**
* Sets the {@link SaveInfo} associated with this response.
*
@@ -340,9 +302,6 @@ public final class FillResponse implements Parcelable {
*/
public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) {
throwIfDestroyed();
- if (mSaveInfoBuilder != null) {
- throw new IllegalStateException("addSavableFields() already called");
- }
mSaveInfo = saveInfo;
return this;
}
@@ -374,8 +333,7 @@ public final class FillResponse implements Parcelable {
public FillResponse build() {
throwIfDestroyed();
- if (mAuthentication == null && mDatasets == null && mSaveInfoBuilder == null
- && mSaveInfo == null) {
+ if (mAuthentication == null && mDatasets == null && mSaveInfo == null) {
throw new IllegalArgumentException("need to provide at least one DataSet or a "
+ "SaveInfo or an authentication with a presentation");
}
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 82e44dc86f89..1087851c853f 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -403,6 +403,7 @@ public final class FontConfig implements Parcelable {
private final String mLanguage;
private final String mVariant;
private final String mProviderAuthority;
+ private final String mProviderPackage;
private final String mQuery;
public Family(String name, List<Font> fonts, String language, String variant) {
@@ -411,18 +412,20 @@ public final class FontConfig implements Parcelable {
mLanguage = language;
mVariant = variant;
mProviderAuthority = null;
+ mProviderPackage = null;
mQuery = null;
}
/**
* @hide
*/
- public Family(String providerAuthority, String query) {
+ public Family(String providerAuthority, String providerPackage, String query) {
mName = null;
mFonts = null;
mLanguage = null;
mVariant = null;
mProviderAuthority = providerAuthority;
+ mProviderPackage = providerPackage;
mQuery = query;
}
@@ -435,6 +438,7 @@ public final class FontConfig implements Parcelable {
mFonts.add(new Font(origin.mFonts.get(i)));
}
mProviderAuthority = origin.mProviderAuthority;
+ mProviderPackage = origin.mProviderPackage;
mQuery = origin.mQuery;
}
@@ -476,6 +480,13 @@ public final class FontConfig implements Parcelable {
/**
* @hide
*/
+ public String getProviderPackage() {
+ return mProviderPackage;
+ }
+
+ /**
+ * @hide
+ */
public String getQuery() {
return mQuery;
}
@@ -498,6 +509,11 @@ public final class FontConfig implements Parcelable {
mProviderAuthority = null;
}
if (in.readInt() == 1) {
+ mProviderPackage = in.readString();
+ } else {
+ mProviderPackage = null;
+ }
+ if (in.readInt() == 1) {
mQuery = in.readString();
} else {
mQuery = null;
@@ -517,6 +533,10 @@ public final class FontConfig implements Parcelable {
if (mProviderAuthority != null) {
out.writeString(mProviderAuthority);
}
+ out.writeInt(mProviderPackage == null ? 0 : 1);
+ if (mProviderPackage != null) {
+ out.writeString(mProviderPackage);
+ }
out.writeInt(mQuery == null ? 0 : 1);
if (mQuery != null) {
out.writeString(mQuery);
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 80ec03e70cb7..c2508a6c92ef 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -42,13 +42,24 @@ public class Hyphenator {
private static String TAG = "Hyphenator";
+ // TODO: Confirm that these are the best values. Various sources suggest (1, 1), but
+ // that appears too small.
+ private static final int INDIC_MIN_PREFIX = 2;
+ private static final int INDIC_MIN_SUFFIX = 2;
+
private final static Object sLock = new Object();
@GuardedBy("sLock")
final static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>();
+ // Reasonable enough values for cases where we have no hyphenation patterns but may be able to
+ // do some automatic hyphenation based on characters. These values would be used very rarely.
+ private static final int DEFAULT_MIN_PREFIX = 2;
+ private static final int DEFAULT_MIN_SUFFIX = 2;
final static Hyphenator sEmptyHyphenator =
- new Hyphenator(StaticLayout.nLoadHyphenator(null, 0), null);
+ new Hyphenator(StaticLayout.nLoadHyphenator(
+ null, 0, DEFAULT_MIN_PREFIX, DEFAULT_MIN_SUFFIX),
+ null);
final private long mNativePtr;
@@ -111,15 +122,26 @@ public class Hyphenator {
return sEmptyHyphenator;
}
- private static Hyphenator loadHyphenator(String languageTag) {
- String patternFilename = "hyph-" + languageTag.toLowerCase(Locale.US) + ".hyb";
+ private static class HyphenationData {
+ final String mLanguageTag;
+ final int mMinPrefix, mMinSuffix;
+ HyphenationData(String languageTag, int minPrefix, int minSuffix) {
+ this.mLanguageTag = languageTag;
+ this.mMinPrefix = minPrefix;
+ this.mMinSuffix = minSuffix;
+ }
+ }
+
+ private static Hyphenator loadHyphenator(HyphenationData data) {
+ String patternFilename = "hyph-" + data.mLanguageTag.toLowerCase(Locale.US) + ".hyb";
File patternFile = new File(getSystemHyphenatorLocation(), patternFilename);
try {
RandomAccessFile f = new RandomAccessFile(patternFile, "r");
try {
FileChannel fc = f.getChannel();
MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
- long nativePtr = StaticLayout.nLoadHyphenator(buf, 0);
+ long nativePtr = StaticLayout.nLoadHyphenator(
+ buf, 0, data.mMinPrefix, data.mMinSuffix);
return new Hyphenator(nativePtr, buf);
} finally {
f.close();
@@ -176,6 +198,46 @@ public class Hyphenator {
{"wal", "und-Ethi"}, // Wolaytta
};
+ private static final HyphenationData[] AVAILABLE_LANGUAGES = {
+ new HyphenationData("as", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Assamese
+ new HyphenationData("bg", 2, 2), // Bulgarian
+ new HyphenationData("bn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Bengali
+ new HyphenationData("cu", 1, 2), // Church Slavonic
+ new HyphenationData("cy", 2, 3), // Welsh
+ new HyphenationData("da", 2, 2), // Danish
+ new HyphenationData("de-1901", 2, 2), // German 1901 orthography
+ new HyphenationData("de-1996", 2, 2), // German 1996 orthography
+ new HyphenationData("de-CH-1901", 2, 2), // Swiss High German 1901 orthography
+ new HyphenationData("en-GB", 2, 3), // British English
+ new HyphenationData("en-US", 2, 3), // American English
+ new HyphenationData("es", 2, 2), // Spanish
+ new HyphenationData("et", 2, 3), // Estonian
+ new HyphenationData("eu", 2, 2), // Basque
+ new HyphenationData("fr", 2, 3), // French
+ new HyphenationData("ga", 2, 3), // Irish
+ new HyphenationData("gu", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Gujarati
+ new HyphenationData("hi", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Hindi
+ new HyphenationData("hr", 2, 2), // Croatian
+ new HyphenationData("hu", 2, 2), // Hungarian
+ // texhyphen sources say Armenian may be (1, 2), but that it needs confirmation.
+ // Going with a more conservative value of (2, 2) for now.
+ new HyphenationData("hy", 2, 2), // Armenian
+ new HyphenationData("kn", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Kannada
+ new HyphenationData("ml", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Malayalam
+ new HyphenationData("mn-Cyrl", 2, 2), // Mongolian in Cyrillic script
+ new HyphenationData("mr", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Marathi
+ new HyphenationData("nb", 2, 2), // Norwegian Bokmål
+ new HyphenationData("nn", 2, 2), // Norwegian Nynorsk
+ new HyphenationData("or", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Oriya
+ new HyphenationData("pa", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Punjabi
+ new HyphenationData("pt", 2, 3), // Portuguese
+ new HyphenationData("sl", 2, 2), // Slovenian
+ new HyphenationData("ta", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Tamil
+ new HyphenationData("te", INDIC_MIN_PREFIX, INDIC_MIN_SUFFIX), // Telugu
+ new HyphenationData("tk", 2, 2), // Turkmen
+ new HyphenationData("und-Ethi", 1, 1), // Any language in Ethiopic script
+ };
+
/**
* Load hyphenation patterns at initialization time. We want to have patterns
* for all locales loaded and ready to use so we don't have to do any file IO
@@ -186,46 +248,11 @@ public class Hyphenator {
public static void init() {
sMap.put(null, null);
- // TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data
- String[] availableLanguages = {
- "as",
- "bg",
- "bn",
- "cu",
- "cy",
- "da",
- "de-1901", "de-1996", "de-CH-1901",
- "en-GB", "en-US",
- "es",
- "et",
- "eu",
- "fr",
- "ga",
- "gu",
- "hi",
- "hr",
- "hu",
- "hy",
- "kn",
- "ml",
- "mn-Cyrl",
- "mr",
- "nb",
- "nn",
- "or",
- "pa",
- "pt",
- "sl",
- "ta",
- "te",
- "tk",
- "und-Ethi",
- };
- for (int i = 0; i < availableLanguages.length; i++) {
- String languageTag = availableLanguages[i];
- Hyphenator h = loadHyphenator(languageTag);
+ for (int i = 0; i < AVAILABLE_LANGUAGES.length; i++) {
+ HyphenationData data = AVAILABLE_LANGUAGES[i];
+ Hyphenator h = loadHyphenator(data);
if (h != null) {
- sMap.put(Locale.forLanguageTag(languageTag), h);
+ sMap.put(Locale.forLanguageTag(data.mLanguageTag), h);
}
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index cb5b073318d8..94c463c8e651 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -1290,7 +1290,8 @@ public class StaticLayout extends Layout {
private static native void nFreeBuilder(long nativePtr);
private static native void nFinishBuilder(long nativePtr);
- /* package */ static native long nLoadHyphenator(ByteBuffer buf, int offset);
+ /* package */ static native long nLoadHyphenator(ByteBuffer buf, int offset,
+ int minPrefix, int minSuffix);
private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
diff --git a/core/java/android/util/AtomicFile.java b/core/java/android/util/AtomicFile.java
index 3aa3447a42d8..2f1abe9a4c8d 100644
--- a/core/java/android/util/AtomicFile.java
+++ b/core/java/android/util/AtomicFile.java
@@ -17,13 +17,15 @@
package android.util;
import android.os.FileUtils;
-import android.util.Log;
+
+import libcore.io.IoUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.function.Consumer;
/**
* Helper class for performing atomic operations on a file by creating a
@@ -244,4 +246,19 @@ public class AtomicFile {
stream.close();
}
}
+
+ /** @hide */
+ public void write(Consumer<FileOutputStream> writeContent) {
+ FileOutputStream out = null;
+ try {
+ out = startWrite();
+ writeContent.accept(out);
+ finishWrite(out);
+ } catch (Throwable t) {
+ failWrite(out);
+ throw ExceptionUtils.propagate(t);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
}
diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
index da0b609dbd9b..87231e106ca3 100644
--- a/core/java/android/util/ExceptionUtils.java
+++ b/core/java/android/util/ExceptionUtils.java
@@ -16,8 +16,11 @@
package android.util;
+import android.annotation.NonNull;
import android.os.ParcelableException;
+import com.android.internal.util.Preconditions;
+
import java.io.IOException;
/**
@@ -51,4 +54,11 @@ public class ExceptionUtils {
public static String getCompleteMessage(Throwable t) {
return getCompleteMessage(null, t);
}
+
+ public static RuntimeException propagate(@NonNull Throwable t) {
+ Preconditions.checkNotNull(t);
+ if (t instanceof Error) throw (Error)t;
+ if (t instanceof RuntimeException) throw (RuntimeException)t;
+ throw new RuntimeException(t);
+ }
}
diff --git a/core/java/android/util/MapCollections.java b/core/java/android/util/MapCollections.java
index 28b788b97aa7..80ab23c8261d 100644
--- a/core/java/android/util/MapCollections.java
+++ b/core/java/android/util/MapCollections.java
@@ -22,6 +22,7 @@ import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Set;
/**
@@ -52,6 +53,7 @@ abstract class MapCollections<K, V> {
@Override
public T next() {
+ if (!hasNext()) throw new NoSuchElementException();
Object res = colGetEntry(mIndex, mOffset);
mIndex++;
mCanRemove = true;
@@ -87,6 +89,7 @@ abstract class MapCollections<K, V> {
@Override
public Map.Entry<K, V> next() {
+ if (!hasNext()) throw new NoSuchElementException();
mIndex++;
mEntryValid = true;
return this;
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 910a6b191a01..ca3985449d92 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -243,12 +243,12 @@ public class Patterns {
public static final String GOOD_IRI_CHAR =
"a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF";
- public static final Pattern IP_ADDRESS
- = Pattern.compile(
+ private static final String IP_ADDRESS_STRING =
"((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]"
+ "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
+ "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
- + "|[1-9][0-9]|[0-9]))");
+ + "|[1-9][0-9]|[0-9]))";
+ public static final Pattern IP_ADDRESS = Pattern.compile(IP_ADDRESS_STRING);
/**
* Valid UCS characters defined in RFC 3987. Excludes space characters.
@@ -298,8 +298,8 @@ public class Patterns {
private static final String HOST_NAME = "(" + IRI_LABEL + "\\.)+" + TLD;
- public static final Pattern DOMAIN_NAME
- = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")");
+ private static final String DOMAIN_NAME_STR = "(" + HOST_NAME + "|" + IP_ADDRESS_STRING + ")";
+ public static final Pattern DOMAIN_NAME = Pattern.compile(DOMAIN_NAME_STR);
private static final String PROTOCOL = "(?i:http|https|rtsp)://";
@@ -323,7 +323,7 @@ public class Patterns {
public static final Pattern WEB_URL = Pattern.compile("("
+ "("
+ "(?:" + PROTOCOL + "(?:" + USER_INFO + ")?" + ")?"
- + "(?:" + DOMAIN_NAME + ")"
+ + "(?:" + DOMAIN_NAME_STR + ")"
+ "(?:" + PORT_NUMBER + ")?"
+ ")"
+ "(" + PATH_AND_QUERY + ")?"
@@ -346,14 +346,14 @@ public class Patterns {
* Regular expression that matches domain names using either {@link #STRICT_HOST_NAME} or
* {@link #IP_ADDRESS}
*/
- private static final Pattern STRICT_DOMAIN_NAME
- = Pattern.compile("(?:" + STRICT_HOST_NAME + "|" + IP_ADDRESS + ")");
+ private static final String STRICT_DOMAIN_NAME = "(?:" + STRICT_HOST_NAME + "|"
+ + IP_ADDRESS_STRING + ")";
/**
* Regular expression that matches domain names without a TLD
*/
private static final String RELAXED_DOMAIN_NAME =
- "(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" +"?)+" + "|" + IP_ADDRESS + ")";
+ "(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" +"?)+" + "|" + IP_ADDRESS_STRING + ")";
/**
* Regular expression to match strings that do not start with a supported protocol. The TLDs
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 7ec7ba7dd162..5494377ceebd 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -302,7 +302,6 @@ public final class Display {
*
* @see #getState
* @see android.os.PowerManager#isInteractive
- * @hide
*/
public static final int STATE_VR = 5;
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index c7340bfa329e..782f3499fb79 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -38,9 +38,11 @@ oneway interface IPinnedStackListener {
* to be changed (ie. after configuration change, aspect ratio change, etc). It then provides
* the components that allow the listener to calculate the movement bounds itself. The
* {@param normalBounds} are also the default bounds that the PiP would be entered in its
- * current state with the aspect ratio applied.
+ * current state with the aspect ratio applied. The {@param animatingBounds} are provided
+ * to indicate the current target bounds of the pinned stack (the final bounds if animating,
+ * the current bounds if not), which may be helpful in calculating dependent animation bounds.
*/
- void onMovementBoundsChanged(in Rect insetBounds, in Rect normalBounds,
+ void onMovementBoundsChanged(in Rect insetBounds, in Rect normalBounds, in Rect animatingBounds,
boolean fromImeAdjustement);
/**
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 16d46666732d..fe9197867484 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -23,9 +23,7 @@ import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -67,33 +65,6 @@ public class NotificationHeaderView extends ViewGroup {
}
}
};
- final AccessibilityDelegate mExpandDelegate = new AccessibilityDelegate() {
-
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (super.performAccessibilityAction(host, action, args)) {
- return true;
- }
- if (action == AccessibilityNodeInfo.ACTION_COLLAPSE
- || action == AccessibilityNodeInfo.ACTION_EXPAND) {
- mExpandClickListener.onClick(mExpandButton);
- return true;
- }
- return false;
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
- // Avoid that the button description is also spoken
- info.setClassName(getClass().getName());
- if (mExpanded) {
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
- } else {
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
- }
- }
- };
private boolean mAcceptAllTouches;
public NotificationHeaderView(Context context) {
@@ -124,9 +95,6 @@ public class NotificationHeaderView extends ViewGroup {
mAppName = findViewById(com.android.internal.R.id.app_name_text);
mHeaderText = findViewById(com.android.internal.R.id.header_text);
mExpandButton = (ImageView) findViewById(com.android.internal.R.id.expand_button);
- if (mExpandButton != null) {
- mExpandButton.setAccessibilityDelegate(mExpandDelegate);
- }
mIcon = (CachingIconView) findViewById(com.android.internal.R.id.icon);
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
}
@@ -295,13 +263,19 @@ public class NotificationHeaderView extends ViewGroup {
private void updateExpandButton() {
int drawableId;
+ int contentDescriptionId;
if (mExpanded) {
drawableId = com.android.internal.R.drawable.ic_collapse_notification;
+ contentDescriptionId
+ = com.android.internal.R.string.expand_button_content_description_expanded;
} else {
drawableId = com.android.internal.R.drawable.ic_expand_notification;
+ contentDescriptionId
+ = com.android.internal.R.string.expand_button_content_description_collapsed;
}
mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
mExpandButton.setColorFilter(mOriginalNotificationColor);
+ mExpandButton.setContentDescription(mContext.getText(contentDescriptionId));
}
public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
@@ -391,7 +365,7 @@ public class NotificationHeaderView extends ViewGroup {
break;
case MotionEvent.ACTION_UP:
if (mTrackGesture) {
- mExpandClickListener.onClick(NotificationHeaderView.this);
+ mExpandButton.performClick();
}
break;
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 519c1e2454df..b718696b2202 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -95,11 +95,6 @@ public class SurfaceControl {
IBinder displayToken, int mode);
private static native void nativeDeferTransactionUntil(long nativeObject,
IBinder handle, long frame);
- private static native void nativeDeferTransactionUntilSurface(long nativeObject,
- long surfaceObject, long frame);
- private static native void nativeReparentChildren(long nativeObject,
- IBinder handle);
- private static native void nativeSeverChildren(long nativeObject);
private static native void nativeSetOverrideScalingMode(long nativeObject,
int scalingMode);
private static native IBinder nativeGetHandle(long nativeObject);
@@ -423,23 +418,7 @@ public class SurfaceControl {
}
public void deferTransactionUntil(IBinder handle, long frame) {
- if (frame > 0) {
- nativeDeferTransactionUntil(mNativeObject, handle, frame);
- }
- }
-
- public void deferTransactionUntil(Surface barrier, long frame) {
- if (frame > 0) {
- nativeDeferTransactionUntilSurface(mNativeObject, barrier.mNativeObject, frame);
- }
- }
-
- public void reparentChildren(IBinder newParentHandle) {
- nativeReparentChildren(mNativeObject, newParentHandle);
- }
-
- public void detachChildren() {
- nativeSeverChildren(mNativeObject);
+ nativeDeferTransactionUntil(mNativeObject, handle, frame);
}
public void setOverrideScalingMode(int scalingMode) {
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index b5912bc1e1c8..3cf5af484625 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -27,7 +27,6 @@ public final class SurfaceSession {
private long mNativeClient; // SurfaceComposerClient*
private static native long nativeCreate();
- private static native long nativeCreateScoped(long surfacePtr);
private static native void nativeDestroy(long ptr);
private static native void nativeKill(long ptr);
@@ -36,10 +35,6 @@ public final class SurfaceSession {
mNativeClient = nativeCreate();
}
- public SurfaceSession(Surface root) {
- mNativeClient = nativeCreateScoped(root.mNativeObject);
- }
-
/* no user serviceable parts here ... */
@Override
protected void finalize() throws Throwable {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 61b12475d542..d2577d48c3d1 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,10 +16,6 @@
package android.view;
-import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_SUBLAYER;
-import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
-import static android.view.WindowManagerPolicy.APPLICATION_PANEL_SUBLAYER;
-
import android.content.Context;
import android.content.res.CompatibilityInfo.Translator;
import android.content.res.Configuration;
@@ -30,12 +26,16 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
+import com.android.internal.view.BaseIWindow;
import com.android.internal.view.SurfaceCallbackHelper;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
@@ -92,8 +92,8 @@ import java.util.concurrent.locks.ReentrantLock;
* positioned asynchronously.</p>
*/
public class SurfaceView extends View {
- private static final String TAG = "SurfaceView";
- private static final boolean DEBUG = false;
+ static private final String TAG = "SurfaceView";
+ static private final boolean DEBUG = false;
final ArrayList<SurfaceHolder.Callback> mCallbacks
= new ArrayList<SurfaceHolder.Callback>();
@@ -102,23 +102,28 @@ public class SurfaceView extends View {
final ReentrantLock mSurfaceLock = new ReentrantLock();
final Surface mSurface = new Surface(); // Current surface in use
+ final Surface mNewSurface = new Surface(); // New surface we are switching to
boolean mDrawingStopped = true;
- // We use this to track if the application has produced a frame
- // in to the Surface. Up until that point, we should be careful not to punch
- // holes.
- boolean mDrawFinished = false;
-
- final Rect mScreenRect = new Rect();
- SurfaceSession mSurfaceSession;
- SurfaceControl mSurfaceControl;
+ final WindowManager.LayoutParams mLayout
+ = new WindowManager.LayoutParams();
+ IWindowSession mSession;
+ MyWindow mWindow;
+ final Rect mVisibleInsets = new Rect();
+ final Rect mWinFrame = new Rect();
+ final Rect mOverscanInsets = new Rect();
+ final Rect mContentInsets = new Rect();
+ final Rect mStableInsets = new Rect();
+ final Rect mOutsets = new Rect();
+ final Rect mBackdropFrame = new Rect();
final Rect mTmpRect = new Rect();
final Configuration mConfiguration = new Configuration();
static final int KEEP_SCREEN_ON_MSG = 1;
- static final int DRAW_FINISHED_MSG = 2;
+ static final int GET_NEW_SURFACE_MSG = 2;
+ static final int UPDATE_WINDOW_MSG = 3;
- int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+ int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
boolean mIsCreating = false;
private volatile boolean mRtHandlingPositionUpdates = false;
@@ -130,9 +135,11 @@ public class SurfaceView extends View {
case KEEP_SCREEN_ON_MSG: {
setKeepScreenOn(msg.arg1 != 0);
} break;
- case DRAW_FINISHED_MSG: {
- mDrawFinished = true;
- invalidate();
+ case GET_NEW_SURFACE_MSG: {
+ handleGetNewSurface();
+ } break;
+ case UPDATE_WINDOW_MSG: {
+ updateWindow();
} break;
}
}
@@ -142,7 +149,7 @@ public class SurfaceView extends View {
= new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
- updateSurface();
+ updateWindow();
}
};
@@ -152,14 +159,13 @@ public class SurfaceView extends View {
public boolean onPreDraw() {
// reposition ourselves where the surface is
mHaveFrame = getWidth() > 0 && getHeight() > 0;
- updateSurface();
+ updateWindow();
return true;
}
};
boolean mRequestedVisible = false;
boolean mWindowVisibility = false;
- boolean mLastWindowVisibility = false;
boolean mViewVisibility = false;
int mRequestedWidth = -1;
int mRequestedHeight = -1;
@@ -175,17 +181,19 @@ public class SurfaceView extends View {
boolean mVisible = false;
int mWindowSpaceLeft = -1;
int mWindowSpaceTop = -1;
- int mSurfaceWidth = -1;
- int mSurfaceHeight = -1;
+ int mWindowSpaceWidth = -1;
+ int mWindowSpaceHeight = -1;
int mFormat = -1;
final Rect mSurfaceFrame = new Rect();
int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
+ boolean mUpdateWindowNeeded;
+ boolean mReportDrawNeeded;
private Translator mTranslator;
+ private int mWindowInsetLeft;
+ private int mWindowInsetTop;
private boolean mGlobalListenersAdded;
- private int mSurfaceFlags = SurfaceControl.HIDDEN;
-
public SurfaceView(Context context) {
this(context, null);
}
@@ -219,8 +227,11 @@ public class SurfaceView extends View {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mParent.requestTransparentRegion(this);
+ mSession = getWindowSession();
+ mLayout.token = getWindowToken();
+ mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle());
+ mLayout.packageName = mContext.getOpPackageName();
mViewVisibility = getVisibility() == VISIBLE;
- mRequestedVisible = mViewVisibility && mWindowVisibility;
if (!mGlobalListenersAdded) {
ViewTreeObserver observer = getViewTreeObserver();
@@ -235,7 +246,7 @@ public class SurfaceView extends View {
super.onWindowVisibilityChanged(visibility);
mWindowVisibility = visibility == VISIBLE;
mRequestedVisible = mWindowVisibility && mViewVisibility;
- updateSurface();
+ updateWindow();
}
@Override
@@ -253,7 +264,7 @@ public class SurfaceView extends View {
requestLayout();
}
mRequestedVisible = newRequestedVisible;
- updateSurface();
+ updateWindow();
}
@Override
@@ -266,14 +277,19 @@ public class SurfaceView extends View {
}
mRequestedVisible = false;
-
- updateSurface();
- if (mSurfaceControl != null) {
- mSurfaceControl.destroy();
+ updateWindow();
+ mHaveFrame = false;
+ if (mWindow != null) {
+ try {
+ mSession.remove(mWindow);
+ } catch (RemoteException ex) {
+ // Not much we can do here...
+ }
+ mWindow = null;
}
- mSurfaceControl = null;
+ mSession = null;
+ mLayout.token = null;
- mHaveFrame = false;
super.onDetachedFromWindow();
}
@@ -292,13 +308,13 @@ public class SurfaceView extends View {
@Override
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean result = super.setFrame(left, top, right, bottom);
- updateSurface();
+ updateWindow();
return result;
}
@Override
public boolean gatherTransparentRegion(Region region) {
- if (isAboveParent()) {
+ if (mWindowType == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
return super.gatherTransparentRegion(region);
}
@@ -325,7 +341,7 @@ public class SurfaceView extends View {
@Override
public void draw(Canvas canvas) {
- if (mDrawFinished && !isAboveParent()) {
+ if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
// draw() is not called when SKIP_DRAW is set
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
// punch a whole in the view-hierarchy below us
@@ -337,8 +353,8 @@ public class SurfaceView extends View {
@Override
protected void dispatchDraw(Canvas canvas) {
- if (mDrawFinished && !isAboveParent()) {
- // draw() is not called when SKIP_DRAW is set
+ if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
+ // if SKIP_DRAW is cleared, draw() has already punched a hole
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
// punch a whole in the view-hierarchy below us
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
@@ -359,8 +375,9 @@ public class SurfaceView extends View {
* <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
*/
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
- mSubLayer = isMediaOverlay
- ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
+ mWindowType = isMediaOverlay
+ ? WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
+ : WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
}
/**
@@ -378,9 +395,12 @@ public class SurfaceView extends View {
*/
public void setZOrderOnTop(boolean onTop) {
if (onTop) {
- mSubLayer = APPLICATION_PANEL_SUBLAYER;
+ mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+ // ensures the surface is placed below the IME
+ mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else {
- mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+ mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+ mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
}
@@ -398,32 +418,31 @@ public class SurfaceView extends View {
*/
public void setSecure(boolean isSecure) {
if (isSecure) {
- mSurfaceFlags |= SurfaceControl.SECURE;
+ mLayout.flags |= WindowManager.LayoutParams.FLAG_SECURE;
} else {
- mSurfaceFlags &= ~SurfaceControl.SECURE;
+ mLayout.flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
}
}
- private Rect getParentSurfaceInsets() {
- final ViewRootImpl root = getViewRootImpl();
- if (root == null) {
- return null;
- } else {
- return root.mWindowAttributes.surfaceInsets;
- }
+ /**
+ * Hack to allow special layering of windows. The type is one of the
+ * types in WindowManager.LayoutParams. This is a hack so:
+ * @hide
+ */
+ public void setWindowType(int type) {
+ mWindowType = type;
}
/** @hide */
- protected void updateSurface() {
+ protected void updateWindow() {
if (!mHaveFrame) {
return;
}
ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
- return;
+ if (viewRoot != null) {
+ mTranslator = viewRoot.mTranslator;
}
- mTranslator = viewRoot.mTranslator;
if (mTranslator != null) {
mSurface.setCompatibilityTranslator(mTranslator);
}
@@ -433,15 +452,17 @@ public class SurfaceView extends View {
int myHeight = mRequestedHeight;
if (myHeight <= 0) myHeight = getHeight();
+ final boolean creating = mWindow == null;
final boolean formatChanged = mFormat != mRequestedFormat;
- final boolean creating = (mSurfaceControl == null || formatChanged)
- && mRequestedVisible;
- final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
+ final boolean sizeChanged = mWindowSpaceWidth != myWidth || mWindowSpaceHeight != myHeight;
final boolean visibleChanged = mVisible != mRequestedVisible;
- final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
+ final boolean layoutSizeChanged = getWidth() != mLayout.width
+ || getHeight() != mLayout.height;
+
boolean redrawNeeded = false;
- if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) {
+ if (creating || formatChanged || sizeChanged || visibleChanged
+ || mUpdateWindowNeeded || mReportDrawNeeded) {
getLocationInWindow(mLocation);
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
@@ -455,77 +476,93 @@ public class SurfaceView extends View {
final boolean visible = mVisible = mRequestedVisible;
mWindowSpaceLeft = mLocation[0];
mWindowSpaceTop = mLocation[1];
- mSurfaceWidth = myWidth;
- mSurfaceHeight = myHeight;
+ mWindowSpaceWidth = myWidth;
+ mWindowSpaceHeight = myHeight;
mFormat = mRequestedFormat;
- mLastWindowVisibility = mWindowVisibility;
- mScreenRect.left = mWindowSpaceLeft;
- mScreenRect.top = mWindowSpaceTop;
- mScreenRect.right = mWindowSpaceLeft + getWidth();
- mScreenRect.bottom = mWindowSpaceTop + getHeight();
+ // Scaling/Translate window's layout here because mLayout is not used elsewhere.
+
+ // Places the window relative
+ mLayout.x = mWindowSpaceLeft;
+ mLayout.y = mWindowSpaceTop;
+ mLayout.width = getWidth();
+ mLayout.height = getHeight();
if (mTranslator != null) {
- mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+ mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
}
- final Rect surfaceInsets = getParentSurfaceInsets();
- mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
+ mLayout.format = mRequestedFormat;
+ mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_SCALED
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ ;
+ if (!creating && !sizeChanged) {
+ mLayout.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
+ } else {
+ mLayout.privateFlags &=
+ ~WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY;
+ }
- if (creating) {
- mSurfaceSession = new SurfaceSession(viewRoot.mSurface);
- mSurfaceControl = new SurfaceControl(mSurfaceSession,
- "SurfaceView - " + viewRoot.getTitle().toString(),
- mSurfaceWidth, mSurfaceHeight, mFormat,
- mSurfaceFlags);
+ if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
+ mLayout.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
+ }
+ mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+ | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+
+ if (mWindow == null) {
+ Display display = getDisplay();
+ mWindow = new MyWindow(this);
+ mLayout.type = mWindowType;
+ mLayout.gravity = Gravity.START|Gravity.TOP;
+ mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
+ mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets,
+ mStableInsets);
}
- boolean realSizeChanged = false;
+ boolean realSizeChanged;
+ boolean reportDrawNeeded;
+
+ int relayoutResult;
mSurfaceLock.lock();
try {
+ mUpdateWindowNeeded = false;
+ reportDrawNeeded = mReportDrawNeeded;
+ mReportDrawNeeded = false;
mDrawingStopped = !visible;
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "Cur surface: " + mSurface);
- SurfaceControl.openTransaction();
- try {
- mSurfaceControl.setLayer(mSubLayer);
- if (mViewVisibility) {
- mSurfaceControl.show();
- } else {
- mSurfaceControl.hide();
- }
-
- // While creating the surface, we will set it's initial
- // geometry. Outside of that though, we should generally
- // leave it to the RenderThread.
- if (creating || !mRtHandlingPositionUpdates) {
- mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
- mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
- 0.0f, 0.0f,
- mScreenRect.height() / (float) mSurfaceHeight);
- }
- if (sizeChanged) {
- mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
- }
- } finally {
- SurfaceControl.closeTransaction();
+ relayoutResult = mSession.relayout(
+ mWindow, mWindow.mSeq, mLayout, mWindowSpaceWidth, mWindowSpaceHeight,
+ visible ? VISIBLE : GONE,
+ WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
+ mWinFrame, mOverscanInsets, mContentInsets,
+ mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
+ mConfiguration, mNewSurface);
+ if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
+ reportDrawNeeded = true;
}
- if (sizeChanged || creating) {
- redrawNeeded = true;
- }
+ if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ + "New surface: " + mNewSurface
+ + ", vis=" + visible + ", frame=" + mWinFrame);
mSurfaceFrame.left = 0;
mSurfaceFrame.top = 0;
if (mTranslator == null) {
- mSurfaceFrame.right = mSurfaceWidth;
- mSurfaceFrame.bottom = mSurfaceHeight;
+ mSurfaceFrame.right = mWinFrame.width();
+ mSurfaceFrame.bottom = mWinFrame.height();
} else {
float appInvertedScale = mTranslator.applicationInvertedScale;
- mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
- mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
+ mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
+ mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
}
final int surfaceWidth = mSurfaceFrame.right;
@@ -539,11 +576,12 @@ public class SurfaceView extends View {
}
try {
- redrawNeeded |= visible && !mDrawFinished;
+ redrawNeeded |= creating | reportDrawNeeded;
SurfaceHolder.Callback callbacks[] = null;
- final boolean surfaceChanged = creating;
+ final boolean surfaceChanged = (relayoutResult
+ & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
mSurfaceCreated = false;
if (mSurface.isValid()) {
@@ -570,10 +608,7 @@ public class SurfaceView extends View {
}
}
- if (creating) {
- mSurface.copyFrom(mSurfaceControl);
- }
-
+ mSurface.transferFrom(mNewSurface);
if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
mSurfaceCreated = true;
@@ -606,55 +641,53 @@ public class SurfaceView extends View {
callbacks = getSurfaceCallbacks();
}
SurfaceCallbackHelper sch =
- new SurfaceCallbackHelper(this::onDrawFinished);
+ new SurfaceCallbackHelper(mSession, mWindow);
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
}
}
} finally {
mIsCreating = false;
- if (mSurfaceControl != null && !mSurfaceCreated) {
- mSurfaceControl.destroy();
- mSurfaceControl = null;
- }
+ mSession.performDeferredDestroy(mWindow);
}
- } catch (Exception ex) {
+ } catch (RemoteException ex) {
Log.e(TAG, "Exception from relayout", ex);
}
if (DEBUG) Log.v(
- TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top
- + " w=" + mScreenRect.width() + " h=" + mScreenRect.height()
- + ", frame=" + mSurfaceFrame);
+ TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
+ " w=" + mLayout.width + " h=" + mLayout.height +
+ ", frame=" + mSurfaceFrame);
} else {
// Calculate the window position in case RT loses the window
// and we need to fallback to a UI-thread driven position update
- getLocationInSurface(mLocation);
+ getLocationInWindow(mLocation);
final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
|| mWindowSpaceTop != mLocation[1];
- final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
- || getHeight() != mScreenRect.height();
if (positionChanged || layoutSizeChanged) { // Only the position has changed
mWindowSpaceLeft = mLocation[0];
mWindowSpaceTop = mLocation[1];
- // For our size changed check, we keep mScreenRect.width() and mScreenRect.height()
+ // For our size changed check, we keep mLayout.width and mLayout.height
// in view local space.
- mLocation[0] = getWidth();
- mLocation[1] = getHeight();
+ mLocation[0] = mLayout.width = getWidth();
+ mLocation[1] = mLayout.height = getHeight();
+
+ transformFromViewToWindowSpace(mLocation);
- mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
+ mTmpRect.set(mWindowSpaceLeft, mWindowSpaceTop,
mLocation[0], mLocation[1]);
if (mTranslator != null) {
- mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+ mTranslator.translateRectInAppWindowToScreen(mTmpRect);
}
if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
try {
- if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " +
+ if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition UI, " +
"postion = [%d, %d, %d, %d]", System.identityHashCode(this),
- mScreenRect.left, mScreenRect.top,
- mScreenRect.right, mScreenRect.bottom));
- setParentSpaceRectangle(mScreenRect, -1);
- } catch (Exception ex) {
+ mTmpRect.left, mTmpRect.top,
+ mTmpRect.right, mTmpRect.bottom));
+ mSession.repositionChild(mWindow, mTmpRect.left, mTmpRect.top,
+ mTmpRect.right, mTmpRect.bottom, -1, mTmpRect);
+ } catch (RemoteException ex) {
Log.e(TAG, "Exception from relayout", ex);
}
}
@@ -662,43 +695,20 @@ public class SurfaceView extends View {
}
}
- private void onDrawFinished() {
- if (DEBUG) {
- Log.i(TAG, System.identityHashCode(this) + " "
- + "finishedDrawing");
- }
- mHandler.sendEmptyMessage(DRAW_FINISHED_MSG);
- }
-
- private void setParentSpaceRectangle(Rect position, long frameNumber) {
- ViewRootImpl viewRoot = getViewRootImpl();
-
- SurfaceControl.openTransaction();
- try {
- if (frameNumber > 0) {
- mSurfaceControl.deferTransactionUntil(viewRoot.mSurface, frameNumber);
- }
- mSurfaceControl.setPosition(position.left, position.top);
- mSurfaceControl.setMatrix(position.width() / (float) mSurfaceWidth,
- 0.0f, 0.0f,
- position.height() / (float) mSurfaceHeight);
- } finally {
- SurfaceControl.closeTransaction();
- }
- }
-
private Rect mRTLastReportedPosition = new Rect();
/**
* Called by native by a Rendering Worker thread to update the window position
* @hide
*/
- public final void updateSurfacePosition_renderWorker(long frameNumber,
+ public final void updateWindowPosition_renderWorker(long frameNumber,
int left, int top, int right, int bottom) {
- if (mSurfaceControl == null) {
+ IWindowSession session = mSession;
+ MyWindow window = mWindow;
+ if (session == null || window == null) {
+ // Guess we got detached, that sucks
return;
}
-
// TODO: This is teensy bit racey in that a brand new SurfaceView moving on
// its 2nd frame if RenderThread is running slowly could potentially see
// this as false, enter the branch, get pre-empted, then this comes along
@@ -716,29 +726,35 @@ public class SurfaceView extends View {
}
try {
if (DEBUG) {
- Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " +
+ Log.d(TAG, String.format("%d updateWindowPosition RenderWorker, frameNr = %d, " +
"postion = [%d, %d, %d, %d]", System.identityHashCode(this),
frameNumber, left, top, right, bottom));
}
- mRTLastReportedPosition.set(left, top, right, bottom);
- setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
+ // Just using mRTLastReportedPosition as a dummy rect here
+ session.repositionChild(window, left, top, right, bottom,
+ frameNumber,
+ mRTLastReportedPosition);
// Now overwrite mRTLastReportedPosition with our values
- } catch (Exception ex) {
+ mRTLastReportedPosition.set(left, top, right, bottom);
+ } catch (RemoteException ex) {
Log.e(TAG, "Exception from repositionChild", ex);
}
}
/**
- * Called by native on RenderThread to notify that the view is no longer in the
+ * Called by native on RenderThread to notify that the window is no longer in the
* draw tree. UI thread is blocked at this point.
* @hide
*/
- public final void surfacePositionLost_uiRtSync(long frameNumber) {
+ public final void windowPositionLost_uiRtSync(long frameNumber) {
if (DEBUG) {
Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
System.identityHashCode(this), frameNumber));
}
- if (mSurfaceControl == null) {
+ IWindowSession session = mSession;
+ MyWindow window = mWindow;
+ if (session == null || window == null) {
+ // We got detached prior to receiving this, abort
return;
}
if (mRtHandlingPositionUpdates) {
@@ -747,14 +763,19 @@ public class SurfaceView extends View {
// safely access other member variables at this time.
// So do what the UI thread would have done if RT wasn't handling position
// updates.
- if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
+ mTmpRect.set(mLayout.x, mLayout.y,
+ mLayout.x + mLayout.width,
+ mLayout.y + mLayout.height);
+
+ if (!mTmpRect.isEmpty() && !mTmpRect.equals(mRTLastReportedPosition)) {
try {
- if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " +
+ if (DEBUG) Log.d(TAG, String.format("%d updateWindowPosition, " +
"postion = [%d, %d, %d, %d]", System.identityHashCode(this),
- mScreenRect.left, mScreenRect.top,
- mScreenRect.right, mScreenRect.bottom));
- setParentSpaceRectangle(mScreenRect, frameNumber);
- } catch (Exception ex) {
+ mTmpRect.left, mTmpRect.top,
+ mTmpRect.right, mTmpRect.bottom));
+ session.repositionChild(window, mTmpRect.left, mTmpRect.top,
+ mTmpRect.right, mTmpRect.bottom, frameNumber, mWinFrame);
+ } catch (RemoteException ex) {
Log.e(TAG, "Exception from relayout", ex);
}
}
@@ -771,6 +792,10 @@ public class SurfaceView extends View {
return callbacks;
}
+ void handleGetNewSurface() {
+ updateWindow();
+ }
+
/**
* Check to see if the surface has fixed size dimensions or if the surface's
* dimensions are dimensions are dependent on its current layout.
@@ -782,8 +807,65 @@ public class SurfaceView extends View {
return (mRequestedWidth != -1 || mRequestedHeight != -1);
}
- private boolean isAboveParent() {
- return mSubLayer >= 0;
+ private static class MyWindow extends BaseIWindow {
+ private final WeakReference<SurfaceView> mSurfaceView;
+
+ public MyWindow(SurfaceView surfaceView) {
+ mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
+ }
+
+ @Override
+ public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
+ Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
+ Configuration newConfig, Rect backDropRect, boolean forceLayout,
+ boolean alwaysConsumeNavBar, int displayId) {
+ SurfaceView surfaceView = mSurfaceView.get();
+ if (surfaceView != null) {
+ if (DEBUG) Log.v(TAG, surfaceView + " got resized: w=" + frame.width()
+ + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight);
+ surfaceView.mSurfaceLock.lock();
+ try {
+ if (reportDraw) {
+ surfaceView.mUpdateWindowNeeded = true;
+ surfaceView.mReportDrawNeeded = true;
+ surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
+ } else if (surfaceView.mWinFrame.width() != frame.width()
+ || surfaceView.mWinFrame.height() != frame.height()
+ || forceLayout) {
+ surfaceView.mUpdateWindowNeeded = true;
+ surfaceView.mHandler.sendEmptyMessage(UPDATE_WINDOW_MSG);
+ }
+ } finally {
+ surfaceView.mSurfaceLock.unlock();
+ }
+ }
+ }
+
+ @Override
+ public void dispatchAppVisibility(boolean visible) {
+ // The point of SurfaceView is to let the app control the surface.
+ }
+
+ @Override
+ public void dispatchGetNewSurface() {
+ SurfaceView surfaceView = mSurfaceView.get();
+ if (surfaceView != null) {
+ Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
+ surfaceView.mHandler.sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
+ Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
+ }
+
+ @Override
+ public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
+ }
+
+ int mCurWidth = -1;
+ int mCurHeight = -1;
}
private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
@@ -831,14 +913,15 @@ public class SurfaceView extends View {
@Override
public void setFormat(int format) {
+
// for backward compatibility reason, OPAQUE always
// means 565 for SurfaceView
if (format == PixelFormat.OPAQUE)
format = PixelFormat.RGB_565;
mRequestedFormat = format;
- if (mSurfaceControl != null) {
- updateSurface();
+ if (mWindow != null) {
+ updateWindow();
}
}
@@ -899,10 +982,10 @@ public class SurfaceView extends View {
mSurfaceLock.lock();
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
- + mDrawingStopped + ", surfaceControl=" + mSurfaceControl);
+ + mDrawingStopped + ", win=" + mWindow);
Canvas c = null;
- if (!mDrawingStopped && mSurfaceControl != null) {
+ if (!mDrawingStopped && mWindow != null) {
try {
if (hardware) {
c = mSurface.lockHardwareCanvas();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 040a59b134f9..8cfd6a7b885c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1115,6 +1115,89 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
@AutoFillHint private int mAutoFillHint;
+ /** @hide */
+ @IntDef({
+ AUTOFILL_TYPE_NONE,
+ AUTOFILL_TYPE_TEXT,
+ AUTOFILL_TYPE_TOGGLE,
+ AUTOFILL_TYPE_LIST,
+ AUTOFILL_TYPE_DATE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AutofillType {}
+
+ /**
+ * Autofill type for views that cannot be autofilled.
+ */
+ public static final int AUTOFILL_TYPE_NONE = 0;
+
+ /**
+ * Autofill type for a text field, which is filled by a {@link CharSequence}.
+ *
+ * <p>{@link AutoFillValue} instances for autofilling a {@link View} can be obtained through
+ * {@link AutoFillValue#forText(CharSequence)}, and the value passed to auto-fill a
+ * {@link View} can be fetched through {@link AutoFillValue#getTextValue()}.
+ */
+ public static final int AUTOFILL_TYPE_TEXT = 1;
+
+ /**
+ * Autofill type for a togglable field, which is filled by a {@code boolean}.
+ *
+ * <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through
+ * {@link AutoFillValue#forToggle(boolean)}, and the value passed to auto-fill a
+ * {@link View} can be fetched through {@link AutoFillValue#getToggleValue()}.
+ */
+ public static final int AUTOFILL_TYPE_TOGGLE = 2;
+
+ /**
+ * Autofill type for a selection list field, which is filled by an {@code int}
+ * representing the element index inside the list (starting at {@code 0}).
+ *
+ * <p>{@link AutoFillValue} instances for autofilling a {@link View} can be obtained through
+ * {@link AutoFillValue#forList(int)}, and the value passed to auto-fill a
+ * {@link View} can be fetched through {@link AutoFillValue#getListValue()}.
+ *
+ * <p>The available options in the selection list are typically provided by
+ * {@link android.app.assist.AssistStructure.ViewNode#getAutoFillOptions()}.
+ */
+ public static final int AUTOFILL_TYPE_LIST = 3;
+
+
+ /**
+ * Autofill type for a field that contains a date, which is represented by a long representing
+ * the number of milliseconds since the standard base time known as "the epoch", namely
+ * January 1, 1970, 00:00:00 GMT (see {@link java.util.Date#getTime()}.
+ *
+ * <p>{@link AutoFillValue} instances for autofilling a {@link View} can be obtained through
+ * {@link AutoFillValue#forDate(long)}, and the values passed to
+ * auto-fill a {@link View} can be fetched through {@link AutoFillValue#getDateValue()}.
+ */
+ public static final int AUTOFILL_TYPE_DATE = 4;
+
+ /** @hide */
+ @IntDef({
+ IMPORTANT_FOR_AUTOFILL_AUTO,
+ IMPORTANT_FOR_AUTOFILL_YES,
+ IMPORTANT_FOR_AUTOFILL_NO
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AutofillImportance {}
+
+ /**
+ * Automatically determine whether a view is important for auto-fill.
+ */
+ public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0x0;
+
+ /**
+ * The view is important for important for auto-fill.
+ */
+ public static final int IMPORTANT_FOR_AUTOFILL_YES = 0x1;
+
+ /**
+ * The view is not important for auto-fill.
+ */
+ public static final int IMPORTANT_FOR_AUTOFILL_NO = 0x2;
+
/**
* This view is enabled. Interpretation varies by subclass.
* Use with ENABLED_MASK when calling setFlags.
@@ -2686,7 +2769,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 1 PFLAG3_FINGER_DOWN
* 1 PFLAG3_FOCUSED_BY_DEFAULT
* 11 PFLAG3_AUTO_FILL_MODE_MASK
- * xx * NO LONGER NEEDED, SHOULD BE REUSED *
+ * 11 PFLAG3_IMPORTANT_FOR_AUTOFILL
* 1 PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
* 1 PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
* 1 PFLAG3_TEMPORARY_DETACH
@@ -2924,6 +3007,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
| AUTO_FILL_MODE_AUTO | AUTO_FILL_MODE_MANUAL) << PFLAG3_AUTO_FILL_MODE_SHIFT;
/**
+ * Shift for the bits in {@link #mPrivateFlags3} related to the
+ * "importantForAutofill" attribute.
+ */
+ static final int PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT = 21;
+
+ /**
+ * Mask for obtaining the bits which specify how to determine
+ * whether a view is important for autofill.
+ */
+ static final int PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK = (IMPORTANT_FOR_AUTOFILL_AUTO
+ | IMPORTANT_FOR_AUTOFILL_YES | IMPORTANT_FOR_AUTOFILL_NO)
+ << PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT;
+
+ /**
* Whether this view has rendered elements that overlap (see {@link
* #hasOverlappingRendering()}, {@link #forceHasOverlappingRendering(boolean)}, and
* {@link #getHasOverlappingRendering()} ). The value in this bit is only valid when
@@ -4950,6 +5047,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setAutoFillHint(a.getInt(attr, AUTO_FILL_HINT_NONE));
}
break;
+ case R.styleable.View_importantForAutofill:
+ if (a.peekValue(attr) != null) {
+ setImportantForAutofill(a.getInt(attr, IMPORTANT_FOR_AUTOFILL_AUTO));
+ }
+ break;
}
}
@@ -7178,14 +7280,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
if (forAutoFill) {
- final AutoFillType autoFillType = getAutoFillType();
+ final @AutofillType int autofillType = getAutofillType();
// Don't need to fill auto-fill info if view does not support it.
// For example, only TextViews that are editable support auto-fill
- if (autoFillType != null) {
+ if (autofillType != AUTOFILL_TYPE_NONE) {
// The auto-fill id needs to be unique, but its value doesn't matter, so it's better
// to reuse the accessibility id to save space.
structure.setAutoFillId(getAccessibilityViewId());
- structure.setAutoFillType(autoFillType);
+ structure.setAutofillType(autofillType);
structure.setAutoFillHint(getAutoFillHint());
structure.setAutoFillValue(getAutoFillValue());
}
@@ -7291,7 +7393,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Automatically fills the content of this view with the {@code value}.
*
- * <p>By default does nothing, but views should override it (and {@link #getAutoFillType()},
+ * <p>By default does nothing, but views should override it (and {@link #getAutofillType()},
* {@link #getAutoFillValue()}, and {@link #onProvideAutoFillStructure(ViewStructure, int)}
* to support the AutoFill Framework.
*
@@ -7330,15 +7432,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * @deprecated TODO(b/35956626): remove once clients use getAutoFilltype
+ */
+ @Deprecated
+ @Nullable
+ public final AutoFillType getAutoFillType() {
+ switch (getAutofillType()) {
+ case AUTOFILL_TYPE_TEXT:
+ return AutoFillType.forText();
+ case AUTOFILL_TYPE_TOGGLE:
+ return AutoFillType.forToggle();
+ case AUTOFILL_TYPE_LIST:
+ return AutoFillType.forList();
+ case AUTOFILL_TYPE_DATE:
+ return AutoFillType.forDate();
+ default:
+ return null;
+ }
+ }
+
+ /**
* Describes the auto-fill type that should be used on calls to
* {@link #autoFill(AutoFillValue)} and {@link #autoFillVirtual(int, AutoFillValue)}.
*
- * <p>By default returns {@code null}, but views should override it (and
+ * <p>By default returns {@link #AUTOFILL_TYPE_NONE}, but views should override it (and
* {@link #autoFill(AutoFillValue)} to support the AutoFill Framework.
*/
- @Nullable
- public AutoFillType getAutoFillType() {
- return null;
+ public @AutofillType int getAutofillType() {
+ return AUTOFILL_TYPE_NONE;
}
/**
@@ -7357,7 +7478,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* Gets the {@link View}'s current auto-fill value.
*
* <p>By default returns {@code null}, but views should override it (and
- * {@link #autoFill(AutoFillValue)}, and {@link #getAutoFillType()} to support the AutoFill
+ * {@link #autoFill(AutoFillValue)}, and {@link #getAutofillType()} to support the AutoFill
* Framework.
*/
@Nullable
@@ -7365,13 +7486,125 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return null;
}
+ /**
+ * Gets the mode for determining whether this View is important for autofill.
+ *
+ * <p>See {@link #setImportantForAutofill(int)} for more info about this mode.
+ *
+ * @return {@link #IMPORTANT_FOR_AUTOFILL_AUTO} by default, or value passed to
+ * {@link #setImportantForAutofill(int)}.
+ *
+ * @attr ref android.R.styleable#View_importantForAutofill
+ */
+ @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_AUTO, to = "auto"),
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_YES, to = "yes"),
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_AUTOFILL_NO, to = "no")})
+ public @AutofillImportance int getImportantForAutofill() {
+ return (mPrivateFlags3
+ & PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK) >> PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT;
+ }
+
+ /**
+ * Sets the mode for determining whether this View is important for autofill.
+ *
+ * <p>See {@link #setImportantForAutofill(int)} for more info about this mode.
+ *
+ * @param mode {@link #IMPORTANT_FOR_AUTOFILL_AUTO}, {@link #IMPORTANT_FOR_AUTOFILL_YES},
+ * or {@link #IMPORTANT_FOR_AUTOFILL_NO}.
+ *
+ * @attr ref android.R.styleable#View_importantForAutofill
+ */
+ public void setImportantForAutofill(@AutofillImportance int mode) {
+ mPrivateFlags3 &= ~PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK;
+ mPrivateFlags3 |= (mode << PFLAG3_IMPORTANT_FOR_AUTOFILL_SHIFT)
+ & PFLAG3_IMPORTANT_FOR_AUTOFILL_MASK;
+ }
+
+ /**
+ * Hints the Android System whether the {@link android.app.assist.AssistStructure.ViewNode}
+ * associated with this View should be included in a {@link ViewStructure} used for
+ * autofill purposes.
+ *
+ * <p>Generally speaking, a view is important for autofill if:
+ * <ol>
+ * <li>The view can-be autofilled by an {@link android.service.autofill.AutoFillService}.
+ * <li>The view contents can help an {@link android.service.autofill.AutoFillService} to
+ * autofill other views.
+ * <ol>
+ *
+ * <p>For example, view containers should typically return {@code false} for performance reasons
+ * (since the important info is provided by their children), but if the container is actually
+ * whose children are part of a compound view, it should return {@code true} (and then override
+ * {@link #dispatchProvideAutoFillStructure(ViewStructure, int)} to simply call
+ * {@link #onProvideAutoFillStructure(ViewStructure, int)} so its children are not included in
+ * the structure). On the other hand, views representing labels or editable fields should
+ * typically return {@code true}, but in some cases they could return {@code false} (for
+ * example, if they're part of a "Captcha" mechanism).
+ *
+ * <p>By default, this method returns {@code true} if {@link #getImportantForAutofill()} returns
+ * {@link #IMPORTANT_FOR_AUTOFILL_YES}, {@code false } if it returns
+ * {@link #IMPORTANT_FOR_AUTOFILL_NO}, and use some heuristics to define the importance when it
+ * returns {@link #IMPORTANT_FOR_AUTOFILL_AUTO}. Hence, it should rarely be overridden - Views
+ * should use {@link #setImportantForAutofill(int)} instead.
+ *
+ * <p><strong>Note:</strong> returning {@code false} does not guarantee the view will be
+ * excluded from the structure; for example, if the user explicitly requested auto-fill, the
+ * View might be always included.
+ *
+ * <p>This decision applies just for the view, not its children - if the view children are not
+ * important for autofill, the view should override
+ * {@link #dispatchProvideAutoFillStructure(ViewStructure, int)} to simply call
+ * {@link #onProvideAutoFillStructure(ViewStructure, int)} (instead of calling
+ * {@link #dispatchProvideAutoFillStructure(ViewStructure, int)} for each child).
+ *
+ * @return whether the view is considered important for autofill.
+ *
+ * @see #IMPORTANT_FOR_AUTOFILL_AUTO
+ * @see #IMPORTANT_FOR_AUTOFILL_YES
+ * @see #IMPORTANT_FOR_AUTOFILL_NO
+ */
+ public final boolean isImportantForAutofill() {
+ final int flag = getImportantForAutofill();
+
+ // First, check if view explicity set it to YES or NO
+ if ((flag & IMPORTANT_FOR_AUTOFILL_YES) != 0) {
+ return true;
+ }
+ if ((flag & IMPORTANT_FOR_AUTOFILL_NO) != 0) {
+ return false;
+ }
+
+ // Then use some heuristics to handle AUTO.
+
+ // Always include views that have a explicity resource id.
+ final int id = mID;
+ if (id != NO_ID && !isViewIdGenerated(id)) {
+ final Resources res = getResources();
+ String entry = null;
+ String pkg = null;
+ try {
+ entry = res.getResourceEntryName(id);
+ pkg = res.getResourcePackageName(id);
+ } catch (Resources.NotFoundException e) {
+ // ignore
+ }
+ if (entry != null && pkg != null && pkg.equals(mContext.getPackageName())) {
+ return true;
+ }
+ }
+
+ // Otherwise, assume it's not important...
+ return false;
+ }
+
@Nullable
private AutoFillManager getAutoFillManager() {
return mContext.getSystemService(AutoFillManager.class);
}
private boolean isAutoFillable() {
- return getAutoFillType() != null && !isAutoFillBlocked();
+ return getAutofillType() != AUTOFILL_TYPE_NONE && !isAutoFillBlocked();
}
private void populateVirtualStructure(ViewStructure structure,
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 047a515bb99e..66c05785d6a9 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -107,10 +107,10 @@ public class ViewDebug {
* these human readable values:
*
* <pre>
- * @ViewDebug.ExportedProperty(mapping = {
- * @ViewDebug.IntToString(from = 0, to = "VISIBLE"),
- * @ViewDebug.IntToString(from = 4, to = "INVISIBLE"),
- * @ViewDebug.IntToString(from = 8, to = "GONE")
+ * {@literal @}ViewDebug.ExportedProperty(mapping = {
+ * {@literal @}ViewDebug.IntToString(from = 0, to = "VISIBLE"),
+ * {@literal @}ViewDebug.IntToString(from = 4, to = "INVISIBLE"),
+ * {@literal @}ViewDebug.IntToString(from = 8, to = "GONE")
* })
* public int getVisibility() { ...
* <pre>
@@ -127,10 +127,10 @@ public class ViewDebug {
* of an array:
*
* <pre>
- * @ViewDebug.ExportedProperty(indexMapping = {
- * @ViewDebug.IntToString(from = 0, to = "INVALID"),
- * @ViewDebug.IntToString(from = 1, to = "FIRST"),
- * @ViewDebug.IntToString(from = 2, to = "SECOND")
+ * {@literal @}ViewDebug.ExportedProperty(indexMapping = {
+ * {@literal @}ViewDebug.IntToString(from = 0, to = "INVALID"),
+ * {@literal @}ViewDebug.IntToString(from = 1, to = "FIRST"),
+ * {@literal @}ViewDebug.IntToString(from = 2, to = "SECOND")
* })
* private int[] mElements;
* <pre>
@@ -148,9 +148,11 @@ public class ViewDebug {
* for the flags of an integer:
*
* <pre>
- * @ViewDebug.ExportedProperty(flagMapping = {
- * @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = ENABLED, name = "ENABLED"),
- * @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = DISABLED, name = "DISABLED"),
+ * {@literal @}ViewDebug.ExportedProperty(flagMapping = {
+ * {@literal @}ViewDebug.FlagToString(mask = ENABLED_MASK, equals = ENABLED,
+ * name = "ENABLED"),
+ * {@literal @}ViewDebug.FlagToString(mask = ENABLED_MASK, equals = DISABLED,
+ * name = "DISABLED"),
* })
* private int mFlags;
* <pre>
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3dd3ba8d3294..214249fc9a6a 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -595,6 +595,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
initViewGroup();
initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
}
@@ -3341,83 +3342,123 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
dispatchProvideStructureForAssistOrAutoFill(structure, true);
}
+ /** @hide */
+ private ArrayList<View> getChildrenForAutofill() {
+ final ArrayList<View> list = new ArrayList<>();
+ populateChildrenForAutofill(list);
+ return list;
+ }
+
+ /** @hide */
+ private void populateChildrenForAutofill(ArrayList<View> list) {
+ final int count = mChildrenCount;
+ for (int i = 0; i < count; i++) {
+ final View child = mChildren[i];
+ if (child.isImportantForAutofill()) {
+ list.add(child);
+ } else if (child instanceof ViewGroup) {
+ ((ViewGroup) child).populateChildrenForAutofill(list);
+ }
+ }
+ }
+
private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure,
boolean forAutoFill) {
boolean blocked = forAutoFill ? isAutoFillBlocked() : isAssistBlocked();
+ if (blocked || structure.getChildCount() != 0) {
+ return;
+ }
+ final View[] childrenArray;
+ final ArrayList<View> childrenList;
+ final int childrenCount;
- if (!blocked) {
- if (structure.getChildCount() == 0) {
- final int childrenCount = getChildCount();
- if (childrenCount > 0) {
- structure.setChildCount(childrenCount);
- ArrayList<View> preorderedList = buildOrderedChildList();
- boolean customOrder = preorderedList == null
- && isChildrenDrawingOrderEnabled();
- final View[] children = mChildren;
- for (int i=0; i<childrenCount; i++) {
- int childIndex;
- try {
- childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
- } catch (IndexOutOfBoundsException e) {
- childIndex = i;
- if (mContext.getApplicationInfo().targetSdkVersion
- < Build.VERSION_CODES.M) {
- Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
- + i + " of " + childrenCount, e);
- // At least one app is failing when we call getChildDrawingOrder
- // at this point, so deal semi-gracefully with it by falling back
- // on the basic order.
- customOrder = false;
- if (i > 0) {
- // If we failed at the first index, there really isn't
- // anything to do -- we will just proceed with the simple
- // sequence order.
- // Otherwise, we failed in the middle, so need to come up
- // with an order for the remaining indices and use that.
- // Failed at the first one, easy peasy.
- int[] permutation = new int[childrenCount];
- SparseBooleanArray usedIndices = new SparseBooleanArray();
- // Go back and collected the indices we have done so far.
- for (int j=0; j<i; j++) {
- permutation[j] = getChildDrawingOrder(childrenCount, j);
- usedIndices.put(permutation[j], true);
- }
- // Fill in the remaining indices with indices that have not
- // yet been used.
- int nextIndex = 0;
- for (int j=i; j<childrenCount; j++) {
- while (usedIndices.get(nextIndex, false)) {
- nextIndex++;
- }
- permutation[j] = nextIndex;
- nextIndex++;
- }
- // Build the final view list.
- preorderedList = new ArrayList<>(childrenCount);
- for (int j=0; j<childrenCount; j++) {
- preorderedList.add(children[permutation[j]]);
- }
+ if (forAutoFill) {
+ childrenArray = null;
+ // TODO(b/33197203): the current algorithm allocates a new list for each children that
+ // is a view group; ideally, we should use mAttachInfo.mTempArrayList instead, but that
+ // would complicated the algorithm a lot...
+ childrenList = getChildrenForAutofill();
+
+ childrenCount = childrenList.size();
+ } else {
+ childrenArray = mChildren;
+ childrenList = null;
+ childrenCount = getChildCount();
+ }
+
+ if (childrenCount > 0) {
+ structure.setChildCount(childrenCount);
+ ArrayList<View> preorderedList = buildOrderedChildList();
+ boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ for (int i = 0; i < childrenCount; i++) {
+ int childIndex;
+ try {
+ childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+ } catch (IndexOutOfBoundsException e) {
+ childIndex = i;
+ if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
+ Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
+ + i + " of " + childrenCount, e);
+ // At least one app is failing when we call getChildDrawingOrder
+ // at this point, so deal semi-gracefully with it by falling back
+ // on the basic order.
+ customOrder = false;
+ if (i > 0) {
+ // If we failed at the first index, there really isn't
+ // anything to do -- we will just proceed with the simple
+ // sequence order.
+ // Otherwise, we failed in the middle, so need to come up
+ // with an order for the remaining indices and use that.
+ // Failed at the first one, easy peasy.
+ int[] permutation = new int[childrenCount];
+ SparseBooleanArray usedIndices = new SparseBooleanArray();
+ // Go back and collected the indices we have done so far.
+ for (int j = 0; j < i; j++) {
+ permutation[j] = getChildDrawingOrder(childrenCount, j);
+ usedIndices.put(permutation[j], true);
+ }
+ // Fill in the remaining indices with indices that have not
+ // yet been used.
+ int nextIndex = 0;
+ for (int j = i; j < childrenCount; j++) {
+ while (usedIndices.get(nextIndex, false)) {
+ nextIndex++;
}
- } else {
- throw e;
+ permutation[j] = nextIndex;
+ nextIndex++;
+ }
+ // Build the final view list.
+ preorderedList = new ArrayList<>(childrenCount);
+ for (int j = 0; j < childrenCount; j++) {
+ final int index = permutation[j];
+ final View child = forAutoFill
+ ? childrenList.get(index)
+ : childrenArray[index];
+ preorderedList.add(child);
}
}
+ } else {
+ throw e;
+ }
+ }
- final View child = getAndVerifyPreorderedView(
- preorderedList, children, childIndex);
- final ViewStructure cstructure = structure.newChild(i);
+ final View child = forAutoFill
+ ? getAndVerifyPreorderedView(preorderedList, childrenList, childIndex)
+ : getAndVerifyPreorderedView(preorderedList, childrenArray, childIndex);
+ final ViewStructure cstructure = structure.newChild(i);
- // Must explicitly check which recursive method to call.
- if (forAutoFill) {
- // NOTE: flags are not currently supported, hence 0
- child.dispatchProvideAutoFillStructure(cstructure, 0);
- } else {
- child.dispatchProvideStructure(cstructure);
- }
- }
- if (preorderedList != null) preorderedList.clear();
+ // Must explicitly check which recursive method to call.
+ if (forAutoFill) {
+ // NOTE: flags are not currently supported, hence 0
+ child.dispatchProvideAutoFillStructure(cstructure, 0);
+ } else {
+ child.dispatchProvideStructure(cstructure);
}
}
+ if (preorderedList != null) {
+ preorderedList.clear();
+ }
}
}
@@ -3436,6 +3477,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return child;
}
+ private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList,
+ ArrayList<View> children, int childIndex) {
+ final View child;
+ if (preorderedList != null) {
+ child = preorderedList.get(childIndex);
+ if (child == null) {
+ throw new RuntimeException("Invalid preorderedList contained null child at index "
+ + childIndex);
+ }
+ } else {
+ child = children.get(childIndex);
+ }
+ return child;
+ }
+
/** @hide */
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f9863b0a6761..20d960fff661 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2632,14 +2632,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- private void onDrawFinished() {
- try {
- mWindowSession.finishDrawing(mWindow);
- } catch (RemoteException e) {
- // Have fun!
- }
- }
-
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
@@ -2690,7 +2682,7 @@ public final class ViewRootImpl implements ViewParent,
}
if (mSurfaceHolder != null && mSurface.isValid()) {
- SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::onDrawFinished);
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(mWindowSession, mWindow);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 92f78b9301c0..c7c2bb82442e 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -311,12 +311,17 @@ public abstract class ViewStructure {
public abstract ViewStructure asyncNewChildForAutoFill(int index, int virtualId, int flags);
/**
- * Sets the {@link AutoFillType} that can be used to auto-fill this node.
+ * @deprecated TODO(b/35956626): remove once clients use setAutoFilltype()
*/
- // TODO(b/33197203, b/33802548): add CTS/unit test
+ @Deprecated
public abstract void setAutoFillType(AutoFillType info);
/**
+ * Sets the {@link View#getAutofillType()} that can be used to autofill this node.
+ */
+ public abstract void setAutofillType(@View.AutofillType int type);
+
+ /**
* Sets the a hint that helps the auto-fill service to select the appropriate data to fill the
* view.
*/
diff --git a/core/java/android/view/autofill/AutoFillManager.java b/core/java/android/view/autofill/AutoFillManager.java
index e8325e89f39a..8beaf4e423cb 100644
--- a/core/java/android/view/autofill/AutoFillManager.java
+++ b/core/java/android/view/autofill/AutoFillManager.java
@@ -19,7 +19,9 @@ package android.view.autofill;
import static android.view.autofill.Helper.DEBUG;
import static android.view.autofill.Helper.VERBOSE;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
@@ -30,7 +32,10 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
+import android.view.WindowManagerGlobal;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.List;
@@ -76,6 +81,8 @@ public final class AutoFillManager {
private final IAutoFillManager mService;
private IAutoFillManagerClient mServiceClient;
+ private AutofillCallback mCallback;
+
private Context mContext;
private boolean mHasSession;
@@ -276,11 +283,11 @@ public final class AutoFillManager {
}
}
- private AutoFillId getAutoFillId(View view) {
+ private static AutoFillId getAutoFillId(View view) {
return new AutoFillId(view.getAccessibilityViewId());
}
- private AutoFillId getAutoFillId(View parent, int childId) {
+ private static AutoFillId getAutoFillId(View parent, int childId) {
return new AutoFillId(parent.getAccessibilityViewId(), childId);
}
@@ -289,10 +296,12 @@ public final class AutoFillManager {
if (DEBUG) {
Log.d(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value);
}
+
try {
mService.startSession(mContext.getActivityToken(), windowToken,
- mServiceClient.asBinder(), id, bounds, value, mContext.getUserId());
- AutoFillClient client = getClient();
+ mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
+ mCallback != null);
+ final AutoFillClient client = getClient();
if (client != null) {
client.resetableStateAvailable();
}
@@ -344,6 +353,119 @@ public final class AutoFillManager {
}
}
+ /**
+ * Registers a {@link AutofillCallback} to receive autofill events.
+ *
+ * @param callback callback to receive events.
+ */
+ public void registerCallback(@Nullable AutofillCallback callback) {
+ if (callback == null) return;
+
+ final boolean hadCallback = mCallback != null;
+ mCallback = callback;
+
+ if (mHasSession && !hadCallback) {
+ try {
+ mService.setHasCallback(mContext.getActivityToken(), mContext.getUserId(), true);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Unregisters a {@link AutofillCallback} to receive autofill events.
+ *
+ * @param callback callback to stop receiving events.
+ */
+ public void unregisterCallback(@Nullable AutofillCallback callback) {
+ if (callback == null || mCallback == null || callback != mCallback) return;
+
+ mCallback = null;
+
+ if (mHasSession) {
+ try {
+ mService.setHasCallback(mContext.getActivityToken(), mContext.getUserId(), false);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private void onAutofillEvent(IBinder windowToken, AutoFillId id, int event) {
+ if (mCallback == null) return;
+ if (id == null) {
+ Log.w(TAG, "onAutofillEvent(): no id for event " + event);
+ return;
+ }
+
+ final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
+ if (root == null) {
+ Log.w(TAG, "onAutofillEvent() for " + id + ": root view gone");
+ return;
+ }
+ final View view = root.findViewByAccessibilityIdTraversal(id.getViewId());
+ if (view == null) {
+ Log.w(TAG, "onAutofillEvent() for " + id + ": view gone");
+ return;
+ }
+ if (id.isVirtual()) {
+ mCallback.onAutofillEventVirtual(view, id.getVirtualChildId(), event);
+ } else {
+ mCallback.onAutofillEvent(view, event);
+ }
+ }
+
+ /**
+ * Callback for auto-fill related events.
+ *
+ * <p>Typically used for applications that display their own "auto-complete" views, so they can
+ * enable / disable such views when the auto-fill UI affordance is shown / hidden.
+ */
+ public abstract static class AutofillCallback {
+
+ /** @hide */
+ @IntDef({EVENT_INPUT_SHOWN, EVENT_INPUT_HIDDEN})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AutofillEventType {}
+
+ /**
+ * The auto-fill input UI affordance associated with the view was shown.
+ *
+ * <p>If the view provides its own auto-complete UI affordance and its currently shown, it
+ * should be hidden upon receiving this event.
+ */
+ public static final int EVENT_INPUT_SHOWN = 1;
+
+ /**
+ * The auto-fill input UI affordance associated with the view was hidden.
+ *
+ * <p>If the view provides its own auto-complete UI affordance that was hidden upon a
+ * {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
+ */
+ public static final int EVENT_INPUT_HIDDEN = 2;
+
+ /**
+ * Called after a change in the autofill state associated with a view.
+ *
+ * @param view view associated with the change.
+ *
+ * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
+ */
+ public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {}
+
+ /**
+ * Called after a change in the autofill state associated with a virtual view.
+ *
+ * @param view parent view associated with the change.
+ * @param childId id identifying the virtual child inside the parent view.
+ *
+ * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
+ */
+ public void onAutofillEventVirtual(@NonNull View view, int childId,
+ @AutofillEventType int event) {}
+ }
+
private static final class AutoFillManagerClient extends IAutoFillManagerClient.Stub {
private final WeakReference<AutoFillManager> mAutoFillManager;
@@ -385,5 +507,17 @@ public final class AutoFillManager {
});
}
}
+
+ @Override
+ public void onAutofillEvent(IBinder windowToken, AutoFillId id, int event) {
+ final AutoFillManager autoFillManager = mAutoFillManager.get();
+ if (autoFillManager != null) {
+ autoFillManager.mContext.getMainThreadHandler().post(() -> {
+ if (autoFillManager.getClient() != null) {
+ autoFillManager.onAutofillEvent(windowToken, id, event);
+ }
+ });
+ }
+ }
}
}
diff --git a/core/java/android/view/autofill/AutoFillType.aidl b/core/java/android/view/autofill/AutoFillType.aidl
index a63d7c50f38b..4606b48e9e10 100644
--- a/core/java/android/view/autofill/AutoFillType.aidl
+++ b/core/java/android/view/autofill/AutoFillType.aidl
@@ -16,4 +16,7 @@
package android.view.autofill;
+/*
+ * TODO(b/35956626): remove once clients use getAutoFilltype()
+ */
parcelable AutoFillType; \ No newline at end of file
diff --git a/core/java/android/view/autofill/AutoFillType.java b/core/java/android/view/autofill/AutoFillType.java
index 536d5e0550da..37966b25ce05 100644
--- a/core/java/android/view/autofill/AutoFillType.java
+++ b/core/java/android/view/autofill/AutoFillType.java
@@ -26,6 +26,8 @@ import android.view.View;
* Defines the type of a object that can be used to auto-fill a {@link View} so the
* {@link android.service.autofill.AutoFillService} can use the proper {@link AutoFillValue} to
* fill it.
+ *
+ * TODO(b/35956626): remove once clients use getAutoFilltype
*/
public final class AutoFillType implements Parcelable {
@@ -95,8 +97,6 @@ public final class AutoFillType implements Parcelable {
* <p>{@link AutoFillValue} instances for auto-filling a {@link View} can be obtained through
* {@link AutoFillValue#forDate(long)}, and the values passed to
* auto-fill a {@link View} can be fetched through {@link AutoFillValue#getDateValue()}.
- *
- * <p>This type has no sub-types.
*/
public boolean isDate() {
return mType == TYPE_DATE;
diff --git a/core/java/android/view/autofill/AutoFillValue.java b/core/java/android/view/autofill/AutoFillValue.java
index c24e04e50154..11fab68ecd08 100644
--- a/core/java/android/view/autofill/AutoFillValue.java
+++ b/core/java/android/view/autofill/AutoFillValue.java
@@ -45,38 +45,36 @@ public final class AutoFillValue implements Parcelable {
}
/**
- * Gets the value to auto-fill a text field.
+ * Gets the value to autofill a text field.
*
- * <p>See {@link AutoFillType#isText()} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
*/
public CharSequence getTextValue() {
return mText;
}
/**
- * Gets the value to auto-fill a toggable field.
+ * Gets the value to autofill a toggable field.
*
- * <p>See {@link AutoFillType#isToggle()} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
*/
public boolean getToggleValue() {
return mToggle;
}
/**
- * Gets the value to auto-fill a selection list field.
+ * Gets the value to autofill a selection list field.
*
- * <p>See {@link AutoFillType#isList()} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
*/
public int getListValue() {
return mListIndex;
}
/**
- * Gets the value representing the the number of milliseconds since the standard base time known
- * as "the epoch", namely January 1, 1970, 00:00:00 GMT (see {@link java.util.Date#getTime()}
- * of a date field.
+ * Gets the value to autofill a date field.
*
- * <p>See {@link AutoFillType#isDate()} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
*/
public long getDateValue() {
return mDate;
@@ -174,9 +172,9 @@ public final class AutoFillValue implements Parcelable {
// TODO(b/33197203): add unit tests for each supported type (new / get should return same value)
/**
- * Creates a new {@link AutoFillValue} to auto-fill a {@link View} representing a text field.
+ * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a text field.
*
- * <p>See {@link AutoFillType#isText()} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
*/
// TODO(b/33197203): use cache
@Nullable
@@ -185,29 +183,29 @@ public final class AutoFillValue implements Parcelable {
}
/**
- * Creates a new {@link AutoFillValue} to auto-fill a {@link View} representing a toggable
+ * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a toggable
* field.
*
- * <p>See {@link AutoFillType#isToggle()} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
*/
public static AutoFillValue forToggle(boolean value) {
return new AutoFillValue(null, 0, value, 0);
}
/**
- * Creates a new {@link AutoFillValue} to auto-fill a {@link View} representing a selection
+ * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a selection
* list.
*
- * <p>See {@link AutoFillType#isList()} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
*/
public static AutoFillValue forList(int value) {
return new AutoFillValue(null, value, false, 0);
}
/**
- * Creates a new {@link AutoFillValue} to auto-fill a {@link View} representing a date.
+ * Creates a new {@link AutoFillValue} to autofill a {@link View} representing a date.
*
- * <p>See {@link AutoFillType#isDate()} for more info.
+ * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
*/
public static AutoFillValue forDate(long date) {
return new AutoFillValue(null, 0, false, date);
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index d054e979ddb7..b36c0f15d58a 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -31,10 +31,12 @@ import android.view.autofill.IAutoFillManagerClient;
interface IAutoFillManager {
boolean addClient(in IAutoFillManagerClient client, int userId);
oneway void startSession(in IBinder activityToken, IBinder windowToken, in IBinder appCallback,
- in AutoFillId autoFillId, in Rect bounds, in AutoFillValue value, int userId);
+ in AutoFillId autoFillId, in Rect bounds, in AutoFillValue value, int userId,
+ boolean hasCallback);
oneway void updateSession(in IBinder activityToken, in AutoFillId id, in Rect bounds,
in AutoFillValue value, int flags, int userId);
oneway void finishSession(in IBinder activityToken, int userId);
oneway void setAuthenticationResult(in Bundle data,
in IBinder activityToken, int userId);
+ oneway void setHasCallback(in IBinder activityToken, int userId, boolean hasIt);
}
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 45f363d32301..9eef7d0a6e69 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -20,6 +20,7 @@ import java.util.List;
import android.content.Intent;
import android.content.IntentSender;
+import android.os.IBinder;
import android.view.autofill.AutoFillId;
import android.view.autofill.AutoFillValue;
@@ -43,4 +44,9 @@ oneway interface IAutoFillManagerClient {
* Authenticates a fill response or a data set.
*/
void authenticate(in IntentSender intent, in Intent fillInIntent);
+
+ /**
+ * Notifies the client when the auto-fill UI changed.
+ */
+ void onAutofillEvent(in IBinder windowToken, in AutoFillId id, int event);
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 791543ed5f84..46f7a81e0550 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -18,7 +18,9 @@ package android.view.textclassifier;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.os.LocaleList;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -51,20 +53,43 @@ public interface TextClassifier {
@Override
public TextSelection suggestSelection(
- CharSequence text, int selectionStartIndex, int selectionEndIndex) {
+ CharSequence text,
+ int selectionStartIndex,
+ int selectionEndIndex,
+ LocaleList defaultLocales) {
return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build();
}
@Override
public TextClassificationResult getTextClassificationResult(
- CharSequence text, int startIndex, int endIndex) {
+ CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales) {
return TextClassificationResult.EMPTY;
}
@Override
- public LinksInfo getLinks(CharSequence text, int linkMask) {
+ public LinksInfo getLinks(CharSequence text, int linkMask, LocaleList defaultLocales) {
return LinksInfo.NO_OP;
}
+
+ // TODO: Remove
+ @Override
+ public TextSelection suggestSelection(
+ CharSequence text, int selectionStartIndex, int selectionEndIndex) {
+ throw new UnsupportedOperationException("Removed");
+ }
+
+ // TODO: Remove
+ @Override
+ public TextClassificationResult getTextClassificationResult(
+ CharSequence text, int startIndex, int endIndex) {
+ throw new UnsupportedOperationException("Removed");
+ }
+
+ // TODO: Remove
+ @Override
+ public LinksInfo getLinks(CharSequence text, int linkMask) {
+ throw new UnsupportedOperationException("Removed");
+ }
};
/**
@@ -75,15 +100,20 @@ public interface TextClassifier {
* by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
* @param selectionStartIndex start index of the selected part of text
* @param selectionEndIndex end index of the selected part of text
+ * @param defaultLocales ordered list of locale preferences that can be used to disambiguate
+ * the provided text. If no locale preferences exist, set this to null or an empty locale
+ * list in which case the classifier will decide whether to use no locale information, use
+ * a default locale, or use the system default.
*
* @throws IllegalArgumentException if text is null; selectionStartIndex is negative;
- * selectionEndIndex is greater than text.length() or less than selectionStartIndex
+ * selectionEndIndex is greater than text.length() or not greater than selectionStartIndex
*/
@NonNull
TextSelection suggestSelection(
@NonNull CharSequence text,
@IntRange(from = 0) int selectionStartIndex,
- @IntRange(from = 0) int selectionEndIndex);
+ @IntRange(from = 0) int selectionEndIndex,
+ @Nullable LocaleList defaultLocales);
/**
* Returns a {@link TextClassificationResult} object that can be used to generate a widget for
@@ -93,13 +123,20 @@ public interface TextClassifier {
* by the sub sequence starting at startIndex and ending at endIndex)
* @param startIndex start index of the text to classify
* @param endIndex end index of the text to classify
+ * @param defaultLocales ordered list of locale preferences that can be used to disambiguate
+ * the provided text. If no locale preferences exist, set this to null or an empty locale
+ * list in which case the classifier will decide whether to use no locale information, use
+ * a default locale, or use the system default.
*
* @throws IllegalArgumentException if text is null; startIndex is negative;
- * endIndex is greater than text.length() or less than startIndex
+ * endIndex is greater than text.length() or not greater than startIndex
*/
@NonNull
TextClassificationResult getTextClassificationResult(
- @NonNull CharSequence text, int startIndex, int endIndex);
+ @NonNull CharSequence text,
+ @IntRange(from = 0) int startIndex,
+ @IntRange(from = 0) int endIndex,
+ @Nullable LocaleList defaultLocales);
/**
* Returns a {@link LinksInfo} that may be applied to the text to annotate it with links
@@ -108,8 +145,25 @@ public interface TextClassifier {
* @param text the text to generate annotations for
* @param linkMask See {@link android.text.util.Linkify} for a list of linkMasks that may be
* specified. Subclasses of this interface may specify additional linkMasks
+ * @param defaultLocales ordered list of locale preferences that can be used to disambiguate
+ * the provided text. If no locale preferences exist, set this to null or an empty locale
+ * list in which case the classifier will decide whether to use no locale information, use
+ * a default locale, or use the system default.
*
* @throws IllegalArgumentException if text is null
*/
- LinksInfo getLinks(@NonNull CharSequence text, int linkMask);
+ LinksInfo getLinks(
+ @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales);
+
+ // TODO: Remove
+ /** @removed */
+ TextSelection suggestSelection(
+ CharSequence text, int selectionStartIndex, int selectionEndIndex);
+ // TODO: Remove
+ /** @removed */
+ TextClassificationResult getTextClassificationResult(
+ CharSequence text, int startIndex, int endIndex);
+ // TODO: Remove
+ /** @removed */
+ LinksInfo getLinks(CharSequence text, int linkMask);
}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 97a36fd71c0f..0486f9f8d09f 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -26,6 +26,7 @@ import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.icu.text.BreakIterator;
import android.net.Uri;
+import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.provider.Browser;
import android.text.Spannable;
@@ -74,7 +75,8 @@ final class TextClassifierImpl implements TextClassifier {
@Override
public TextSelection suggestSelection(
- @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex) {
+ @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex,
+ LocaleList defaultLocales) {
validateInput(text, selectionStartIndex, selectionEndIndex);
try {
if (text.length() > 0) {
@@ -101,12 +103,12 @@ final class TextClassifierImpl implements TextClassifier {
}
// Getting here means something went wrong, return a NO_OP result.
return TextClassifier.NO_OP.suggestSelection(
- text, selectionStartIndex, selectionEndIndex);
+ text, selectionStartIndex, selectionEndIndex, defaultLocales);
}
@Override
public TextClassificationResult getTextClassificationResult(
- @NonNull CharSequence text, int startIndex, int endIndex) {
+ @NonNull CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales) {
validateInput(text, startIndex, endIndex);
try {
if (text.length() > 0) {
@@ -125,11 +127,12 @@ final class TextClassifierImpl implements TextClassifier {
Log.e(LOG_TAG, "Error getting assist info.", t);
}
// Getting here means something went wrong, return a NO_OP result.
- return TextClassifier.NO_OP.getTextClassificationResult(text, startIndex, endIndex);
+ return TextClassifier.NO_OP.getTextClassificationResult(
+ text, startIndex, endIndex, defaultLocales);
}
@Override
- public LinksInfo getLinks(CharSequence text, int linkMask) {
+ public LinksInfo getLinks(CharSequence text, int linkMask, LocaleList defaultLocales) {
Preconditions.checkArgument(text != null);
try {
return LinksInfoFactory.create(
@@ -139,7 +142,27 @@ final class TextClassifierImpl implements TextClassifier {
Log.e(LOG_TAG, "Error getting links info.", t);
}
// Getting here means something went wrong, return a NO_OP result.
- return TextClassifier.NO_OP.getLinks(text, linkMask);
+ return TextClassifier.NO_OP.getLinks(text, linkMask, defaultLocales);
+ }
+
+ // TODO: Remove
+ @Override
+ public TextSelection suggestSelection(
+ CharSequence text, int selectionStartIndex, int selectionEndIndex) {
+ throw new UnsupportedOperationException("Removed");
+ }
+
+ // TODO: Remove
+ @Override
+ public TextClassificationResult getTextClassificationResult(
+ CharSequence text, int startIndex, int endIndex) {
+ throw new UnsupportedOperationException("Removed");
+ }
+
+ // TODO: Remove
+ @Override
+ public LinksInfo getLinks(CharSequence text, int linkMask) {
+ throw new UnsupportedOperationException("Removed");
}
private SmartSelection getSmartSelection() throws FileNotFoundException {
@@ -195,13 +218,13 @@ final class TextClassifierImpl implements TextClassifier {
/**
* @throws IllegalArgumentException if text is null; startIndex is negative;
- * endIndex is greater than text.length() or less than startIndex
+ * endIndex is greater than text.length() or is not greater than startIndex
*/
private static void validateInput(@NonNull CharSequence text, int startIndex, int endIndex) {
Preconditions.checkArgument(text != null);
Preconditions.checkArgument(startIndex >= 0);
Preconditions.checkArgument(endIndex <= text.length());
- Preconditions.checkArgument(endIndex >= startIndex);
+ Preconditions.checkArgument(endIndex > startIndex);
}
/**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1e7cddfcda92..3fbeb0323a1a 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -630,6 +630,12 @@ public class WebView extends AbsoluteLayout
protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ // WebView is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
if (context == null) {
throw new IllegalArgumentException("Invalid context argument");
}
@@ -2784,6 +2790,10 @@ public class WebView extends AbsoluteLayout
* package that was used to load it. Otherwise, the package that would be used if the WebView
* was loaded right now will be returned; this does not cause WebView to be loaded, so this
* information may become outdated at any time.
+ * The WebView package changes either when the current WebView package is updated, disabled, or
+ * uninstalled. It can also be changed through a Developer Setting.
+ * If the WebView package changes, any app process that has loaded WebView will be killed. The
+ * next time the app starts and loads WebView it will use the new WebView package instead.
* @return the current WebView package, or null if there is none.
*/
public static PackageInfo getCurrentWebViewPackage() {
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index bc3dfffc1411..e6cd56607edc 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -26,6 +26,8 @@ import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewStructure;
+import android.view.autofill.AutoFillValue;
import com.android.internal.R;
@@ -68,6 +70,12 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
public AbsSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ // Spinner is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
initAbsSpinner();
final TypedArray a = context.obtainStyledAttributes(
@@ -480,4 +488,43 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
public CharSequence getAccessibilityClassName() {
return AbsSpinner.class.getName();
}
+
+ // TODO(b/33197203): add unit/CTS tests for auto-fill methods (and make sure they handle enable)
+
+ @Override
+ public void onProvideAutoFillStructure(ViewStructure structure, int flags) {
+ super.onProvideAutoFillStructure(structure, flags);
+
+ if (getAdapter() == null) return;
+
+ // TODO(b/33197203): implement sanitization so initial value is only sanitized when coming
+ // from resources.
+
+ final int count = getAdapter().getCount();
+ if (count > 0) {
+ final String[] options = new String[count];
+ for (int i = 0; i < count; i++) {
+ options[i] = getAdapter().getItem(i).toString();
+ }
+ structure.setAutoFillOptions(options);
+ }
+ }
+
+ @Override
+ public void autoFill(AutoFillValue value) {
+ if (!isEnabled()) return;
+
+ final int position = value.getListValue();
+ setSelection(position);
+ }
+
+ @Override
+ public @AutofillType int getAutofillType() {
+ return isEnabled() ? AUTOFILL_TYPE_LIST : AUTOFILL_TYPE_NONE;
+ }
+
+ @Override
+ public AutoFillValue getAutoFillValue() {
+ return isEnabled() ? AutoFillValue.forList(getSelectedItemPosition()) : null;
+ }
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 141b52fd2ce3..dce33a0e6fbb 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -36,7 +36,6 @@ import android.view.ViewStructure;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.autofill.AutoFillManager;
-import android.view.autofill.AutoFillType;
import android.view.autofill.AutoFillValue;
import com.android.internal.R;
@@ -590,8 +589,8 @@ public abstract class CompoundButton extends Button implements Checkable {
}
@Override
- public AutoFillType getAutoFillType() {
- return isEnabled() ? AutoFillType.forToggle() : null;
+ public @AutofillType int getAutofillType() {
+ return isEnabled() ? AUTOFILL_TYPE_TOGGLE : AUTOFILL_TYPE_NONE;
}
@Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 0ffefd091b06..c90517295b4f 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -34,7 +34,6 @@ import android.view.View;
import android.view.ViewStructure;
import android.view.accessibility.AccessibilityEvent;
import android.view.autofill.AutoFillManager;
-import android.view.autofill.AutoFillType;
import android.view.autofill.AutoFillValue;
import com.android.internal.R;
@@ -146,6 +145,11 @@ public class DatePicker extends FrameLayout {
public DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ // DatePicker is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
defStyleAttr, defStyleRes);
final boolean isDialogMode = a.getBoolean(R.styleable.DatePicker_dialogMode, false);
@@ -775,8 +779,8 @@ public class DatePicker extends FrameLayout {
}
@Override
- public AutoFillType getAutoFillType() {
- return isEnabled() ? AutoFillType.forDate() : null;
+ public @AutofillType int getAutofillType() {
+ return isEnabled() ? AUTOFILL_TYPE_DATE : AUTOFILL_TYPE_NONE;
}
@Override
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index cd806510ed51..7e6f2e4305b4 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -156,6 +156,11 @@ public class ImageView extends View {
initImageView();
+ // ImageView is not important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_NO);
+ }
+
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.ImageView, defStyleAttr, defStyleRes);
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index be5fc5381ac7..bb8cd2829caa 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -26,7 +26,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStructure;
import android.view.autofill.AutoFillManager;
-import android.view.autofill.AutoFillType;
import android.view.autofill.AutoFillValue;
import com.android.internal.R;
@@ -86,6 +85,11 @@ public class RadioGroup extends LinearLayout {
public RadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
+ // RadioGroup is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
// retrieve selected radio button as requested by the user in the
// XML layout file
TypedArray attributes = context.obtainStyledAttributes(
@@ -435,8 +439,8 @@ public class RadioGroup extends LinearLayout {
}
@Override
- public AutoFillType getAutoFillType() {
- return isEnabled() ? AutoFillType.forList() : null;
+ public @AutofillType int getAutofillType() {
+ return isEnabled() ? AUTOFILL_TYPE_LIST : AUTOFILL_TYPE_NONE;
}
@Override
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 679053299666..a03238374f39 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.annotation.UiThread;
import android.annotation.WorkerThread;
import android.os.AsyncTask;
+import android.os.LocaleList;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextUtils;
@@ -60,14 +61,14 @@ final class SelectionActionModeHelper {
mEditor = Preconditions.checkNotNull(editor);
final TextView textView = mEditor.getTextView();
mTextClassificationHelper = new TextClassificationHelper(
- textView.getTextClassifier(), textView.getText(),
- textView.getSelectionStart(), textView.getSelectionEnd());
+ textView.getTextClassifier(), textView.getText(), 0, 1, textView.getTextLocales());
}
public void startActionModeAsync() {
cancelAsyncTask();
- if (isNoOpTextClassifier()) {
+ if (isNoOpTextClassifier() || !hasSelection()) {
// No need to make an async call for a no-op TextClassifier.
+ // Do not call the TextClassifier if there is no selection.
startActionMode(null);
} else {
resetTextClassificationHelper();
@@ -84,8 +85,9 @@ final class SelectionActionModeHelper {
public void invalidateActionModeAsync() {
cancelAsyncTask();
- if (isNoOpTextClassifier()) {
+ if (isNoOpTextClassifier() || !hasSelection()) {
// No need to make an async call for a no-op TextClassifier.
+ // Do not call the TextClassifier if there is no selection.
invalidateActionMode(null);
} else {
resetTextClassificationHelper();
@@ -126,6 +128,11 @@ final class SelectionActionModeHelper {
return mEditor.getTextView().getTextClassifier() == TextClassifier.NO_OP;
}
+ private boolean hasSelection() {
+ final TextView textView = mEditor.getTextView();
+ return textView.getSelectionEnd() > textView.getSelectionStart();
+ }
+
private void startActionMode(@Nullable SelectionResult result) {
final TextView textView = mEditor.getTextView();
final CharSequence text = textView.getText();
@@ -164,7 +171,8 @@ final class SelectionActionModeHelper {
private void resetTextClassificationHelper() {
final TextView textView = mEditor.getTextView();
mTextClassificationHelper.reset(textView.getTextClassifier(), textView.getText(),
- textView.getSelectionStart(), textView.getSelectionEnd());
+ textView.getSelectionStart(), textView.getSelectionEnd(),
+ textView.getTextLocales());
}
/**
@@ -291,6 +299,7 @@ final class SelectionActionModeHelper {
private int mSelectionStart;
/** End index relative to mText. */
private int mSelectionEnd;
+ private LocaleList mLocales;
/** Trimmed text starting from mTrimStart in mText. */
private CharSequence mTrimmedText;
@@ -302,17 +311,19 @@ final class SelectionActionModeHelper {
private int mRelativeEnd;
TextClassificationHelper(TextClassifier textClassifier,
- CharSequence text, int selectionStart, int selectionEnd) {
- reset(textClassifier, text, selectionStart, selectionEnd);
+ CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
+ reset(textClassifier, text, selectionStart, selectionEnd, locales);
}
@UiThread
public void reset(TextClassifier textClassifier,
- CharSequence text, int selectionStart, int selectionEnd) {
+ CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
mTextClassifier = Preconditions.checkNotNull(textClassifier);
mText = Preconditions.checkNotNull(text).toString();
+ Preconditions.checkArgument(selectionEnd > selectionStart);
mSelectionStart = selectionStart;
mSelectionEnd = selectionEnd;
+ mLocales = locales;
}
@WorkerThread
@@ -322,14 +333,14 @@ final class SelectionActionModeHelper {
mSelectionStart,
mSelectionEnd,
mTextClassifier.getTextClassificationResult(
- mTrimmedText, mRelativeStart, mRelativeEnd));
+ mTrimmedText, mRelativeStart, mRelativeEnd, mLocales));
}
@WorkerThread
public SelectionResult suggestSelection() {
trimText();
final TextSelection sel = mTextClassifier.suggestSelection(
- mTrimmedText, mRelativeStart, mRelativeEnd);
+ mTrimmedText, mRelativeStart, mRelativeEnd, mLocales);
mSelectionStart = Math.max(0, sel.getSelectionStartIndex() + mTrimStart);
mSelectionEnd = Math.min(mText.length(), sel.getSelectionEndIndex() + mTrimStart);
return classifyText();
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 1eff26e33609..3811e1adee61 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -45,8 +45,6 @@ import android.view.ViewStructure;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.autofill.AutoFillType;
-import android.view.autofill.AutoFillValue;
import android.widget.PopupWindow.OnDismissListener;
import com.android.internal.R;
@@ -937,24 +935,6 @@ public class Spinner extends AbsSpinner implements OnClickListener {
}
}
- @Override
- public void autoFill(AutoFillValue value) {
- if (!isEnabled()) return;
-
- final int position = value.getListValue();
- setSelection(position);
- }
-
- @Override
- public AutoFillType getAutoFillType() {
- return AutoFillType.forList();
- }
-
- @Override
- public AutoFillValue getAutoFillValue() {
- return isEnabled() ? AutoFillValue.forList(getSelectedItemPosition()) : null;
- }
-
static class SavedState extends AbsSpinner.SavedState {
boolean showDropdown;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5307a08c59a9..b901ab4f1e4c 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -141,7 +141,6 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
import android.view.autofill.AutoFillManager;
-import android.view.autofill.AutoFillType;
import android.view.autofill.AutoFillValue;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
@@ -790,6 +789,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ // TextView is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
mText = "";
final Resources res = getResources();
@@ -10018,9 +10022,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
@Override
- @Nullable
- public AutoFillType getAutoFillType() {
- return isTextEditable() ? AutoFillType.forText() : null;
+ public @AutofillType int getAutofillType() {
+ return isTextEditable() ? AUTOFILL_TYPE_TEXT : AUTOFILL_TYPE_NONE;
}
@Override
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 1df202e96a3b..3a19f2104e2c 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -32,7 +32,6 @@ import android.view.View;
import android.view.ViewStructure;
import android.view.accessibility.AccessibilityEvent;
import android.view.autofill.AutoFillManager;
-import android.view.autofill.AutoFillType;
import android.view.autofill.AutoFillValue;
import com.android.internal.R;
@@ -112,6 +111,11 @@ public class TimePicker extends FrameLayout {
public TimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ // DatePicker is important by default, unless app developer overrode attribute.
+ if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+ setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+ }
+
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
final boolean isDialogMode = a.getBoolean(R.styleable.TimePicker_dialogMode, false);
@@ -530,8 +534,8 @@ public class TimePicker extends FrameLayout {
}
@Override
- public AutoFillType getAutoFillType() {
- return isEnabled() ? AutoFillType.forDate() : null;
+ public @AutofillType int getAutofillType() {
+ return isEnabled() ? AUTOFILL_TYPE_DATE : AUTOFILL_TYPE_NONE;
}
@Override
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index c8bf302e46d5..949e7ac50a7b 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -29,8 +29,10 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
*/
public class MetricsLogger {
// define metric categories in frameworks/base/proto/src/metrics_constants.proto.
+ // mirror changes in native version at system/core/libmetricslogger/metrics_logger.cpp
public static final int VIEW_UNKNOWN = MetricsEvent.VIEW_UNKNOWN;
+ public static final int LOGTAG = EventLogTags.SYSUI_MULTI_ACTION;
public static void visible(Context context, int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
@@ -125,6 +127,7 @@ public class MetricsLogger {
/** Increment the bucket with the integer label on the histogram with the given name. */
public static void histogram(Context context, String name, int bucket) {
+ // see LogHistogram in system/core/libmetricslogger/metrics_logger.cpp
EventLogTags.writeSysuiHistogram(name, bucket);
EventLogTags.writeSysuiMultiAction(
new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
diff --git a/core/java/com/android/internal/logging/legacy/EventLogCollector.java b/core/java/com/android/internal/logging/legacy/EventLogCollector.java
deleted file mode 100644
index 598f0d51c1b9..000000000000
--- a/core/java/com/android/internal/logging/legacy/EventLogCollector.java
+++ /dev/null
@@ -1,173 +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.logging.legacy;
-
-import android.util.ArrayMap;
-import android.util.EventLog;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-
-/**
- * Scan the event log for interaction metrics events.
- * @hide
- */
-public class EventLogCollector {
- private static final String TAG = "EventLogCollector";
-
- // TODO replace this with GoogleLogTags.TRON_HEARTBEAT
- @VisibleForTesting
- static final int TRON_HEARTBEAT = 208000;
-
- private static EventLogCollector sInstance;
-
- private final ArrayMap<Integer, TagParser> mTagParsers;
- private int[] mInterestingTags;
-
- private LogReader mLogReader;
-
- private EventLogCollector() {
- mTagParsers = new ArrayMap<>();
- addParser(new PowerScreenStateParser());
- addParser(new SysuiMultiActionParser());
-
- mLogReader = new LogReader();
- }
-
- public static EventLogCollector getInstance() {
- if (sInstance == null) {
- sInstance = new EventLogCollector();
- }
- return sInstance;
- }
-
- @VisibleForTesting
- public void setLogReader(LogReader logReader) {
- mLogReader = logReader;
- }
-
- private int[] getInterestingTags() {
- if (mInterestingTags == null) {
- mInterestingTags = new int[mTagParsers.size()];
- for (int i = 0; i < mTagParsers.size(); i++) {
- mInterestingTags[i] = mTagParsers.valueAt(i).getTag();
- }
- }
- return mInterestingTags;
- }
-
- // I would customize ArrayMap to add put(TagParser), but ArrayMap is final.
- @VisibleForTesting
- void addParser(TagParser parser) {
- mTagParsers.put(parser.getTag(), parser);
- mInterestingTags = null;
- }
-
- public void collect(LegacyConversionLogger logger) {
- collect(logger, 0L);
- }
-
- public long collect(TronLogger logger, long lastSeenEventMs) {
- long lastEventMs = 0L;
- final boolean debug = Util.debug();
-
- if (debug) {
- Log.d(TAG, "Eventlog Collection");
- }
- ArrayList<Event> events = new ArrayList<>();
- try {
- mLogReader.readEvents(getInterestingTags(), events);
- } catch (IOException e) {
- e.printStackTrace();
- }
- if (debug) {
- Log.d(TAG, "read this many events: " + events.size());
- }
-
- for (Event event : events) {
- final long millis = event.getTimeNanos() / 1000000;
- if (millis > lastSeenEventMs) {
- final int tag = event.getTag();
- TagParser parser = mTagParsers.get(tag);
- if (parser == null) {
- if (debug) {
- Log.d(TAG, "unknown tag: " + tag);
- }
- continue;
- }
- if (debug) {
- Log.d(TAG, "parsing tag: " + tag);
- }
- parser.parseEvent(logger, event);
- lastEventMs = Math.max(lastEventMs, millis);
- } else {
- if (debug) {
- Log.d(TAG, "old event: " + millis + " < " + lastSeenEventMs);
- }
- }
- }
- return lastEventMs;
- }
-
- @VisibleForTesting
- static class Event {
- long mTimeNanos;
- int mTag;
- Object mData;
-
- Event(long timeNanos, int tag, Object data) {
- super();
- mTimeNanos = timeNanos;
- mTag = tag;
- mData = data;
- }
-
- Event(EventLog.Event event) {
- mTimeNanos = event.getTimeNanos();
- mTag = event.getTag();
- mData = event.getData();
- }
-
- public long getTimeNanos() {
- return mTimeNanos;
- }
-
- public int getTag() {
- return mTag;
- }
-
- public Object getData() {
- return mData;
- }
- }
-
- @VisibleForTesting
- static class LogReader {
- public void readEvents(int[] tags, Collection<Event> events) throws IOException {
- // Testing in Android: the Static Final Class Strikes Back!
- ArrayList<EventLog.Event> nativeEvents = new ArrayList<>();
- EventLog.readEventsOnWrapping(tags, 0L, nativeEvents);
- for (EventLog.Event nativeEvent : nativeEvents) {
- Event event = new Event(nativeEvent);
- events.add(event);
- }
- }
- }
-}
diff --git a/core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java b/core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java
deleted file mode 100644
index 1209e4d8dc8c..000000000000
--- a/core/java/com/android/internal/logging/legacy/LegacyConversionLogger.java
+++ /dev/null
@@ -1,101 +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.logging.legacy;
-
-import android.metrics.LogMaker;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Queue;
-
-/** @hide */
-public class LegacyConversionLogger implements TronLogger {
- private final Queue<LogMaker> mQueue;
- private HashMap<String, Boolean> mConfig;
-
- public LegacyConversionLogger() {
- mQueue = new LinkedList<>();
- }
-
- public Queue<LogMaker> getEvents() {
- return mQueue;
- }
-
- @Override
- public void increment(String counterName) {
- LogMaker b = new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER)
- .setCounterName(counterName)
- .setCounterValue(1);
- mQueue.add(b);
- }
-
- @Override
- public void incrementBy(String counterName, int value) {
- LogMaker b = new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_COUNTER)
- .setCounterName(counterName)
- .setCounterValue(value);
- mQueue.add(b);
- }
-
- @Override
- public void incrementIntHistogram(String counterName, int bucket) {
- LogMaker b = new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
- .setCounterName(counterName)
- .setCounterBucket(bucket)
- .setCounterValue(1);
- mQueue.add(b);
- }
-
- @Override
- public void incrementLongHistogram(String counterName, long bucket) {
- LogMaker b = new LogMaker(MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM)
- .setCounterName(counterName)
- .setCounterBucket(bucket)
- .setCounterValue(1);
- mQueue.add(b);
- }
-
- @Override
- public LogMaker obtain() {
- return new LogMaker(MetricsEvent.VIEW_UNKNOWN);
- }
-
- @Override
- public void dispose(LogMaker proto) {
- }
-
- @Override
- public void addEvent(LogMaker proto) {
- mQueue.add(proto);
- }
-
- @Override
- public boolean getConfig(String configName) {
- if (mConfig != null && mConfig.containsKey(configName)) {
- return mConfig.get(configName);
- }
- return false;
- }
-
- @Override
- public void setConfig(String configName, boolean newValue) {
- if (mConfig == null) {
- mConfig = new HashMap<>();
- }
- mConfig.put(configName, newValue);
- }
-}
diff --git a/core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java b/core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java
deleted file mode 100644
index e9baf9baf8da..000000000000
--- a/core/java/com/android/internal/logging/legacy/PowerScreenStateParser.java
+++ /dev/null
@@ -1,66 +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.logging.legacy;
-
-import android.util.Log;
-
-import android.metrics.LogMaker;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-/**
- * Parse the Android lockscreen gesture logs.
- * @hide
- */
-public class PowerScreenStateParser extends TagParser {
- private static final String TAG = "PowerScreenStateParser";
- private static final int EVENTLOG_TAG = 2728;
-
- // source of truth is android.view.WindowManagerPolicy, why:
- // 0: on
- // 1: OFF_BECAUSE_OF_ADMIN
- // 2: OFF_BECAUSE_OF_USER
- // 3: OFF_BECAUSE_OF_TIMEOUT
-
- @Override
- public int getTag() {
- return EVENTLOG_TAG;
- }
-
- @Override
- public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
- final boolean debug = Util.debug();
- if (operands.length >= 2) {
- try {
- // (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1)
- boolean state = (((Integer) operands[0]).intValue()) == 1;
- int why = ((Integer) operands[1]).intValue();
-
- LogMaker proto = logger.obtain();
- proto.setCategory(MetricsEvent.SCREEN);
- proto.setType(state ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE);
- proto.setTimestamp(eventTimeMs);
- proto.setSubtype(why);
- logger.addEvent(proto);
- } catch (ClassCastException e) {
- if (debug) {
- Log.e(TAG, "unexpected operand type: ", e);
- }
- }
- } else if (debug) {
- Log.w(TAG, "wrong number of operands: " + operands.length);
- }
- }
-}
diff --git a/core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java b/core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java
deleted file mode 100644
index 0c77b7a6ccd0..000000000000
--- a/core/java/com/android/internal/logging/legacy/SysuiMultiActionParser.java
+++ /dev/null
@@ -1,48 +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.logging.legacy;
-
-import android.util.Log;
-
-import android.metrics.LogMaker;
-
-/**
- * ...and one parser to rule them all.
- *
- * This should, at some point in the future, be the only parser.
- * @hide
- */
-public class SysuiMultiActionParser extends TagParser {
- private static final String TAG = "SysuiMultiActionParser";
- private static final int EVENTLOG_TAG = 524292;
-
- @Override
- public int getTag() {
- return EVENTLOG_TAG;
- }
-
- @Override
- public void parseEvent(TronLogger logger, long eventTimeMs, Object[] operands) {
- final boolean debug = Util.debug();
- try {
- logger.addEvent(new LogMaker(operands).setTimestamp(eventTimeMs));
- } catch (ClassCastException e) {
- if (debug) {
- Log.e(TAG, "unexpected operand type: ", e);
- }
- }
- }
-}
diff --git a/core/java/com/android/internal/logging/legacy/TagParser.java b/core/java/com/android/internal/logging/legacy/TagParser.java
deleted file mode 100755
index 3bffdd522236..000000000000
--- a/core/java/com/android/internal/logging/legacy/TagParser.java
+++ /dev/null
@@ -1,104 +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.logging.legacy;
-
-import android.util.Log;
-
-import android.metrics.LogMaker;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-/**
- * Abstraction layer between EventLog static classes and the actual TagParsers.
- * @hide
- */
-public abstract class TagParser {
- private static final String TAG = "TagParser";
-
- protected int mSinceCreationMillis;
- protected int mSinceUpdateMillis;
- protected int mSinceVisibleMillis;
-
- abstract int getTag();
-
- @VisibleForTesting
- abstract public void parseEvent(TronLogger logger, long eventTimeMs, Object[] objects);
-
- /**
- * Parse the event into the proto: return true if proto was modified.
- */
- public void parseEvent(TronLogger logger, EventLogCollector.Event event) {
- final boolean debug = Util.debug();
- Object data = event.getData();
- Object[] objects;
- if (data instanceof Object[]) {
- objects = (Object[]) data;
- for (int i = 0; i < objects.length; i++) {
- if (objects[i] == null) {
- if (debug) {
- Log.d(TAG, "unexpected null value:" + event.getTag());
- }
- return;
- }
- }
- } else {
- // wrap scalar objects
- objects = new Object[1];
- objects[0] = data;
- }
-
- parseEvent(logger, event.getTimeNanos() / 1000000, objects);
- }
-
- protected void resetTimes() {
- mSinceCreationMillis = 0;
- mSinceUpdateMillis = 0;
- mSinceVisibleMillis = 0;
- }
-
- public void parseTimes(Object[] operands, int index) {
- resetTimes();
-
- if (operands.length > index && operands[index] instanceof Integer) {
- mSinceCreationMillis = (Integer) operands[index];
- }
-
- index++;
- if (operands.length > index && operands[index] instanceof Integer) {
- mSinceUpdateMillis = (Integer) operands[index];
- }
-
- index++;
- if (operands.length > index && operands[index] instanceof Integer) {
- mSinceVisibleMillis = (Integer) operands[index];
- }
- }
-
- public void filltimes(LogMaker proto) {
- if (mSinceCreationMillis != 0) {
- proto.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS,
- mSinceCreationMillis);
- }
- if (mSinceUpdateMillis != 0) {
- proto.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS,
- mSinceUpdateMillis);
- }
- if (mSinceVisibleMillis != 0) {
- proto.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS,
- mSinceVisibleMillis);
- }
- }
-}
diff --git a/core/java/com/android/internal/logging/legacy/TronCounters.java b/core/java/com/android/internal/logging/legacy/TronCounters.java
deleted file mode 100644
index e0828a20d6ad..000000000000
--- a/core/java/com/android/internal/logging/legacy/TronCounters.java
+++ /dev/null
@@ -1,72 +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.logging.legacy;
-
-/**
- * Names of the counters that the Tron package defines.
- *
- * Other counter names may be generated by AOSP code.
- * @hide
- */
-public class TronCounters {
-
- static final String TRON_COLLECTIONS = "tron_collections";
-
- static final String TRON_COLLECTION_PERIOD = "tron_collection_period_minutes";
-
- static final String TRON_EVENTLOG_LENGTH = "tron_eventlog_horizon";
-
- static final String TRON_NOTE_DISMISS = "tron_note_dismiss";
-
- static final String TRON_NOTE_DISMISS_BY_USER = "tron_note_dismiss_user";
-
- static final String TRON_NOTE_DISMISS_BY_CLICK = "tron_note_dismiss_click";
-
- static final String TRON_NOTE_DISMISS_BY_LISTENER = "tron_note_dismiss_listener";
-
- static final String TRON_NOTE_DISMISS_BY_BAN = "tron_note_dismiss_ban";
-
- static final String TRON_NOTE_REVEALED = "tron_note_revealed";
-
- static final String TRON_NOTIFICATION_LOAD = "tron_notification_load";
-
- static final String TRON_VIEW = "tron_view";
-
- static final String TRON_ACTION = "tron_action";
-
- static final String TRON_DETAIL = "tron_detail";
-
- static final String TRON_NOTE_LIFETIME = "tron_note_lifetime";
-
- /** Append the AOSP-generated name */
- static final String TRON_AOSP_PREFIX = "tron_varz_";
-
- static final String TRON_ACTION_BAD_INT = "tron_action_bad_int";
-
- static final String TRON_NOTE_FRESHNESS = "tron_note_freshness";
-
- static final String TRON_NOTE_BUZZ = "tron_note_buzz";
-
- static final String TRON_NOTE_BEEP = "tron_note_beep";
-
- static final String TRON_NOTE_BLINK = "tron_note_blink";
-
- static final String TRON_DISABLE = "tron_disable";
-
- static final String TRON_LAST_HEART_AGE = "tron_last_heart_minutes";
-
- static final String TRON_HEARTS_SEEN = "tron_hearts_seen";
-}
diff --git a/core/java/com/android/internal/logging/legacy/TronLogger.java b/core/java/com/android/internal/logging/legacy/TronLogger.java
deleted file mode 100644
index ee9341ab2138..000000000000
--- a/core/java/com/android/internal/logging/legacy/TronLogger.java
+++ /dev/null
@@ -1,49 +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.logging.legacy;
-
-import android.metrics.LogMaker;
-
-/**
- * An entity that knows how to log events and counters.
- */
-public interface TronLogger {
- /** Add one to the named counter. */
- void increment(String counterName);
-
- /** Add an arbitrary value to the named counter. */
- void incrementBy(String counterName, int value);
-
- /** Increment a specified bucket on the named histogram by one. */
- void incrementIntHistogram(String counterName, int bucket);
-
- /** Increment the specified bucket on the named histogram by one. */
- void incrementLongHistogram(String counterName, long bucket);
-
- /** Obtain a SystemUiEvent proto, must release this with dispose() or addEvent(). */
- LogMaker obtain();
-
- void dispose(LogMaker proto);
-
- /** Submit an event to be logged. Logger will dispose of proto. */
- void addEvent(LogMaker proto);
-
- /** Get a config flag. */
- boolean getConfig(String configName);
-
- /** Set a config flag. */
- void setConfig(String configName, boolean newValue);
-}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 12d96e2d486f..2aeddb306c4d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3706,6 +3706,20 @@ public class BatteryStatsImpl extends BatteryStats {
public void noteScreenStateLocked(int state) {
state = mPretendScreenOff ? Display.STATE_OFF : state;
+
+ // Battery stats relies on there being 4 states. To accommodate this, new states beyond the
+ // original 4 are mapped to one of the originals.
+ if (state > MAX_TRACKED_SCREEN_STATE) {
+ switch (state) {
+ case Display.STATE_VR:
+ state = Display.STATE_ON;
+ break;
+ default:
+ Slog.wtf(TAG, "Unknown screen state (not mapped): " + state);
+ break;
+ }
+ }
+
if (mScreenState != state) {
recordDailyStatsIfNeededLocked(true);
final int oldState = mScreenState;
@@ -3715,9 +3729,9 @@ public class BatteryStatsImpl extends BatteryStats {
if (state != Display.STATE_UNKNOWN) {
int stepState = state-1;
- if (stepState < 4) {
- mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_SCREEN_STATE) ^ stepState;
- mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_SCREEN_STATE) | stepState;
+ if ((stepState & STEP_LEVEL_MODE_SCREEN_STATE) == stepState) {
+ mModStepMode |= (mCurStepMode & STEP_LEVEL_MODE_SCREEN_STATE) ^ stepState;
+ mCurStepMode = (mCurStepMode & ~STEP_LEVEL_MODE_SCREEN_STATE) | stepState;
} else {
Slog.wtf(TAG, "Unexpected screen state: " + state);
}
@@ -8870,6 +8884,10 @@ public class BatteryStatsImpl extends BatteryStats {
if (entry.rxBytes != 0) {
u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
entry.rxPackets);
+ if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers
+ u.noteNetworkActivityLocked(NETWORK_WIFI_BG_RX_DATA, entry.rxBytes,
+ entry.rxPackets);
+ }
mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
entry.rxBytes);
mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
@@ -8885,6 +8903,10 @@ public class BatteryStatsImpl extends BatteryStats {
if (entry.txBytes != 0) {
u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
entry.txPackets);
+ if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers
+ u.noteNetworkActivityLocked(NETWORK_WIFI_BG_TX_DATA, entry.txBytes,
+ entry.txPackets);
+ }
mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
entry.txBytes);
mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
@@ -9104,6 +9126,12 @@ public class BatteryStatsImpl extends BatteryStats {
final Uid u = getUidStatsLocked(mapUid(entry.uid));
u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes, entry.rxPackets);
u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes, entry.txPackets);
+ if (entry.set == NetworkStats.SET_DEFAULT) { // Background transfers
+ u.noteNetworkActivityLocked(NETWORK_MOBILE_BG_RX_DATA,
+ entry.rxBytes, entry.rxPackets);
+ u.noteNetworkActivityLocked(NETWORK_MOBILE_BG_TX_DATA,
+ entry.txBytes, entry.txPackets);
+ }
mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
entry.rxBytes);
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index 040f150e8c95..f88ac495fe52 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -18,9 +18,11 @@ package com.android.internal.policy;
import android.content.Context;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.util.Size;
import android.view.Gravity;
import android.view.ViewConfiguration;
import android.widget.Scroller;
@@ -56,6 +58,10 @@ public class PipSnapAlgorithm {
private final int mDefaultSnapMode = SNAP_MODE_CORNERS_AND_EDGES;
private int mSnapMode = mDefaultSnapMode;
+ private final float mDefaultSizePercent;
+ private final float mMinAspectRatioForMinSize;
+ private final float mMaxAspectRatioForMinSize;
+
private Scroller mScroller;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -63,9 +69,15 @@ public class PipSnapAlgorithm {
private boolean mIsMinimized;
public PipSnapAlgorithm(Context context) {
+ Resources res = context.getResources();
mContext = context;
- mMinimizedVisibleSize = context.getResources().getDimensionPixelSize(
+ mMinimizedVisibleSize = res.getDimensionPixelSize(
com.android.internal.R.dimen.pip_minimized_visible_size);
+ mDefaultSizePercent = res.getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent);
+ mMaxAspectRatioForMinSize = res.getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
+ mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
onConfigurationChanged();
}
@@ -242,6 +254,40 @@ public class PipSnapAlgorithm {
}
/**
+ * @return the size of the PiP at the given {@param aspectRatio}, ensuring that the minimum edge
+ * is at least {@param minEdgeSize}.
+ */
+ public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth,
+ int displayHeight) {
+ final int smallestDisplaySize = Math.min(displayWidth, displayHeight);
+ final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent);
+
+ final int width;
+ final int height;
+ if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) {
+ // Beyond these points, we can just use the min size as the shorter edge
+ if (aspectRatio <= 1) {
+ // Portrait, width is the minimum size
+ width = minSize;
+ height = Math.round(width / aspectRatio);
+ } else {
+ // Landscape, height is the minimum size
+ height = minSize;
+ width = Math.round(height * aspectRatio);
+ }
+ } else {
+ // Within these points, we ensure that the bounds fit within the radius of the limits
+ // at the points
+ final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize;
+ final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize);
+ height = (int) Math.round(Math.sqrt((radius * radius) /
+ (aspectRatio * aspectRatio + 1)));
+ width = Math.round(height * aspectRatio);
+ }
+ return new Size(width, height);
+ }
+
+ /**
* @return the closest point in {@param points} to the given {@param x} and {@param y}.
*/
private Point findClosestPoint(int x, int y, Point[] points) {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 2b1625432d52..be69d9f808e2 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -31,6 +31,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.function.Function;
/**
* ArrayUtils contains some methods that you can call to find out
@@ -474,12 +475,25 @@ public class ArrayUtils {
}
}
+ public static int size(@Nullable Collection<?> cur) {
+ return cur != null ? cur.size() : 0;
+ }
+
+ public static @NonNull <I, O> List<O> map(@Nullable List<I> cur,
+ Function<? super I, ? extends O> f) {
+ if (cur == null || cur.isEmpty()) return Collections.emptyList();
+ final ArrayList<O> result = new ArrayList<>();
+ for (int i = 0; i < cur.size(); i++) {
+ result.add(f.apply(cur.get(i)));
+ }
+ return result;
+ }
+
/**
* Returns the given list, or an immutable empty list if the provided list is null
*
* @see Collections#emptyList
*/
-
public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) {
return cur == null ? Collections.emptyList() : cur;
}
diff --git a/core/java/com/android/internal/util/ExponentiallyBucketedHistogram.java b/core/java/com/android/internal/util/ExponentiallyBucketedHistogram.java
new file mode 100644
index 000000000000..dc9f3f4d89dc
--- /dev/null
+++ b/core/java/com/android/internal/util/ExponentiallyBucketedHistogram.java
@@ -0,0 +1,97 @@
+/*
+ * 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.util;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * A histogram for positive integers where each bucket is twice the size of the previous one.
+ */
+public class ExponentiallyBucketedHistogram {
+ @NonNull
+ private final int[] mData;
+
+ /**
+ * Create a new histogram.
+ *
+ * @param numBuckets The number of buckets. The highest bucket is for all value >=
+ * 2<sup>numBuckets - 1</sup>
+ */
+ public ExponentiallyBucketedHistogram(@IntRange(from = 1, to = 31) int numBuckets) {
+ numBuckets = Preconditions.checkArgumentInRange(numBuckets, 1, 31, "numBuckets");
+
+ mData = new int[numBuckets];
+ }
+
+ /**
+ * Add a new value to the histogram.
+ *
+ * All values <= 0 are in the first bucket. The last bucket contains all values >=
+ * 2<sup>numBuckets - 1</sup>
+ *
+ * @param value The value to add
+ */
+ public void add(int value) {
+ if (value <= 0) {
+ mData[0]++;
+ } else {
+ mData[Math.min(mData.length - 1, 32 - Integer.numberOfLeadingZeros(value))]++;
+ }
+ }
+
+ /**
+ * Clear all data from the histogram
+ */
+ public void reset() {
+ Arrays.fill(mData, 0);
+ }
+
+ /**
+ * Write the histogram to the log.
+ *
+ * @param tag The tag to use when logging
+ * @param prefix A custom prefix that is printed in front of the histogram
+ */
+ public void log(@NonNull String tag, @Nullable CharSequence prefix) {
+ StringBuilder builder = new StringBuilder(prefix);
+ builder.append('[');
+
+ for (int i = 0; i < mData.length; i++) {
+ if (i != 0) {
+ builder.append(", ");
+ }
+
+ if (i < mData.length - 1) {
+ builder.append("<");
+ builder.append(1 << i);
+ } else {
+ builder.append(">=");
+ builder.append(1 << (i - 1));
+ }
+
+ builder.append(": ");
+ builder.append(mData[i]);
+ }
+ builder.append("]");
+
+ Log.d(tag, builder.toString());
+ }
+}
diff --git a/core/java/com/android/internal/view/SurfaceCallbackHelper.java b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
index 507b673ec279..5b6a82cf1c43 100644
--- a/core/java/com/android/internal/view/SurfaceCallbackHelper.java
+++ b/core/java/com/android/internal/view/SurfaceCallbackHelper.java
@@ -17,11 +17,14 @@
package com.android.internal.view;
import android.os.RemoteException;
+import android.view.IWindow;
+import android.view.IWindowSession;
import android.view.Surface;
import android.view.SurfaceHolder;
public class SurfaceCallbackHelper {
- Runnable mRunnable;
+ IWindowSession mSession;
+ IWindow.Stub mWindow;
int mFinishDrawingCollected = 0;
int mFinishDrawingExpected = 0;
@@ -34,18 +37,26 @@ public class SurfaceCallbackHelper {
if (mFinishDrawingCollected < mFinishDrawingExpected) {
return;
}
- mRunnable.run();
+ try {
+ mSession.finishDrawing(mWindow);
+ } catch (RemoteException e) {
+ }
}
}
};
- public SurfaceCallbackHelper(Runnable callbacksCollected) {
- mRunnable = callbacksCollected;
+ public SurfaceCallbackHelper(IWindowSession session,
+ IWindow.Stub window) {
+ mSession = session;
+ mWindow = window;
}
public void dispatchSurfaceRedrawNeededAsync(SurfaceHolder holder, SurfaceHolder.Callback callbacks[]) {
if (callbacks == null || callbacks.length == 0) {
- mRunnable.run();
+ try {
+ mSession.finishDrawing(mWindow);
+ } catch (RemoteException e) {
+ }
return;
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
index ace0cce10473..ac226ddb8ea9 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItem.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -145,12 +145,12 @@ public class ActionMenuItem implements MenuItem {
}
public MenuItem setAlphabeticShortcut(char alphaChar) {
- mShortcutAlphabeticChar = alphaChar;
+ mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
return this;
}
public MenuItem setAlphabeticShortcut(char alphachar, int alphaModifiers) {
- mShortcutAlphabeticChar = alphachar;
+ mShortcutAlphabeticChar = Character.toLowerCase(alphachar);
mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
return this;
}
@@ -210,7 +210,7 @@ public class ActionMenuItem implements MenuItem {
public MenuItem setShortcut(char numericChar, char alphaChar) {
mShortcutNumericChar = numericChar;
- mShortcutAlphabeticChar = alphaChar;
+ mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
return this;
}
@@ -218,7 +218,7 @@ public class ActionMenuItem implements MenuItem {
int alphaModifiers) {
mShortcutNumericChar = numericChar;
mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
- mShortcutAlphabeticChar = alphaChar;
+ mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
return this;
}
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 4c3118d4f072..9310d14ab667 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -255,7 +255,7 @@ public final class MenuItemImpl implements MenuItem {
return this;
}
- mShortcutAlphabeticChar = alphaChar;
+ mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
mMenu.onItemsChanged(false);
@@ -307,7 +307,7 @@ public final class MenuItemImpl implements MenuItem {
int alphaModifiers) {
mShortcutNumericChar = numericChar;
mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
- mShortcutAlphabeticChar = alphaChar;
+ mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
mMenu.onItemsChanged(false);
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index f4f49b1e4ffe..c64ace403f76 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -20,6 +20,8 @@ import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -59,4 +61,10 @@ public class NotificationExpandButton extends ImageView {
rect.top = rect.centerY() - touchTargetSize / 2;
rect.bottom = rect.top + touchTargetSize;
}
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(Button.class.getName());
+ }
}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 5a50fbfd5277..78e879795c06 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -35,6 +35,8 @@ import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -46,6 +48,8 @@ import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -83,6 +87,12 @@ public class BootReceiver extends BroadcastReceiver {
private static final File lastHeaderFile = new File(
Environment.getDataSystemDirectory(), LAST_HEADER_FILE);
+ // example: fs_stat,/dev/block/platform/soc/by-name/userdata,0x5
+ private static final String FS_STAT_PATTERN = "fs_stat,[^,]*/([^/,]+),(0x[0-9a-fA-F]+)";
+ // ro.boottime.init.mount_all. + postfix for mount_all duration
+ private static final String[] MOUNT_DURATION_PROPS_POSTFIX =
+ new String[] { "early", "default", "late" };
+
@Override
public void onReceive(final Context context, Intent intent) {
// Log boot events in the background to avoid blocking the main thread with I/O
@@ -200,10 +210,11 @@ public class BootReceiver extends BroadcastReceiver {
addFileToDropBox(db, timestamps, headers, "/cache/recovery/last_kmsg",
-LOG_SIZE, "SYSTEM_RECOVERY_KMSG");
addAuditErrorsToDropBox(db, timestamps, headers, -LOG_SIZE, "SYSTEM_AUDIT");
- addFsckErrorsToDropBox(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK");
} else {
if (db != null) db.addText("SYSTEM_RESTART", headers);
}
+ addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK");
+ logFsMountTime();
// Scan existing tombstones (in case any new ones appeared)
File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
@@ -297,11 +308,14 @@ public class BootReceiver extends BroadcastReceiver {
db.addText(tag, headers + sb.toString());
}
- private static void addFsckErrorsToDropBox(DropBoxManager db,
+ private static void addFsckErrorsToDropBoxAndLogFsStat(DropBoxManager db,
HashMap<String, Long> timestamps, String headers, int maxSize, String tag)
throws IOException {
- boolean upload_needed = false;
- if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
+ boolean uploadEnabled = true;
+ if (db == null || !db.isTagEnabled(tag)) {
+ uploadEnabled = false;
+ }
+ boolean uploadNeeded = false;
Slog.i(TAG, "Checking for fsck errors");
File file = new File("/dev/fscklogs/log");
@@ -310,14 +324,21 @@ public class BootReceiver extends BroadcastReceiver {
String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
StringBuilder sb = new StringBuilder();
- for (String line : log.split("\n")) {
+ Pattern pattern = Pattern.compile(FS_STAT_PATTERN);
+ for (String line : log.split("\n")) { // should check all lines
if (line.contains("FILE SYSTEM WAS MODIFIED")) {
- upload_needed = true;
- break;
+ uploadNeeded = true;
+ } else if (line.contains("fs_stat")){
+ Matcher matcher = pattern.matcher(line);
+ if (matcher.find()) {
+ handleFsckFsStat(matcher);
+ } else {
+ Slog.w(TAG, "cannot parse fs_stat:" + line);
+ }
}
}
- if (upload_needed) {
+ if (uploadEnabled && uploadNeeded ) {
addFileToDropBox(db, timestamps, headers, "/dev/fscklogs/log", maxSize, tag);
}
@@ -325,6 +346,29 @@ public class BootReceiver extends BroadcastReceiver {
file.delete();
}
+ private static void logFsMountTime() {
+ for (String propPostfix : MOUNT_DURATION_PROPS_POSTFIX) {
+ int duration = SystemProperties.getInt("ro.boottime.init.mount_all." + propPostfix, 0);
+ if (duration != 0) {
+ MetricsLogger.histogram(null, "boot_mount_all_duration_" + propPostfix, duration);
+ }
+ }
+ }
+
+ private static void handleFsckFsStat(Matcher match) {
+ String partition = match.group(1);
+ int stat;
+ try {
+ stat = Integer.decode(match.group(2));
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "cannot parse fs_stat: partition:" + partition + " stat:" + match.group(2));
+ return;
+ }
+
+ MetricsLogger.histogram(null, "boot_fs_stat_" + partition, stat);
+ Slog.i(TAG, "fs_stat, partition:" + partition + " stat:" + match.group(2));
+ }
+
private static HashMap<String, Long> readTimestamps() {
synchronized (sFile) {
HashMap<String, Long> timestamps = new HashMap<String, Long>();
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 68392a9f52bc..c9f9d6f6dfb0 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -249,6 +249,7 @@ LOCAL_SHARED_LIBRARIES := \
libbinder \
libui \
libgui \
+ libsensor \
libinput \
libcamera_client \
libcamera_metadata \
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index a196540078f5..f85219453212 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -8,7 +8,10 @@
#include "SkImageInfo.h"
#include "SkColor.h"
#include "SkColorPriv.h"
+#include "SkColorSpace.h"
+#include "SkColorSpaceXform.h"
#include "SkHalf.h"
+#include "SkMatrix44.h"
#include "SkPM4f.h"
#include "SkPM4fPriv.h"
#include "GraphicsJNI.h"
@@ -28,6 +31,7 @@
#include "core_jni_helpers.h"
#include <jni.h>
+#include <string.h>
#include <memory>
#include <string>
@@ -447,11 +451,32 @@ bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int
// reset to to actual choice from caller
dst = dstBitmap.getAddr(x, y);
- // now copy/convert each scanline
- for (int y = 0; y < height; y++) {
- proc(dst, src, width, x, y);
- src += srcStride;
- dst = (char*)dst + dstBitmap.rowBytes();
+
+ SkColorSpace* colorSpace = dstBitmap.colorSpace();
+ if (GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+ // now copy/convert each scanline
+ for (int y = 0; y < height; y++) {
+ proc(dst, src, width, x, y);
+ src += srcStride;
+ dst = (char*)dst + dstBitmap.rowBytes();
+ }
+ } else {
+ auto sRGB = SkColorSpace::MakeSRGB();
+ auto xform = SkColorSpaceXform::New(sRGB.get(), colorSpace);
+
+ std::unique_ptr<SkColor[]> row(new SkColor[width]);
+
+ // now copy/convert each scanline
+ for (int y = 0; y < height; y++) {
+ memcpy(row.get(), src, sizeof(SkColor) * width);
+ xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, row.get(),
+ SkColorSpaceXform::kBGRA_8888_ColorFormat, row.get(), width,
+ SkAlphaType::kUnpremul_SkAlphaType);
+
+ proc(dst, row.get(), width, x, y);
+ src += srcStride;
+ dst = (char*)dst + dstBitmap.rowBytes();
+ }
}
dstBitmap.notifyPixelsChanged();
@@ -923,7 +948,13 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
const bool isMutable = p->readInt32() != 0;
const SkColorType colorType = (SkColorType)p->readInt32();
const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
- const bool isSRGB = p->readInt32() != 0;
+ const uint32_t colorSpaceSize = p->readUint32();
+ sk_sp<SkColorSpace> colorSpace;
+ if (kRGBA_F16_SkColorType == colorType) {
+ colorSpace = SkColorSpace::MakeSRGBLinear();
+ } else if (colorSpaceSize > 0) {
+ colorSpace = SkColorSpace::Deserialize(p->readInplace(colorSpaceSize), colorSpaceSize);
+ }
const int width = p->readInt32();
const int height = p->readInt32();
const int rowBytes = p->readInt32();
@@ -940,14 +971,6 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
}
std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
-
- sk_sp<SkColorSpace> colorSpace;
- if (kRGBA_F16_SkColorType == colorType) {
- colorSpace = SkColorSpace::MakeSRGBLinear();
- } else {
- colorSpace = isSRGB ? SkColorSpace::MakeSRGB() : nullptr;
- }
-
if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, colorSpace),
rowBytes)) {
return NULL;
@@ -1064,13 +1087,20 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle);
bitmapWrapper->getSkBitmap(&bitmap);
- sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
- bool isSRGB = bitmap.colorSpace() == sRGB.get();
-
p->writeInt32(isMutable);
p->writeInt32(bitmap.colorType());
p->writeInt32(bitmap.alphaType());
- p->writeInt32(isSRGB); // TODO: We should write the color space (b/32072280)
+ SkColorSpace* colorSpace = bitmap.colorSpace();
+ if (colorSpace != nullptr && bitmap.colorType() != kRGBA_F16_SkColorType) {
+ sk_sp<SkData> data = colorSpace->serialize();
+ size_t size = data->size();
+ p->writeUint32(size);
+ if (size > 0) {
+ p->write(data->data(), size);
+ }
+ } else {
+ p->writeUint32(0);
+ }
p->writeInt32(bitmap.width());
p->writeInt32(bitmap.height());
p->writeInt32(bitmap.rowBytes());
@@ -1168,6 +1198,56 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
///////////////////////////////////////////////////////////////////////////////
+static jboolean Bitmap_isSRGB(JNIEnv* env, jobject, jlong bitmapHandle) {
+ LocalScopedBitmap bitmapHolder(bitmapHandle);
+ if (!bitmapHolder.valid()) return JNI_TRUE;
+
+ SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
+ return GraphicsJNI::isColorSpaceSRGB(colorSpace);
+}
+
+static jboolean Bitmap_getColorSpace(JNIEnv* env, jobject, jlong bitmapHandle,
+ jfloatArray xyzArray, jfloatArray paramsArray) {
+
+ LocalScopedBitmap bitmapHolder(bitmapHandle);
+ if (!bitmapHolder.valid()) return JNI_FALSE;
+
+ SkColorSpace* colorSpace = bitmapHolder->info().colorSpace();
+ if (colorSpace == nullptr) return JNI_FALSE;
+
+ SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
+ if (!colorSpace->toXYZD50(&xyzMatrix)) return JNI_FALSE;
+
+ jfloat* xyz = env->GetFloatArrayElements(xyzArray, NULL);
+ xyz[0] = xyzMatrix.getFloat(0, 0);
+ xyz[1] = xyzMatrix.getFloat(1, 0);
+ xyz[2] = xyzMatrix.getFloat(2, 0);
+ xyz[3] = xyzMatrix.getFloat(0, 1);
+ xyz[4] = xyzMatrix.getFloat(1, 1);
+ xyz[5] = xyzMatrix.getFloat(2, 1);
+ xyz[6] = xyzMatrix.getFloat(0, 2);
+ xyz[7] = xyzMatrix.getFloat(1, 2);
+ xyz[8] = xyzMatrix.getFloat(2, 2);
+ env->ReleaseFloatArrayElements(xyzArray, xyz, 0);
+
+ SkColorSpaceTransferFn transferParams;
+ if (!colorSpace->isNumericalTransferFn(&transferParams)) return JNI_FALSE;
+
+ jfloat* params = env->GetFloatArrayElements(paramsArray, NULL);
+ params[0] = transferParams.fA;
+ params[1] = transferParams.fB;
+ params[2] = transferParams.fC;
+ params[3] = transferParams.fD;
+ params[4] = transferParams.fE;
+ params[5] = transferParams.fF;
+ params[6] = transferParams.fG;
+ env->ReleaseFloatArrayElements(paramsArray, params, 0);
+
+ return JNI_TRUE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
jint x, jint y) {
SkBitmap bitmap;
@@ -1185,6 +1265,16 @@ static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
SkColor dst[1];
proc(dst, src, 1, bitmap.getColorTable());
+
+ SkColorSpace* colorSpace = bitmap.colorSpace();
+ if (!GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+ auto sRGB = SkColorSpace::MakeSRGB();
+ auto xform = SkColorSpaceXform::New(colorSpace, sRGB.get());
+ xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &dst[0],
+ SkColorSpaceXform::kBGRA_8888_ColorFormat, &dst[0], 1,
+ SkAlphaType::kUnpremul_SkAlphaType);
+ }
+
return static_cast<jint>(dst[0]);
}
@@ -1207,11 +1297,30 @@ static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
SkColorTable* ctable = bitmap.getColorTable();
jint* dst = env->GetIntArrayElements(pixelArray, NULL);
SkColor* d = (SkColor*)dst + offset;
- while (--height >= 0) {
- proc(d, src, width, ctable);
- d += stride;
- src = (void*)((const char*)src + bitmap.rowBytes());
+
+ SkColorSpace* colorSpace = bitmap.colorSpace();
+ if (GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+ while (--height >= 0) {
+ proc(d, src, width, ctable);
+ d += stride;
+ src = (void*)((const char*)src + bitmap.rowBytes());
+ }
+ } else {
+ auto sRGB = SkColorSpace::MakeSRGB();
+ auto xform = SkColorSpaceXform::New(colorSpace, sRGB.get());
+
+ while (--height >= 0) {
+ proc(d, src, width, ctable);
+
+ xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, d,
+ SkColorSpaceXform::kBGRA_8888_ColorFormat, d, width,
+ SkAlphaType::kUnpremul_SkAlphaType);
+
+ d += stride;
+ src = (void*)((const char*)src + bitmap.rowBytes());
+ }
}
+
env->ReleaseIntArrayElements(pixelArray, dst, 0);
}
@@ -1232,6 +1341,15 @@ static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
return;
}
+ SkColorSpace* colorSpace = bitmap.colorSpace();
+ if (!GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+ auto sRGB = SkColorSpace::MakeSRGB();
+ auto xform = SkColorSpaceXform::New(sRGB.get(), colorSpace);
+ xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &color,
+ SkColorSpaceXform::kBGRA_8888_ColorFormat, &color, 1,
+ SkAlphaType::kUnpremul_SkAlphaType);
+ }
+
proc(bitmap.getAddr(x, y), &color, 1, x, y);
bitmap.notifyPixelsChanged();
}
@@ -1459,7 +1577,9 @@ static const JNINativeMethod gBitmapMethods[] = {
{ "nativeCreateHardwareBitmap", "(Landroid/graphics/GraphicBuffer;)Landroid/graphics/Bitmap;",
(void*) Bitmap_createHardwareBitmap },
{ "nativeCreateGraphicBufferHandle", "(J)Landroid/graphics/GraphicBuffer;",
- (void*) Bitmap_createGraphicBufferHandle }
+ (void*) Bitmap_createGraphicBufferHandle },
+ { "nativeGetColorSpace", "(J[F[F)Z", (void*)Bitmap_getColorSpace },
+ { "nativeIsSRGB", "(J)Z", (void*)Bitmap_isSRGB },
};
int register_android_graphics_Bitmap(JNIEnv* env)
diff --git a/core/jni/android/graphics/GraphicBuffer.cpp b/core/jni/android/graphics/GraphicBuffer.cpp
index e661c210d5dc..73e53c6f93a2 100644
--- a/core/jni/android/graphics/GraphicBuffer.cpp
+++ b/core/jni/android/graphics/GraphicBuffer.cpp
@@ -30,8 +30,6 @@
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
-#include <gui/IGraphicBufferAlloc.h>
-#include <gui/ISurfaceComposer.h>
#include <hwui/Bitmap.h>
#include <SkCanvas.h>
@@ -111,21 +109,14 @@ static jlong android_graphics_GraphicBuffer_wrap(JNIEnv* env, jobject clazz,
static jlong android_graphics_GraphicBuffer_create(JNIEnv* env, jobject clazz,
jint width, jint height, jint format, jint usage) {
- sp<ISurfaceComposer> composer(ComposerService::getComposerService());
- sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
- if (alloc == NULL) {
- if (kDebugGraphicBuffer) {
- ALOGW("createGraphicBufferAlloc() failed in GraphicBuffer.create()");
- }
- return NULL;
- }
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ uint32_t(width), uint32_t(height), PixelFormat(format), uint32_t(usage),
+ std::string("android_graphics_GraphicBuffer_create pid [") +
+ std::to_string(getpid()) +"]");
- status_t error;
- sp<GraphicBuffer> buffer(alloc->createGraphicBuffer(width, height, format, 1, usage, &error));
- if (buffer == NULL) {
- if (kDebugGraphicBuffer) {
- ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
- }
+ status_t error = buffer->initCheck();
+ if (error < 0) {
+ ALOGW_IF(kDebugGraphicBuffer, "createGraphicBuffer() failed in GraphicBuffer.create()");
return NULL;
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5d7310167b7c..7c56c7bf4158 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -460,6 +460,15 @@ sk_sp<SkColorSpace> GraphicsJNI::colorSpaceForType(SkColorType type) {
}
}
+bool GraphicsJNI::isColorSpaceSRGB(SkColorSpace* colorSpace) {
+ return colorSpace == nullptr
+ || colorSpace == SkColorSpace::MakeSRGB().get()
+ || colorSpace == SkColorSpace::MakeRGB(
+ SkColorSpace::kSRGB_RenderTargetGamma,
+ SkColorSpace::kSRGB_Gamut,
+ SkColorSpace::kNonLinearBlending_ColorSpaceFlag).get();
+}
+
///////////////////////////////////////////////////////////////////////////////
bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
mStorage = android::Bitmap::allocateHeapBitmap(bitmap, ctable);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 8a1ef6ee5d34..7d7c88159a55 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -111,6 +111,7 @@ public:
static sk_sp<SkColorSpace> defaultColorSpace();
static sk_sp<SkColorSpace> linearColorSpace();
static sk_sp<SkColorSpace> colorSpaceForType(SkColorType type);
+ static bool isColorSpaceSRGB(SkColorSpace* colorSpace);
};
class HeapAllocator : public SkBRDAllocator {
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index 3fc3aaf6c8f3..f1fa76a1fc24 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -205,6 +205,10 @@ static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jlong windowPtr,
if (type == CursorWindow::FIELD_TYPE_BLOB || type == CursorWindow::FIELD_TYPE_STRING) {
size_t size;
const void* value = window->getFieldSlotValueBlob(fieldSlot, &size);
+ if (!value) {
+ throw_sqlite3_exception(env, "Native could not read blob slot");
+ return NULL;
+ }
jbyteArray byteArray = env->NewByteArray(size);
if (!byteArray) {
env->ExceptionClear();
@@ -240,6 +244,10 @@ static jstring nativeGetString(JNIEnv* env, jclass clazz, jlong windowPtr,
if (type == CursorWindow::FIELD_TYPE_STRING) {
size_t sizeIncludingNull;
const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
+ if (!value) {
+ throw_sqlite3_exception(env, "Native could not read string slot");
+ return NULL;
+ }
if (sizeIncludingNull <= 1) {
return gEmptyString;
}
diff --git a/core/jni/android_hardware_HardwareBuffer.cpp b/core/jni/android_hardware_HardwareBuffer.cpp
index b91bd5c3aef8..ed0ab6054be7 100644
--- a/core/jni/android_hardware_HardwareBuffer.cpp
+++ b/core/jni/android_hardware_HardwareBuffer.cpp
@@ -31,8 +31,6 @@
#include <binder/Parcel.h>
#include <ui/GraphicBuffer.h>
-#include <gui/IGraphicBufferAlloc.h>
-#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
#include <hardware/gralloc1.h>
@@ -73,15 +71,6 @@ public:
static jlong android_hardware_HardwareBuffer_create(JNIEnv* env, jobject clazz,
jint width, jint height, jint format, jint layers, jlong usage) {
- sp<ISurfaceComposer> composer(ComposerService::getComposerService());
- sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
- if (alloc == NULL) {
- if (kDebugGraphicBuffer) {
- ALOGW("createGraphicBufferAlloc() failed in HardwareBuffer.create()");
- }
- return NULL;
- }
-
// TODO: update createGraphicBuffer to take two 64-bit values.
int pixelFormat = android_hardware_HardwareBuffer_convertToPixelFormat(format);
if (pixelFormat == 0) {
@@ -92,14 +81,14 @@ static jlong android_hardware_HardwareBuffer_create(JNIEnv* env, jobject clazz,
}
uint64_t producerUsage = 0;
uint64_t consumerUsage = 0;
- android_hardware_HardwareBuffer_convertToGrallocUsageBits(&producerUsage, &consumerUsage, usage,
- 0);
- status_t error;
- sp<GraphicBuffer> buffer(alloc->createGraphicBuffer(width, height, pixelFormat,
- layers, producerUsage, consumerUsage,
- std::string("HardwareBuffer pid [") + std::to_string(getpid()) +"]",
- &error));
- if (buffer == NULL) {
+ android_hardware_HardwareBuffer_convertToGrallocUsageBits(
+ &producerUsage, &consumerUsage, usage, 0);
+
+ sp<GraphicBuffer> buffer = new GraphicBuffer(width, height, pixelFormat, layers,
+ producerUsage, consumerUsage,
+ std::string("HardwareBuffer pid [") + std::to_string(getpid()) +"]");
+ status_t error = buffer->initCheck();
+ if (error < 0) {
if (kDebugGraphicBuffer) {
ALOGW("createGraphicBuffer() failed in HardwareBuffer.create()");
}
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index bc2fc1c39890..dae431057209 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -23,9 +23,10 @@
#include <ScopedUtfChars.h>
#include <ScopedLocalRef.h>
#include <android_runtime/AndroidRuntime.h>
-#include <gui/Sensor.h>
-#include <gui/SensorEventQueue.h>
-#include <gui/SensorManager.h>
+#include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <sensor/Sensor.h>
+#include <sensor/SensorEventQueue.h>
+#include <sensor/SensorManager.h>
#include <cutils/native_handle.h>
#include <utils/Log.h>
#include <utils/Looper.h>
@@ -245,39 +246,28 @@ static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong s
}
static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
- jlong size, jint channelType, jlongArray channelData) {
- jint ret = -1;
- jsize len = _env->GetArrayLength(channelData);
- if (len > 2) {
- jlong *data = _env->GetLongArrayElements(channelData, NULL);
- if (data != nullptr) {
- // construct native handle from jlong*
- jlong numFd = data[0];
- jlong numInt = data[1];
- if ((numFd + numInt + 2) == len) {
- native_handle_t *nativeHandle = native_handle_create(numFd, numInt);
- if (nativeHandle != nullptr) {
- const jlong *readPointer = data + 2;
- int *writePointer = nativeHandle->data;
- size_t n = static_cast<size_t>(numFd + numInt);
- while (n--) {
- // native type of data is int, jlong is just to ensure Java does not
- // truncate data on 64-bit system. The cast here is safe.
- *writePointer++ = static_cast<int>(*readPointer++);
- }
-
- SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
- ret = mgr->createDirectChannel(size, channelType, nativeHandle);
-
- // do not native_handle_close() here as handle is owned by java
- native_handle_delete(nativeHandle);
- }
- }
- // unidirectional parameter passing, thus JNI_ABORT
- _env->ReleaseLongArrayElements(channelData, data, JNI_ABORT);
+ jlong size, jint channelType, jint fd, jobject hardwareBufferObj) {
+ const native_handle_t *nativeHandle = nullptr;
+ NATIVE_HANDLE_DECLARE_STORAGE(ashmemHandle, 1, 0);
+
+ if (channelType == SENSOR_DIRECT_MEM_TYPE_ASHMEM) {
+ native_handle_t *handle = native_handle_init(ashmemHandle, 1, 0);
+ handle->data[0] = fd;
+ nativeHandle = handle;
+ } else if (channelType == SENSOR_DIRECT_MEM_TYPE_GRALLOC) {
+ AHardwareBuffer *hardwareBuffer =
+ android_hardware_HardwareBuffer_getNativeHardwareBuffer(_env, hardwareBufferObj);
+ if (hardwareBuffer != nullptr) {
+ nativeHandle = AHardwareBuffer_getNativeHandle(hardwareBuffer);
}
}
- return ret;
+
+ if (nativeHandle == nullptr) {
+ return BAD_VALUE;
+ }
+
+ SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
+ return mgr->createDirectChannel(size, channelType, nativeHandle);
}
static void nativeDestroyDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
@@ -499,7 +489,7 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = {
(void*)nativeIsDataInjectionEnabled },
{"nativeCreateDirectChannel",
- "(JJI[J)I",
+ "(JJIILandroid/hardware/HardwareBuffer;)I",
(void*)nativeCreateDirectChannel },
{"nativeDestroyDirectChannel",
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 90ed6eb5e60e..4a445d8705ca 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -121,7 +121,8 @@ static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) {
b->finish();
}
-static jlong nLoadHyphenator(JNIEnv* env, jclass, jobject buffer, jint offset) {
+static jlong nLoadHyphenator(JNIEnv* env, jclass, jobject buffer, jint offset,
+ jint minPrefix, jint minSuffix) {
const uint8_t* bytebuf = nullptr;
if (buffer != nullptr) {
void* rawbuf = env->GetDirectBufferAddress(buffer);
@@ -131,7 +132,8 @@ static jlong nLoadHyphenator(JNIEnv* env, jclass, jobject buffer, jint offset) {
ALOGE("failed to get direct buffer address");
}
}
- minikin::Hyphenator* hyphenator = minikin::Hyphenator::loadBinary(bytebuf);
+ minikin::Hyphenator* hyphenator = minikin::Hyphenator::loadBinary(
+ bytebuf, minPrefix, minSuffix);
return reinterpret_cast<jlong>(hyphenator);
}
@@ -191,7 +193,7 @@ static const JNINativeMethod gMethods[] = {
{"nNewBuilder", "()J", (void*) nNewBuilder},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
{"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
- {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;I)J", (void*) nLoadHyphenator},
+ {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;III)J", (void*) nLoadHyphenator},
{"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
{"nSetupParagraph", "(J[CIFIF[IIIIZ)V", (void*) nSetupParagraph},
{"nSetIndents", "(J[I)V", (void*) nSetIndents},
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index c2800e78698d..373bda96afba 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -119,6 +119,96 @@ jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
return block;
}
+// This is called by zygote (running as user root) as part of preloadResources.
+static void verifySystemIdmaps()
+{
+ pid_t pid;
+ char system_id[10];
+
+ snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);
+
+ switch (pid = fork()) {
+ case -1:
+ ALOGE("failed to fork for idmap: %s", strerror(errno));
+ break;
+ case 0: // child
+ {
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata;
+
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+
+ capheader.version = _LINUX_CAPABILITY_VERSION;
+ capheader.pid = 0;
+
+ if (capget(&capheader, &capdata) != 0) {
+ ALOGE("capget: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ capdata.effective = capdata.permitted;
+ if (capset(&capheader, &capdata) != 0) {
+ ALOGE("capset: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (setgid(AID_SYSTEM) != 0) {
+ ALOGE("setgid: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (setuid(AID_SYSTEM) != 0) {
+ ALOGE("setuid: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ // Generic idmap parameters
+ const char* argv[8];
+ int argc = 0;
+ struct stat st;
+
+ memset(argv, NULL, sizeof(argv));
+ argv[argc++] = AssetManager::IDMAP_BIN;
+ argv[argc++] = "--scan";
+ argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
+ argv[argc++] = AssetManager::TARGET_APK_PATH;
+ argv[argc++] = AssetManager::IDMAP_DIR;
+
+ // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
+ // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
+ char subdir[PROP_VALUE_MAX];
+ int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PERSIST_PROPERTY,
+ subdir);
+ if (len == 0) {
+ len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir);
+ }
+ if (len > 0) {
+ String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
+ if (stat(overlayPath.string(), &st) == 0) {
+ argv[argc++] = overlayPath.string();
+ }
+ }
+ if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::OVERLAY_DIR;
+ }
+
+ // Finally, invoke idmap (if any overlay directory exists)
+ if (argc > 5) {
+ execv(AssetManager::IDMAP_BIN, (char* const*)argv);
+ ALOGE("failed to execv for idmap: %s", strerror(errno));
+ exit(1); // should never get here
+ } else {
+ exit(0);
+ }
+ }
+ break;
+ default: // parent
+ waitpid(pid, NULL, 0);
+ break;
+ }
+}
+
// ----------------------------------------------------------------------------
// this guy is exported to other jni routines
@@ -1507,6 +1597,9 @@ static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jo
static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
+ if (isSystem) {
+ verifySystemIdmaps();
+ }
AssetManager* am = new AssetManager();
if (am == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", "");
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 6e8c93132562..f221392f16bd 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -452,6 +452,10 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
const RenderProperties& props = node.properties();
uirenderer::Rect bounds(props.getWidth(), props.getHeight());
transform.mapRect(bounds);
+ bounds.left -= info.windowInsetLeft;
+ bounds.right -= info.windowInsetLeft;
+ bounds.top -= info.windowInsetTop;
+ bounds.bottom -= info.windowInsetTop;
if (CC_LIKELY(transform.isPureTranslate())) {
// snap/round the computed bounds, so they match the rounding behavior
@@ -623,9 +627,9 @@ static const JNINativeMethod gMethods[] = {
int register_android_view_RenderNode(JNIEnv* env) {
jclass clazz = FindClassOrDie(env, "android/view/SurfaceView");
gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz,
- "updateSurfacePosition_renderWorker", "(JIIII)V");
+ "updateWindowPosition_renderWorker", "(JIIII)V");
gSurfaceViewPositionLostMethod = GetMethodIDOrDie(env, clazz,
- "surfacePositionLost_uiRtSync", "(J)V");
+ "windowPositionLost_uiRtSync", "(J)V");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index be86f5c6b8ab..a81901df9a1b 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -693,6 +693,7 @@ static jboolean nativeGetAnimationFrameStats(JNIEnv* env, jclass clazz, jobject
return JNI_TRUE;
}
+
static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeObject,
jobject handleObject, jlong frameNumber) {
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
@@ -701,27 +702,6 @@ static void nativeDeferTransactionUntil(JNIEnv* env, jclass clazz, jlong nativeO
ctrl->deferTransactionUntil(handle, frameNumber);
}
-static void nativeDeferTransactionUntilSurface(JNIEnv* env, jclass clazz, jlong nativeObject,
- jobject surfaceObject, jlong frameNumber) {
- auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- sp<Surface> barrier = reinterpret_cast<Surface *>(surfaceObject);
-
- ctrl->deferTransactionUntil(barrier, frameNumber);
-}
-
-static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong nativeObject,
- jobject newParentObject) {
- auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- sp<IBinder> handle = ibinderForJavaObject(env, newParentObject);
-
- ctrl->reparentChildren(handle);
-}
-
-static void nativeSeverChildren(JNIEnv* env, jclass clazz, jlong nativeObject) {
- auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- ctrl->detachChildren();
-}
-
static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong nativeObject,
jint scalingMode) {
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
@@ -844,12 +824,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetDisplayPowerMode },
{"nativeDeferTransactionUntil", "(JLandroid/os/IBinder;J)V",
(void*)nativeDeferTransactionUntil },
- {"nativeDeferTransactionUntilSurface", "(JJJ)V",
- (void*)nativeDeferTransactionUntilSurface },
- {"nativeReparentChildren", "(JLandroid/os/IBinder;)V",
- (void*)nativeReparentChildren } ,
- {"nativeSeverChildren", "(J)V",
- (void*)nativeSeverChildren } ,
{"nativeSetOverrideScalingMode", "(JI)V",
(void*)nativeSetOverrideScalingMode },
{"nativeGetHandle", "(J)Landroid/os/IBinder;",
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 508d89795569..dad6958560c0 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -24,7 +24,6 @@
#include <utils/RefBase.h>
#include <gui/SurfaceComposerClient.h>
-#include <gui/Surface.h>
namespace android {
@@ -46,13 +45,6 @@ static jlong nativeCreate(JNIEnv* env, jclass clazz) {
return reinterpret_cast<jlong>(client);
}
-static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) {
- Surface *parent = reinterpret_cast<Surface*>(surfaceObject);
- SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer());
- client->incStrong((void*)nativeCreate);
- return reinterpret_cast<jlong>(client);
-}
-
static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
client->decStrong((void*)nativeCreate);
@@ -63,12 +55,11 @@ static void nativeKill(JNIEnv* env, jclass clazz, jlong ptr) {
client->dispose();
}
+
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "nativeCreate", "()J",
(void*)nativeCreate },
- { "nativeCreateScoped", "(J)J",
- (void*)nativeCreateScoped },
{ "nativeDestroy", "(J)V",
(void*)nativeDestroy },
{ "nativeKill", "(J)V",
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 99edf6ef944e..37eae48a7a11 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -178,9 +178,13 @@ public:
}
}
// TODO: This is hacky
+ info.windowInsetLeft = -stagingProperties().getLeft();
+ info.windowInsetTop = -stagingProperties().getTop();
info.updateWindowPositions = true;
RenderNode::prepareTree(info);
info.updateWindowPositions = false;
+ info.windowInsetLeft = 0;
+ info.windowInsetTop = 0;
info.errorHandler = nullptr;
}
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 0dfeb6286fb3..448cd2e30ef4 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -97,7 +97,7 @@
android:layout_height="wrap_content"
android:paddingTop="1dp"
android:visibility="gone"
- android:contentDescription="@string/expand_button_content_description"
+ android:contentDescription="@string/expand_button_content_description_collapsed"
/>
<ImageView android:id="@+id/profile_badge"
android:layout_width="@dimen/notification_badge_size"
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index c27cb06deb8d..1987ac454d86 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -24,9 +24,10 @@
<!-- Flags enabling default window features. See Window.java -->
<bool name="config_defaultWindowFeatureOptionsPanel">false</bool>
- <!-- Max default size [WIDTHxHEIGHT] on screen for picture-in-picture windows to fit inside.
- These values are in DPs and will be converted to pixel sizes internally. -->
- <string translatable="false" name="config_defaultPictureInPictureSize">240x135</string>
+ <!-- The percentage of the screen width to use for the default width or height of
+ picture-in-picture windows. Regardless of the percent set here, calculated size will never
+ be smaller than @dimen/default_minimal_size_pip_resizable_task. -->
+ <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.14</item>
<!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows.
These values are in DPs and will be converted to pixel sizes internally. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2e09e7d1b0c5..b3cb2c7d548e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2339,6 +2339,17 @@
<flag name="creditCardExpirationDay" value="0x1000" />
</attr>
+ <!-- Hints the Android System whether the view node associated with this View should be
+ included in a view structure used for autofill purposes. -->
+ <attr name="importantForAutofill">
+ <!-- Let the Android System use its heuristics to determine if the view is important for autofill. -->
+ <flag name="auto" value="0" />
+ <!-- Hint the Android System that this view is important for autofill. -->
+ <flag name="yes" value="0x1" />
+ <!-- Hint the Android System that this view is *not* important for autofill. -->
+ <flag name="no" value="0x2" />
+ </attr>
+
<!-- Boolean that controls whether a view can take focus while in touch mode.
If this is true for a view, that view can gain focus when clicked on, and can keep
focus if another view is clicked on that doesn't have this attribute set to true. -->
@@ -3336,7 +3347,7 @@
{@link android.accessibilityservice.AccessibilityService#SERVICE_META_DATA}
meta-data entry. -->
<declare-styleable name="AccessibilityService">
- <!-- The event types this serivce would like to receive as specified in
+ <!-- The event types this service would like to receive as specified in
{@link android.view.accessibility.AccessibilityEvent}. This setting
can be changed at runtime by calling
{@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
@@ -3395,11 +3406,11 @@
<!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPES_ALL_MASK} i.e. all events. -->
<flag name="typeAllMask" value="0xffffffff" />
</attr>
- <!-- Comma separated package names from which this serivce would like to receive events (leave out for all packages).
+ <!-- Comma separated package names from which this service would like to receive events (leave out for all packages).
{@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
<attr name="packageNames" format="string" />
- <!-- The feedback types this serivce provides as specified in
+ <!-- The feedback types this service provides as specified in
{@link android.accessibilityservice.AccessibilityServiceInfo}. This setting
can be changed at runtime by calling
{@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
@@ -3419,7 +3430,7 @@
<flag name="feedbackAllMask" value="0xffffffff" />
</attr>
<!-- The minimal period in milliseconds between two accessibility events of the same type
- are sent to this serivce. This setting can be changed at runtime by calling
+ are sent to this service. This setting can be changed at runtime by calling
{@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)
android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->
<attr name="notificationTimeout" format="integer" />
@@ -3498,6 +3509,8 @@
<attr name="canCaptureFingerprintGestures" format="boolean" />
<!-- Short description of the accessibility service purpose or behavior.-->
<attr name="description" />
+ <!-- Brief summary of the accessibility service purpose or behavior. -->
+ <attr name="summary" />
</declare-styleable>
<!-- Use <code>print-service</code> as the root tag of the XML resource that
@@ -8565,6 +8578,7 @@
<!-- Attributes that are read when parsing a <fontfamily> tag. -->
<declare-styleable name="FontFamily">
<attr name="fontProviderAuthority" format="string" />
+ <attr name="fontProviderPackage" format="string" />
<attr name="fontProviderQuery" format="string" />
</declare-styleable>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index deacc24be158..bfe666eed6e9 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1891,6 +1891,7 @@
<attr name="path" />
<attr name="pathPrefix" />
<attr name="pathPattern" />
+ <attr name="pathAdvancedPattern" />
<attr name="permission" />
<attr name="readPermission" />
<attr name="writePermission" />
@@ -2280,6 +2281,17 @@
"\\\\". This is basically the same as what you would need to
write if constructing the string in Java code. -->
<attr name="pathPattern" />
+ <!-- Specify a URI path that matches an advanced pattern, as per
+ {@link android.content.IntentFilter#addDataPath
+ IntentFilter.addDataPath()} with
+ {@link android.os.PatternMatcher#PATTERN_ADVANCED_GLOB}.
+ Note that because '\' is used as an escape character when
+ reading the string from XML (before it is parsed as a pattern),
+ you will need to double-escape: for example a literal "*" would
+ be written as "\\*" and a literal "\" would be written as
+ "\\\\". This is basically the same as what you would need to
+ write if constructing the string in Java code. -->
+ <attr name="pathAdvancedPattern" />
</declare-styleable>
<!-- Attributes that can be supplied in an AndroidManifest.xml
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ab2e090b2869..a3a0c830a0cf 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2545,9 +2545,17 @@
These values are in DPs and will be converted to pixel sizes internally. -->
<string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string>
- <!-- Max default size [WIDTHxHEIGHT] on screen for picture-in-picture windows to fit inside.
- These values are in DPs and will be converted to pixel sizes internally. -->
- <string translatable="false" name="config_defaultPictureInPictureSize">192x120</string>
+ <!-- The percentage of the screen width to use for the default width or height of
+ picture-in-picture windows. Regardless of the percent set here, calculated size will never
+ be smaller than @dimen/default_minimal_size_pip_resizable_task. -->
+ <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.23</item>
+
+ <!-- The default aspect ratio for picture-in-picture windows. -->
+ <item name="config_pictureInPictureDefaultAspectRatio" format="float" type="dimen">1.777778</item>
+
+ <!-- This is the limit for the max and min aspect ratio (1 / this value) at which the min size
+ will be used instead of an adaptive size based loosely on area. -->
+ <item name="config_pictureInPictureAspectRatioLimitForMinSize" format="float" type="dimen">1.777778</item>
<!-- The default gravity for the picture-in-picture window.
Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 0ab17842ebb7..d0127a3a6cd6 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -486,6 +486,9 @@
<!-- The default minimal size of a resizable task, in both dimensions. -->
<dimen name="default_minimal_size_resizable_task">220dp</dimen>
+ <!-- The default minimal size of a PiP task, in both dimensions. -->
+ <dimen name="default_minimal_size_pip_resizable_task">108dp</dimen>
+
<!-- Height of a task when in minimized mode from the top when launcher is resizable. -->
<dimen name="task_height_of_minimized_mode">80dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index df3962c5dd5c..b664448ad712 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2760,8 +2760,9 @@
=============================================================== -->
<eat-comment />
- <public-group type="attr" first-id="0x01010531">
- <public name="fontStyle" />
+ <public type="attr" name="visibleToInstantApps" id="0x01010531" />
+
+ <public-group type="attr" first-id="0x01010532">
<public name="font" />
<public name="fontWeight" />
<public name="tooltipText" />
@@ -2775,7 +2776,7 @@
<public name="layout_marginVertical" />
<public name="paddingHorizontal" />
<public name="paddingVertical" />
- <public name="visibleToInstantApps" />
+ <public name="fontStyle" />
<public name="keyboardNavigationCluster" />
<public name="targetProcess" />
<public name="nextClusterForward" />
@@ -2802,6 +2803,8 @@
<public name="requiredFeature" />
<public name="requiredNotFeature" />
<public name="autoFillHint" />
+ <public name="fontProviderPackage" />
+ <public name="importantForAutofill" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ea0666413f88..bd8d5724ac50 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4382,8 +4382,11 @@
<!-- Content description of the work profile icon in the notification. -->
<string name="notification_work_profile_content_description">Work profile</string>
- <!-- Content description of the expand button icon in the notification.-->
- <string name="expand_button_content_description">Expand button</string>
+ <!-- Content description of the expand button icon in the notification when collaped.-->
+ <string name="expand_button_content_description_collapsed">Expand</string>
+
+ <!-- Content description of the expand button icon in the notification when expanded.-->
+ <string name="expand_button_content_description_expanded">Collapse</string>
<!-- Accessibility action description on the expand button. -->
<string name="expand_action_accessibility">toggle expansion</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3ba6a274ee8a..32babab658bb 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -318,7 +318,9 @@
<java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" />
<java-symbol type="bool" name="config_enableAppWidgetService" />
<java-symbol type="string" name="config_defaultPictureInPictureScreenEdgeInsets" />
- <java-symbol type="string" name="config_defaultPictureInPictureSize" />
+ <java-symbol type="dimen" name="config_pictureInPictureDefaultSizePercent" />
+ <java-symbol type="dimen" name="config_pictureInPictureDefaultAspectRatio" />
+ <java-symbol type="dimen" name="config_pictureInPictureAspectRatioLimitForMinSize" />
<java-symbol type="integer" name="config_defaultPictureInPictureGravity" />
<java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" />
<java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" />
@@ -1777,6 +1779,7 @@
<java-symbol type="id" name="replace_message" />
<java-symbol type="fraction" name="config_dimBehindFadeDuration" />
<java-symbol type="dimen" name="default_minimal_size_resizable_task" />
+ <java-symbol type="dimen" name="default_minimal_size_pip_resizable_task" />
<java-symbol type="dimen" name="task_height_of_minimized_mode" />
<java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" />
<java-symbol type="fraction" name="config_autoBrightnessAdjustmentMaxGamma" />
@@ -2885,6 +2888,9 @@
<java-symbol type="style" name="Widget.LockPatternView" />
<java-symbol type="attr" name="lockPatternStyle" />
+ <java-symbol type="string" name="expand_button_content_description_collapsed" />
+ <java-symbol type="string" name="expand_button_content_description_expanded" />
+
<!-- Colon separated list of package names that should be granted Notification Listener access -->
<java-symbol type="string" name="config_defaultListenerAccessPackages" />
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index ef09c35d1201..1a16b3be41a0 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -29,7 +29,6 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
frameworks-core-util-lib \
mockwebserver \
guava \
- littlemock \
android-support-test \
mockito-target-minus-junit4 \
espresso-core \
diff --git a/core/tests/coretests/res/font/samplexmldownloadedfont.xml b/core/tests/coretests/res/font/samplexmldownloadedfont.xml
index 35391bd598b3..f1bdc473b9d2 100644
--- a/core/tests/coretests/res/font/samplexmldownloadedfont.xml
+++ b/core/tests/coretests/res/font/samplexmldownloadedfont.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android"
- android:fontProviderAuthority="com.example.test.fontprovider"
+ android:fontProviderAuthority="com.example.test.fontprovider.authority"
+ android:fontProviderPackage="com.example.test.fontprovider.package"
android:fontProviderQuery="MyRequestedFont">
</font-family> \ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 8b536a7a90b5..23d3aa5a64ab 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -91,7 +91,8 @@ public class FontResourcesParserTest {
List<FontConfig.Family> families = result.getFamilies();
assertEquals(1, families.size());
FontConfig.Family family = families.get(0);
- assertEquals("com.example.test.fontprovider", family.getProviderAuthority());
+ assertEquals("com.example.test.fontprovider.authority", family.getProviderAuthority());
+ assertEquals("com.example.test.fontprovider.package", family.getProviderPackage());
assertEquals("MyRequestedFont", family.getQuery());
assertNull(family.getFonts());
}
diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
new file mode 100644
index 000000000000..5ade66e59776
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
@@ -0,0 +1,193 @@
+package android.graphics.drawable;
+
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Path.Direction;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Arrays;
+import org.junit.Test;
+
+public class AdaptiveIconDrawableTest extends AndroidTestCase {
+
+ public static final String TAG = AdaptiveIconDrawableTest.class.getSimpleName();
+ public static void L(String s, Object... parts) {
+ Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts));
+ }
+ private Drawable mBackgroundDrawable;
+ private Drawable mForegroundDrawable;
+ private AdaptiveIconDrawable mIconDrawable;
+ private File mDir;
+
+ /**
+ * When setBound isn't called before draw method is called.
+ * Nothing is drawn.
+ */
+ @Test
+ public void testDrawWithoutSetBounds() throws Exception {
+ mBackgroundDrawable = new ColorDrawable(Color.BLUE);
+ mForegroundDrawable = new ColorDrawable(Color.RED);
+ mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable);
+ mDir = getContext().getExternalFilesDir(null);
+ L("writing temp bitmaps to %s...", mDir);
+
+ final Bitmap bm_test = Bitmap.createBitmap(150, 150, Bitmap.Config.ARGB_8888);
+ final Bitmap bm_org = bm_test.copy(Config.ARGB_8888, false);
+ final Canvas can1 = new Canvas(bm_test);
+
+ // Even when setBounds is not called, should not crash
+ mIconDrawable.draw(can1);
+ // Draws nothing! Hence same as original.
+ if (!equalBitmaps(bm_test, bm_org)) {
+ findBitmapDifferences(bm_test, bm_org);
+ fail("bm differs, check " + mDir);
+ }
+ }
+
+ /**
+ * When setBound is called, translate accordingly.
+ */
+ @Test
+ public void testDrawSetBounds() throws Exception {
+ int dpi = 4 ;
+ int top = 18 * dpi;
+ int left = 18 * dpi;
+ int right = 90 * dpi;
+ int bottom = 90 * dpi;
+ int width = right - left;
+ int height = bottom - top;
+
+ mIconDrawable = (AdaptiveIconDrawable) getContext().getResources().getDrawable(android.R.drawable.sym_def_app_icon);
+ mDir = getContext().getExternalFilesDir(null);
+ L("writing temp bitmaps to %s...", mDir);
+ final Bitmap bm_org = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final Canvas can_org = new Canvas(bm_org);
+ mIconDrawable.setBounds(0, 0, width, height);
+ mIconDrawable.draw(can_org);
+
+ // Tested bitmap is drawn from the adaptive icon drawable.
+ final Bitmap bm_test = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final Canvas can_test = new Canvas(bm_test);
+
+ mIconDrawable.setBounds(left, top, right, bottom);
+ can_test.translate(-left, -top);
+ mIconDrawable.draw(can_test);
+ can_test.translate(left, top);
+
+
+ bm_org.compress(Bitmap.CompressFormat.PNG, 100,
+ new FileOutputStream(new File(mDir, "adaptive-bm-original.png")));
+ bm_test.compress(Bitmap.CompressFormat.PNG, 100,
+ new FileOutputStream(new File(mDir, "adaptive-bm-test.png")));
+ Region region = new Region(new Rect(0, 0, width, height));
+
+ Path circle = new Path();
+ circle.addCircle(width / 2, height / 2, (right - left)/2 -10 /* room for anti-alias */, Direction.CW);
+
+ region.setPath(circle, region);
+ if (!equalBitmaps(bm_test, bm_org, region)) {
+ findBitmapDifferences(bm_test, bm_org);
+ fail("bm differs, check " + mDir);
+ }
+ }
+
+ //
+ // Utils
+ //
+
+ boolean equalBitmaps(Bitmap a, Bitmap b) {
+ return equalBitmaps(a, b, null);
+ }
+
+ boolean equalBitmaps(Bitmap a, Bitmap b, Region region) {
+ if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) return false;
+
+ final int w = a.getWidth();
+ final int h = a.getHeight();
+ int[] aPix = new int[w * h];
+ int[] bPix = new int[w * h];
+
+ if (region != null) {
+ for (int i = 0; i < w; i++) {
+ for (int j = 0; j < h; j++) {
+ int ra = (a.getPixel(i, j) >> 16) & 0xff;
+ int ga = (a.getPixel(i, j) >> 8) & 0xff;
+ int ba = a.getPixel(i, j) & 0xff;
+ int rb = (b.getPixel(i, j) >> 16) & 0xff;
+ int gb = (b.getPixel(i, j) >> 8) & 0xff;
+ int bb = b.getPixel(i, j) & 0xff;
+ if (region.contains(i, j) && a.getPixel(i, j) != b.getPixel(i, j) ) {
+ return false;
+ }
+ }
+ }
+ return true;
+ } else {
+ a.getPixels(aPix, 0, w, 0, 0, w, h);
+ b.getPixels(bPix, 0, w, 0, 0, w, h);
+ return Arrays.equals(aPix, bPix);
+ }
+ }
+
+ void findBitmapDifferences(Bitmap a, Bitmap b) {
+ if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
+ L("different sizes: %dx%d vs %dx%d",
+ a.getWidth(), a.getHeight(), b.getWidth(), b.getHeight());
+ return;
+ }
+
+ final int w = a.getWidth();
+ final int h = a.getHeight();
+ int[] aPix = new int[w * h];
+ int[] bPix = new int[w * h];
+
+ a.getPixels(aPix, 0, w, 0, 0, w, h);
+ b.getPixels(bPix, 0, w, 0, 0, w, h);
+
+ L("bitmap a (%dx%d)", w, h);
+ printBits(aPix, w, h);
+ L("bitmap b (%dx%d)", w, h);
+ printBits(bPix, w, h);
+
+ StringBuffer sb = new StringBuffer("Different pixels: ");
+ for (int i=0; i<w; i++) {
+ for (int j=0; j<h; j++) {
+ if (aPix[i+w*j] != bPix[i+w*j]) {
+ sb.append(" ").append(i).append(",").append(j).append("<")
+ .append(aPix[i+w*j]).append(",").append(bPix[i+w*j]).append(">");
+ }
+ }
+ }
+ L(sb.toString());
+ }
+
+ static void printBits(int[] a, int w, int h) {
+ final StringBuilder sb = new StringBuilder();
+ for (int i=0; i<w; i++) {
+ for (int j=0; j<h; j++) {
+ sb.append(colorToChar(a[i+w*j]));
+ }
+ sb.append('\n');
+ }
+ L(sb.toString());
+ }
+
+ static char colorToChar(int color) {
+ int sum = ((color >> 16) & 0xff)
+ + ((color >> 8) & 0xff)
+ + ((color) & 0xff);
+ return GRADIENT[sum * (GRADIENT.length-1) / (3*0xff)];
+ }
+ static final char[] GRADIENT = " .:;+=xX$#".toCharArray();
+}
diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java
index b9c973fb09cd..ece44bebf9ba 100644
--- a/core/tests/coretests/src/android/metrics/LogMakerTest.java
+++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java
@@ -122,6 +122,55 @@ public class LogMakerTest extends TestCase {
assertEquals(null, builder.getTaggedData(1));
}
+ public void testClearFieldLeavesOtherFieldsIntact() {
+ LogMaker builder = new LogMaker(0);
+ builder.setPackageName("package.name");
+ builder.setSubtype(10);
+ builder.clearPackageName();
+ assertEquals(null, builder.getPackageName());
+ assertEquals(10, builder.getSubtype());
+ }
+
+ public void testSetAndClearCategory() {
+ LogMaker builder = new LogMaker(0);
+ builder.setCategory(MetricsEvent.MAIN_SETTINGS);
+ assertEquals(MetricsEvent.MAIN_SETTINGS, builder.getCategory());
+ builder.clearCategory();
+ assertEquals(MetricsEvent.VIEW_UNKNOWN, builder.getCategory());
+ }
+
+ public void testSetAndClearType() {
+ LogMaker builder = new LogMaker(0);
+ builder.setType(MetricsEvent.TYPE_OPEN);
+ assertEquals(MetricsEvent.TYPE_OPEN, builder.getType());
+ builder.clearType();
+ assertEquals(MetricsEvent.TYPE_UNKNOWN, builder.getType());
+ }
+
+ public void testSetAndClearSubtype() {
+ LogMaker builder = new LogMaker(0);
+ builder.setSubtype(1);
+ assertEquals(1, builder.getSubtype());
+ builder.clearSubtype();
+ assertEquals(0, builder.getSubtype());
+ }
+
+ public void testSetAndClearTimestamp() {
+ LogMaker builder = new LogMaker(0);
+ builder.setTimestamp(1);
+ assertEquals(1, builder.getTimestamp());
+ builder.clearTimestamp();
+ assertEquals(0, builder.getTimestamp());
+ }
+
+ public void testSetAndClearPackageName() {
+ LogMaker builder = new LogMaker(0);
+ builder.setPackageName("package.name");
+ assertEquals("package.name", builder.getPackageName());
+ builder.clearPackageName();
+ assertEquals(null, builder.getPackageName());
+ }
+
public void testGiantLogOmitted() {
LogMaker badBuilder = new LogMaker(0);
StringBuilder b = new StringBuilder();
diff --git a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
index be193032d703..ec130e049676 100644
--- a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
+++ b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
@@ -19,7 +19,7 @@ package android.net;
import com.android.org.conscrypt.ClientSessionContext;
import com.android.org.conscrypt.SSLClientSessionCache;
-import com.google.testing.littlemock.LittleMock;
+import org.mockito.Mockito;
import junit.framework.TestCase;
@@ -39,25 +39,25 @@ public class SSLSessionCacheTest extends TestCase {
public void testInstall_compatibleContext() throws Exception {
final SSLContext ctx = SSLContext.getDefault();
- final SSLClientSessionCache mock = LittleMock.mock(SSLClientSessionCache.class);
+ final SSLClientSessionCache mock = Mockito.mock(SSLClientSessionCache.class);
final ClientSessionContext clientCtx = (ClientSessionContext) ctx.getClientSessionContext();
try {
SSLSessionCache.install(new SSLSessionCache(mock), ctx);
clientCtx.getSession("www.foogle.com", 443);
- LittleMock.verify(mock).getSessionData(LittleMock.anyString(), LittleMock.anyInt());
+ Mockito.verify(mock).getSessionData(Mockito.anyString(), Mockito.anyInt());
} finally {
// Restore cacheless behaviour.
SSLSessionCache.install(null, ctx);
clientCtx.getSession("www.foogle.com", 443);
- LittleMock.verifyNoMoreInteractions(mock);
+ Mockito.verifyNoMoreInteractions(mock);
}
}
public void testInstall_incompatibleContext() {
try {
SSLSessionCache.install(
- new SSLSessionCache(LittleMock.mock(SSLClientSessionCache.class)),
+ new SSLSessionCache(Mockito.mock(SSLClientSessionCache.class)),
new FakeSSLContext());
fail();
} catch (IllegalArgumentException expected) {}
@@ -102,7 +102,7 @@ public class SSLSessionCacheTest extends TestCase {
@Override
protected SSLSessionContext engineGetClientSessionContext() {
- return LittleMock.mock(SSLSessionContext.class);
+ return Mockito.mock(SSLSessionContext.class);
}
}
}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 5c7da7099f32..3a751afaec7b 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -16,6 +16,7 @@
package android.os;
+import static android.os.FileUtils.roundStorageSize;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
@@ -25,10 +26,10 @@ import android.provider.DocumentsContract.Document;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
-import com.google.android.collect.Sets;
-
import libcore.io.IoUtils;
+import com.google.android.collect.Sets;
+
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -312,25 +313,31 @@ public class FileUtilsTest extends AndroidTestCase {
}
public void testRoundStorageSize() throws Exception {
- final long M128 = 134217728L;
- final long M256 = M128 * 2;
- final long M512 = M256 * 2;
- final long M1024 = M512 * 2;
- final long G16 = M1024 * 16;
- final long G32 = M1024 * 32;
- final long G64 = M1024 * 64;
-
- assertEquals(M128, FileUtils.roundStorageSize(M128));
- assertEquals(M256, FileUtils.roundStorageSize(M128 + 1));
- assertEquals(M256, FileUtils.roundStorageSize(M256 - 1));
- assertEquals(M256, FileUtils.roundStorageSize(M256));
- assertEquals(M512, FileUtils.roundStorageSize(M256 + 1));
-
- assertEquals(G16, FileUtils.roundStorageSize(G16));
- assertEquals(G32, FileUtils.roundStorageSize(G16 + 1));
- assertEquals(G32, FileUtils.roundStorageSize(G32 - 1));
- assertEquals(G32, FileUtils.roundStorageSize(G32));
- assertEquals(G64, FileUtils.roundStorageSize(G32 + 1));
+ final long M128 = 128000000L;
+ final long M256 = 256000000L;
+ final long M512 = 512000000L;
+ final long G1 = 1000000000L;
+ final long G2 = 2000000000L;
+ final long G16 = 16000000000L;
+ final long G32 = 32000000000L;
+ final long G64 = 64000000000L;
+
+ assertEquals(M128, roundStorageSize(M128));
+ assertEquals(M256, roundStorageSize(M128 + 1));
+ assertEquals(M256, roundStorageSize(M256 - 1));
+ assertEquals(M256, roundStorageSize(M256));
+ assertEquals(M512, roundStorageSize(M256 + 1));
+ assertEquals(M512, roundStorageSize(M512 - 1));
+ assertEquals(M512, roundStorageSize(M512));
+ assertEquals(G1, roundStorageSize(M512 + 1));
+ assertEquals(G1, roundStorageSize(G1));
+ assertEquals(G2, roundStorageSize(G1 + 1));
+
+ assertEquals(G16, roundStorageSize(G16));
+ assertEquals(G32, roundStorageSize(G16 + 1));
+ assertEquals(G32, roundStorageSize(G32 - 1));
+ assertEquals(G32, roundStorageSize(G32));
+ assertEquals(G64, roundStorageSize(G32 + 1));
}
private static void assertNameEquals(String expected, File actual) {
diff --git a/core/tests/coretests/src/android/provider/FontsContractTest.java b/core/tests/coretests/src/android/provider/FontsContractTest.java
new file mode 100644
index 000000000000..db623a4a2044
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/FontsContractTest.java
@@ -0,0 +1,235 @@
+/*
+ * 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 android.provider;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.Signature;
+import android.graphics.Typeface;
+import android.graphics.fonts.FontRequest;
+import android.graphics.fonts.FontResult;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.support.test.filters.SmallTest;
+import android.test.ProviderTestCase2;
+import android.util.Base64;
+
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link FontsContract}.
+ */
+@SmallTest
+public class FontsContractTest extends ProviderTestCase2<TestFontsProvider> {
+ private static final byte[] BYTE_ARRAY =
+ Base64.decode("e04fd020ea3a6910a2d808002b30", Base64.DEFAULT);
+ private static final String PACKAGE_NAME = "com.my.font.provider.package";
+
+ private final FontRequest request = new FontRequest(
+ TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query");
+ private TestFontsProvider mProvider;
+ private FontsContract mContract;
+ private ResultReceiver mResultReceiver;
+ private PackageManager mPackageManager;
+
+ public FontsContractTest() {
+ super(TestFontsProvider.class, TestFontsProvider.AUTHORITY);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mProvider = getProvider();
+ mPackageManager = mock(PackageManager.class);
+ mContract = new FontsContract(getMockContext(), mPackageManager);
+ mResultReceiver = mock(ResultReceiver.class);
+ }
+
+ public void testGetFontFromProvider() {
+ mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY);
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mResultReceiver).send(eq(FontsContract.RESULT_CODE_OK), bundleCaptor.capture());
+
+ Bundle bundle = bundleCaptor.getValue();
+ assertNotNull(bundle);
+ List<FontResult> resultList =
+ bundle.getParcelableArrayList(FontsContract.PARCEL_FONT_RESULTS);
+ assertNotNull(resultList);
+ assertEquals(1, resultList.size());
+ FontResult fontResult = resultList.get(0);
+ assertEquals(TestFontsProvider.TTC_INDEX, fontResult.getTtcIndex());
+ assertEquals(TestFontsProvider.VARIATION_SETTINGS, fontResult.getFontVariationSettings());
+ assertEquals(TestFontsProvider.STYLE, fontResult.getStyle());
+ assertNotNull(fontResult.getFileDescriptor());
+ }
+
+ public void testGetFontFromProvider_providerDoesntReturnAllFields() {
+ mProvider.setReturnAllFields(false);
+
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ mContract.getFontFromProvider(request, mResultReceiver, TestFontsProvider.AUTHORITY);
+ verify(mResultReceiver).send(eq(FontsContract.RESULT_CODE_OK), bundleCaptor.capture());
+
+ Bundle bundle = bundleCaptor.getValue();
+ assertNotNull(bundle);
+ List<FontResult> resultList =
+ bundle.getParcelableArrayList(FontsContract.PARCEL_FONT_RESULTS);
+ assertNotNull(resultList);
+ assertEquals(1, resultList.size());
+ FontResult fontResult = resultList.get(0);
+ assertEquals(0, fontResult.getTtcIndex());
+ assertNull(fontResult.getFontVariationSettings());
+ assertEquals(Typeface.NORMAL, fontResult.getStyle());
+ assertNotNull(fontResult.getFileDescriptor());
+ }
+
+ public void testGetProvider_providerNotFound() {
+ when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(null);
+
+ ProviderInfo result = mContract.getProvider(request);
+
+ assertNull(result);
+ }
+
+ public void testGetProvider_providerIsSystemApp() throws PackageManager.NameNotFoundException {
+ ProviderInfo info = setupPackageManager();
+ info.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+ when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info);
+
+ ProviderInfo result = mContract.getProvider(request);
+
+ assertEquals(info, result);
+ }
+
+ public void testGetProvider_providerIsSystemAppWrongPackage()
+ throws PackageManager.NameNotFoundException {
+ ProviderInfo info = setupPackageManager();
+ info.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+ when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info);
+
+ ProviderInfo result = mContract.getProvider(
+ new FontRequest(TestFontsProvider.AUTHORITY, "com.wrong.package", "query"));
+
+ assertNull(result);
+ }
+
+ public void testGetProvider_providerIsNonSystemAppNoCerts()
+ throws PackageManager.NameNotFoundException {
+ setupPackageManager();
+
+ // The default request is missing the certificates info.
+ ProviderInfo result = mContract.getProvider(request);
+
+ assertNull(result);
+ }
+
+ public void testGetProvider_providerIsNonSystemAppWrongCerts()
+ throws PackageManager.NameNotFoundException {
+ setupPackageManager();
+
+ byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT);
+ List<byte[]> certList = Arrays.asList(wrongCert);
+ FontRequest requestWrongCerts = new FontRequest(
+ TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList));
+ ProviderInfo result = mContract.getProvider(requestWrongCerts);
+
+ assertNull(result);
+ }
+
+ public void testGetProvider_providerIsNonSystemAppCorrectCerts()
+ throws PackageManager.NameNotFoundException {
+ ProviderInfo info = setupPackageManager();
+
+ List<byte[]> certList = Arrays.asList(BYTE_ARRAY);
+ FontRequest requestRightCerts = new FontRequest(
+ TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList));
+ ProviderInfo result = mContract.getProvider(requestRightCerts);
+
+ assertEquals(info, result);
+ }
+
+ public void testGetProvider_providerIsNonSystemAppMoreCerts()
+ throws PackageManager.NameNotFoundException {
+ setupPackageManager();
+
+ byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT);
+ List<byte[]> certList = Arrays.asList(wrongCert, BYTE_ARRAY);
+ FontRequest requestRightCerts = new FontRequest(
+ TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", Arrays.asList(certList));
+ ProviderInfo result = mContract.getProvider(requestRightCerts);
+
+ // There is one too many certs, should fail as the set doesn't match.
+ assertNull(result);
+ }
+
+ public void testGetProvider_providerIsNonSystemAppCorrectCertsSeveralSets()
+ throws PackageManager.NameNotFoundException {
+ ProviderInfo info = setupPackageManager();
+
+ List<List<byte[]>> certList = new ArrayList<>();
+ byte[] wrongCert = Base64.decode("this is a wrong cert", Base64.DEFAULT);
+ certList.add(Arrays.asList(wrongCert));
+ certList.add(Arrays.asList(BYTE_ARRAY));
+ FontRequest requestRightCerts = new FontRequest(
+ TestFontsProvider.AUTHORITY, PACKAGE_NAME, "query", certList);
+ ProviderInfo result = mContract.getProvider(requestRightCerts);
+
+ assertEquals(info, result);
+ }
+
+ public void testGetProvider_providerIsNonSystemAppWrongPackage()
+ throws PackageManager.NameNotFoundException {
+ setupPackageManager();
+
+ List<List<byte[]>> certList = new ArrayList<>();
+ certList.add(Arrays.asList(BYTE_ARRAY));
+ FontRequest requestRightCerts = new FontRequest(
+ TestFontsProvider.AUTHORITY, "com.wrong.package.name", "query", certList);
+ ProviderInfo result = mContract.getProvider(requestRightCerts);
+
+ assertNull(result);
+ }
+
+ private ProviderInfo setupPackageManager()
+ throws PackageManager.NameNotFoundException {
+ ProviderInfo info = new ProviderInfo();
+ info.packageName = PACKAGE_NAME;
+ info.applicationInfo = new ApplicationInfo();
+ when(mPackageManager.resolveContentProvider(anyString(), anyInt())).thenReturn(info);
+ PackageInfo packageInfo = new PackageInfo();
+ Signature signature = mock(Signature.class);
+ when(signature.toByteArray()).thenReturn(BYTE_ARRAY);
+ packageInfo.packageName = PACKAGE_NAME;
+ packageInfo.signatures = new Signature[] { signature };
+ when(mPackageManager.getPackageInfo(anyString(), anyInt())).thenReturn(packageInfo);
+ return info;
+ }
+}
diff --git a/core/tests/coretests/src/android/provider/TestFontsProvider.java b/core/tests/coretests/src/android/provider/TestFontsProvider.java
new file mode 100644
index 000000000000..6d40f37af548
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/TestFontsProvider.java
@@ -0,0 +1,117 @@
+/*
+ * 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 android.provider;
+
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Provides a test Content Provider implementing {@link FontsContract}.
+ */
+public class TestFontsProvider extends ContentProvider {
+ static final String AUTHORITY = "android.provider.TestFontsProvider";
+ static final int TTC_INDEX = 2;
+ static final String VARIATION_SETTINGS = "'wdth' 1";
+ static final int STYLE = Typeface.BOLD;
+
+ private ParcelFileDescriptor mPfd;
+ private boolean mReturnAllFields = true;
+
+ /**
+ * Used by tests to switch whether all fields should be returned or not.
+ */
+ void setReturnAllFields(boolean returnAllFields) {
+ mReturnAllFields = returnAllFields;
+ }
+
+ @Override
+ public boolean onCreate() {
+ mPfd = createFontFile();
+ return true;
+ }
+
+ @Override
+ public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
+ @Nullable String[] selectionArgs, @Nullable String sortOrder) {
+ MatrixCursor cursor;
+ if (mReturnAllFields) {
+ cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID,
+ FontsContract.Columns.TTC_INDEX, FontsContract.Columns.VARIATION_SETTINGS,
+ FontsContract.Columns.STYLE });
+ cursor.addRow(new Object[] { 1, TTC_INDEX, VARIATION_SETTINGS, STYLE });
+ } else {
+ cursor = new MatrixCursor(new String[] { FontsContract.Columns._ID });
+ cursor.addRow(new Object[] { 1 });
+ }
+ return cursor;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) {
+ try {
+ return mPfd.dup();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ public String getType(@NonNull Uri uri) {
+ return "application/x-font-ttf";
+ }
+
+ @Override
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(@NonNull Uri uri, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ return 0;
+ }
+
+ private ParcelFileDescriptor createFontFile() {
+ try {
+ final File file = new File(getContext().getCacheDir(), "font.ttf");
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ return ParcelFileDescriptor.open(file, MODE_READ_ONLY);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+}
diff --git a/core/tests/coretests/src/android/view/PopupWindowVisibility.java b/core/tests/coretests/src/android/view/PopupWindowVisibility.java
index 7eb0468caa2a..6e11ede201db 100644
--- a/core/tests/coretests/src/android/view/PopupWindowVisibility.java
+++ b/core/tests/coretests/src/android/view/PopupWindowVisibility.java
@@ -82,7 +82,7 @@ public class PopupWindowVisibility extends Activity implements OnClickListener {
"Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index f2eba2335808..cdfa21755344 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -16,8 +16,8 @@
package android.widget.focus;
-import static com.google.testing.littlemock.LittleMock.inOrder;
-import static com.google.testing.littlemock.LittleMock.mock;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
import android.os.Handler;
import android.test.ActivityInstrumentationTestCase2;
@@ -31,7 +31,7 @@ import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
import android.widget.Button;
import com.android.frameworks.coretests.R;
-import com.google.testing.littlemock.LittleMock.InOrder;
+import org.mockito.InOrder;
/**
* {@link RequestFocusTest} is set up to exercise cases where the views that
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java
deleted file mode 100644
index 4adf62991d66..000000000000
--- a/core/tests/coretests/src/com/android/internal/logging/legacy/ParserTest.java
+++ /dev/null
@@ -1,111 +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.logging.legacy;
-
-import android.metrics.LogMaker;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.when;
-
-import junit.framework.TestCase;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.OngoingStubbing;
-
-/**
- * Common functions and temporaries for parser tests.
- */
-public class ParserTest extends TestCase {
- @Mock
- protected TronLogger mLogger;
-
- protected TagParser mParser;
-
- protected LogMaker[] mProto;
- protected ArgumentCaptor<LogMaker> mProtoCaptor;
- protected ArgumentCaptor<String> mNameCaptor;
- protected ArgumentCaptor<Integer> mCountCaptor;
- protected String mKey = "0|com.android.example.notificationshowcase|31338|null|10090";
- protected String mTaggedKey = "0|com.android.example.notificationshowcase|31338|badger|10090";
- protected String mKeyPackage = "com.android.example.notificationshowcase";
- protected String mTag = "badger";
- protected int mId = 31338;
- protected int mSinceCreationMillis = 5000;
- protected int mSinceUpdateMillis = 1012;
- protected int mSinceVisibleMillis = 323;
-
-
- public ParserTest() {
- mProto = new LogMaker[5];
- for (int i = 0; i < mProto.length; i++) {
- mProto[i] = new LogMaker(MetricsEvent.VIEW_UNKNOWN);
- }
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- MockitoAnnotations.initMocks(this);
-
- mProtoCaptor = ArgumentCaptor.forClass(LogMaker.class);
- mNameCaptor = ArgumentCaptor.forClass(String.class);
- mCountCaptor = ArgumentCaptor.forClass(Integer.class);
-
- OngoingStubbing<LogMaker> stub = when(mLogger.obtain()).thenReturn(mProto[0]);
- for (int i = 1; i < mProto.length; i++) {
- stub.thenReturn(mProto[i]);
- }
- doNothing().when(mLogger).addEvent(any(LogMaker.class));
- doNothing().when(mLogger).incrementBy(anyString(), anyInt());
- }
-
- protected void validateNotificationTimes(LogMaker proto, int life, int freshness,
- int exposure) {
- validateNotificationTimes(proto, life, freshness);
- if (exposure != 0) {
- assertEquals(exposure,
- proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS));
- } else {
- assertNull(proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_VISIBLE_MILLIS));
- }
- }
-
- protected void validateNotificationTimes(LogMaker proto, int life, int freshness) {
- if (life != 0) {
- assertEquals(life,
- proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS));
- } else {
- assertNull(proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS));
- }
- if (freshness != 0) {
- assertEquals(freshness,
- proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS));
- } else {
- assertNull(proto.getTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS));
- }
- }
-
- protected void validateNotificationIdAndTag(LogMaker proto, int id, String tag) {
- assertEquals(tag, proto.getTaggedData(MetricsEvent.NOTIFICATION_TAG));
- assertEquals(id, proto.getTaggedData(MetricsEvent.NOTIFICATION_ID));
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java
deleted file mode 100644
index b480e61e1ac1..000000000000
--- a/core/tests/coretests/src/com/android/internal/logging/legacy/PowerScreenStateParserTest.java
+++ /dev/null
@@ -1,69 +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.logging.legacy;
-
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-
-import android.metrics.LogMaker;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-public class PowerScreenStateParserTest extends ParserTest {
-
- public PowerScreenStateParserTest() {
- mParser = new PowerScreenStateParser();
- }
-
- public void testScreenOn() throws Throwable {
- validate(MetricsEvent.TYPE_OPEN, 0, "1,0,0,0");
- }
-
- public void testTimeout() throws Throwable {
- validate(MetricsEvent.TYPE_CLOSE, 3, "0,3,0,0");
- }
-
- public void testUser() throws Throwable {
- validate(MetricsEvent.TYPE_CLOSE, 2, "0,2,0,0");
- }
-
- public void testAdmin() throws Throwable {
- validate(MetricsEvent.TYPE_CLOSE, 1, "0,1,0,0");
- }
-
- public void testIgnoreUnexpectedData() throws Throwable {
- validate(MetricsEvent.TYPE_OPEN, 0, "1,0,0,0,5");
- }
-
- private void validate(int type, int subType, String log) {
- String[] parts = log.split(",");
- int t = 1000;
- Object[] objects = new Object[parts.length];
- for (int i = 0; i < parts.length; i++) {
- objects[i] = Integer.valueOf(parts[i]);
- }
-
- mParser.parseEvent(mLogger, t, objects);
-
- verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
-
- LogMaker proto = mProtoCaptor.getValue();
- assertEquals(t, proto.getTimestamp());
- assertEquals(type, proto.getType());
- assertEquals(MetricsEvent.SCREEN, proto.getCategory());
- assertEquals(subType, proto.getSubtype());
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java b/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java
deleted file mode 100644
index e7a05d88f430..000000000000
--- a/core/tests/coretests/src/com/android/internal/logging/legacy/SysuiMultiActionParserTest.java
+++ /dev/null
@@ -1,67 +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.logging.legacy;
-
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-
-import android.metrics.LogMaker;
-
-public class SysuiMultiActionParserTest extends ParserTest {
-
- public SysuiMultiActionParserTest() {
- mParser = new SysuiMultiActionParser();
- }
-
- public void testParseAllFields() {
- int category = 10;
- int type = 11;
- int subtype = 12;
- long timestamp = 1484669007890L;
- String packageName = "com.foo.bar";
- String counterName = "sheep";
- int bucket = 13;
- int value = 14;
- LogMaker builder = new LogMaker(category);
- builder.setType(type);
- builder.setSubtype(subtype);
- builder.setPackageName(packageName);
- builder.setCounterName(counterName);
- builder.setCounterBucket(bucket);
- builder.setCounterValue(value);
- builder.addTaggedData(1, "one");
- builder.addTaggedData(2, "two");
- Object[] out = builder.serialize();
- int t = 1000;
-
- mParser.parseEvent(mLogger, timestamp, out);
-
- verify(mLogger, times(1)).addEvent(mProtoCaptor.capture());
-
- LogMaker proto = mProtoCaptor.getValue();
- assertEquals(category, proto.getCategory());
- assertEquals(type, proto.getType());
- assertEquals(subtype, proto.getSubtype());
- assertEquals(timestamp, proto.getTimestamp());
- assertEquals(packageName, proto.getPackageName());
- assertEquals(counterName, proto.getCounterName());
- assertEquals(bucket, proto.getCounterBucket());
- assertEquals(value, proto.getCounterValue());
- assertEquals("one", proto.getTaggedData(1));
- assertEquals("two", proto.getTaggedData(2));
- }
-}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 6248856067a0..344f3c83ba6c 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -60,6 +60,7 @@
</permission>
<permission name="android.permission.WRITE_MEDIA_STORAGE" >
+ <group gid="media_rw" />
<group gid="sdcard_rw" />
</permission>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index ba7f05d86fc8..3d5ba7938b31 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -19,12 +19,12 @@ package android.graphics;
import android.annotation.CheckResult;
import android.annotation.ColorInt;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Log;
-
import libcore.util.NativeAllocationRegistry;
import java.io.OutputStream;
@@ -73,6 +73,8 @@ public final class Bitmap implements Parcelable {
private int mHeight;
private boolean mRecycled;
+ private ColorSpace mColorSpace;
+
/** @hide */
public int mDensity = getDefaultDensity();
@@ -145,6 +147,7 @@ public final class Bitmap implements Parcelable {
mWidth = width;
mHeight = height;
mRequestPremultiplied = requestPremultiplied;
+ mColorSpace = null;
}
/**
@@ -252,6 +255,7 @@ public final class Bitmap implements Parcelable {
nativeReconfigure(mNativePtr, width, height, config.nativeInt, mRequestPremultiplied);
mWidth = width;
mHeight = height;
+ mColorSpace = null;
}
/**
@@ -518,7 +522,7 @@ public final class Bitmap implements Parcelable {
* <p>The content of the bitmap is copied into the buffer as-is. This means
* that if this bitmap stores its pixels pre-multiplied
* (see {@link #isPremultiplied()}, the values in the buffer will also be
- * pre-multiplied.</p>
+ * pre-multiplied. The pixels remain in the color space of the bitmap.</p>
* <p>After this method returns, the current position of the buffer is
* updated: the position is incremented by the number of elements written
* in the buffer.</p>
@@ -558,7 +562,8 @@ public final class Bitmap implements Parcelable {
* <p>Copy the pixels from the buffer, beginning at the current position,
* overwriting the bitmap's pixels. The data in the buffer is not changed
* in any way (unlike setPixels(), which converts from unpremultipled 32bit
- * to whatever the bitmap's native format is.</p>
+ * to whatever the bitmap's native format is. The pixels in the source
+ * buffer are assumed to be in the bitmap's color space.</p>
* <p>After this method returns, the current position of the buffer is
* updated: the position is incremented by the number of elements read from
* the buffer. If you need to read the bitmap from the buffer again you must
@@ -1435,6 +1440,47 @@ public final class Bitmap implements Parcelable {
}
/**
+ * Returns the color space associated with this bitmap. If the color
+ * space is unknown, this method returns null.
+ */
+ @Nullable
+ public final ColorSpace getColorSpace() {
+ // A reconfigure can change the configuration and rgba16f is
+ // always linear scRGB at this time
+ if (getConfig() == Config.RGBA_F16) {
+ // Reset the color space for potential future reconfigurations
+ mColorSpace = null;
+ return ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB);
+ }
+
+ // Cache the color space retrieval since it can be fairly expensive
+ if (mColorSpace == null) {
+ if (nativeIsSRGB(mNativePtr)) {
+ mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+ } else {
+ float[] xyz = new float[9];
+ float[] params = new float[7];
+
+ boolean hasColorSpace = nativeGetColorSpace(mNativePtr, xyz, params);
+ if (hasColorSpace) {
+ ColorSpace.Rgb.TransferParameters parameters =
+ new ColorSpace.Rgb.TransferParameters(
+ params[0], params[1], params[2],
+ params[3], params[4], params[5], params[6]);
+ ColorSpace cs = ColorSpace.match(xyz, parameters);
+ if (cs != null) {
+ mColorSpace = cs;
+ } else {
+ mColorSpace = new ColorSpace.Rgb("Unknown", xyz, parameters);
+ }
+ }
+ }
+ }
+
+ return mColorSpace;
+ }
+
+ /**
* Fills the bitmap's pixels with the specified {@link Color}.
*
* @throws IllegalStateException if the bitmap is not mutable.
@@ -1450,7 +1496,8 @@ public final class Bitmap implements Parcelable {
/**
* Returns the {@link Color} at the specified location. Throws an exception
* if x or y are out of bounds (negative or >= to the width or height
- * respectively). The returned color is a non-premultiplied ARGB value.
+ * respectively). The returned color is a non-premultiplied ARGB value in
+ * the {@link ColorSpace.Named#SRGB sRGB} color space.
*
* @param x The x coordinate (0...width-1) of the pixel to return
* @param y The y coordinate (0...height-1) of the pixel to return
@@ -1472,7 +1519,8 @@ public final class Bitmap implements Parcelable {
* a packed int representing a {@link Color}. The stride parameter allows
* the caller to allow for gaps in the returned pixels array between
* rows. For normal packed results, just pass width for the stride value.
- * The returned colors are non-premultiplied ARGB values.
+ * The returned colors are non-premultiplied ARGB values in the
+ * {@link ColorSpace.Named#SRGB sRGB} color space.
*
* @param pixels The array to receive the bitmap's colors
* @param offset The first index to write into pixels[]
@@ -1565,7 +1613,8 @@ public final class Bitmap implements Parcelable {
/**
* <p>Write the specified {@link Color} into the bitmap (assuming it is
* mutable) at the x,y coordinate. The color must be a
- * non-premultiplied ARGB value.</p>
+ * non-premultiplied ARGB value in the {@link ColorSpace.Named#SRGB sRGB}
+ * color space.</p>
*
* @param x The x coordinate of the pixel to replace (0...width-1)
* @param y The y coordinate of the pixel to replace (0...height-1)
@@ -1587,7 +1636,7 @@ public final class Bitmap implements Parcelable {
/**
* <p>Replace pixels in the bitmap with the colors in the array. Each element
* in the array is a packed int representing a non-premultiplied ARGB
- * {@link Color}.</p>
+ * {@link Color} in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
*
* @param pixels The colors to write to the bitmap
* @param offset The index of the first color to read from pixels[]
@@ -1816,4 +1865,6 @@ public final class Bitmap implements Parcelable {
private static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap);
private static native Bitmap nativeCreateHardwareBitmap(GraphicBuffer buffer);
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/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index b1a433cbaaeb..908ec5045b27 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -20,8 +20,8 @@ import android.annotation.AnyThread;
import android.annotation.ColorInt;
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.Size;
import android.annotation.Nullable;
+import android.annotation.Size;
import android.util.Pair;
import java.util.ArrayList;
@@ -263,18 +263,18 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">sRGB IEC61966-2.1</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(\begin{equation}
- * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \le 0.0031308 \\
- * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \gt 0.0031308 \end{cases}
+ * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0031308 \\
+ * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0031308 \end{cases}
* \end{equation}\)
* </td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(\begin{equation}
- * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \le 0.04045 \\
- * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \gt 0.04045 \end{cases}
+ * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \lt 0.04045 \\
+ * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.04045 \end{cases}
* \end{equation}\)
* </td>
* </tr>
@@ -298,11 +298,11 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">sRGB IEC61966-2.1 (Linear)</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(C_{sRGB} = C_{linear}\)</td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(C_{linear} = C_{sRGB}\)</td>
* </tr>
* <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
@@ -325,22 +325,22 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">scRGB-nl IEC 61966-2-2:2003</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(\begin{equation}
* C_{scRGB} = \begin{cases} sign(C_{linear}) 12.92 \times \left| C_{linear} \right| &
- * \left| C_{linear} \right| \le 0.0031308 \\
+ * \left| C_{linear} \right| \lt 0.0031308 \\
* sign(C_{linear}) 1.055 \times \left| C_{linear} \right| ^{\frac{1}{2.4}} - 0.055 &
- * \left| C_{linear} \right| \gt 0.0031308 \end{cases}
+ * \left| C_{linear} \right| \ge 0.0031308 \end{cases}
* \end{equation}\)
* </td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(\begin{equation}
* C_{linear} = \begin{cases}sign(C_{scRGB}) \frac{\left| C_{scRGB} \right|}{12.92} &
- * \left| C_{scRGB} \right| \le 0.04045 \\
+ * \left| C_{scRGB} \right| \lt 0.04045 \\
* sign(C_{scRGB}) \left( \frac{\left| C_{scRGB} \right| + 0.055}{1.055} \right) ^{2.4} &
- * \left| C_{scRGB} \right| \gt 0.04045 \end{cases}
+ * \left| C_{scRGB} \right| \ge 0.04045 \end{cases}
* \end{equation}\)
* </td>
* </tr>
@@ -364,11 +364,11 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">scRGB IEC 61966-2-2:2003</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(C_{scRGB} = C_{linear}\)</td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(C_{linear} = C_{scRGB}\)</td>
* </tr>
* <tr><td>Range</td><td colspan="4">\([-0.5..7.499[\)</td></tr>
@@ -391,7 +391,7 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">Rec. ITU-R BT.709-5</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(\begin{equation}
* C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\
* 1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases}
@@ -399,7 +399,7 @@ public abstract class ColorSpace {
* </td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(\begin{equation}
* C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\
* \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases}
@@ -426,7 +426,7 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">Rec. ITU-R BT.2020-1</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(\begin{equation}
* C_{BT2020} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.0181 \\
* 1.0993 \times C_{linear}^{\frac{1}{2.2}} - 0.0993 & C_{linear} \ge 0.0181 \end{cases}
@@ -434,7 +434,7 @@ public abstract class ColorSpace {
* </td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(\begin{equation}
* C_{linear} = \begin{cases}\frac{C_{BT2020}}{4.5} & C_{BT2020} \lt 0.08145 \\
* \left( \frac{C_{BT2020} + 0.0993}{1.0993} \right) ^{2.2} & C_{BT2020} \ge 0.08145 \end{cases}
@@ -461,11 +461,11 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">SMPTE RP 431-2-2007 DCI (P3)</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">N/A</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(C_{P3} = C_{linear}^{\frac{1}{2.6}}\)</td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(C_{linear} = C_{P3}^{2.6}\)</td>
* </tr>
* <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
@@ -488,18 +488,18 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">Display P3</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(\begin{equation}
- * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \le 0.0031308 \\
- * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \gt 0.0031308 \end{cases}
+ * C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0031308 \\
+ * 1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0031308 \end{cases}
* \end{equation}\)
* </td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(\begin{equation}
- * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \le 0.04045 \\
- * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \gt 0.04045 \end{cases}
+ * C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \lt 0.04045 \\
+ * \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.04045 \end{cases}
* \end{equation}\)
* </td>
* </tr>
@@ -523,7 +523,7 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">NTSC (1953)</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">C</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(\begin{equation}
* C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\
* 1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases}
@@ -531,7 +531,7 @@ public abstract class ColorSpace {
* </td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(\begin{equation}
* C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\
* \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases}
@@ -558,7 +558,7 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">SMPTE-C RGB</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(\begin{equation}
* C_{BT709} = \begin{cases} 4.5 \times C_{linear} & C_{linear} \lt 0.018 \\
* 1.099 \times C_{linear}^{\frac{1}{2.2}} - 0.099 & C_{linear} \ge 0.018 \end{cases}
@@ -566,7 +566,7 @@ public abstract class ColorSpace {
* </td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(\begin{equation}
* C_{linear} = \begin{cases}\frac{C_{BT709}}{4.5} & C_{BT709} \lt 0.081 \\
* \left( \frac{C_{BT709} + 0.099}{1.099} \right) ^{2.2} & C_{BT709} \ge 0.081 \end{cases}
@@ -593,11 +593,11 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">Adobe RGB (1998)</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(C_{RGB} = C_{linear}^{\frac{1}{2.2}}\)</td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(C_{linear} = C_{RGB}^{2.2}\)</td>
* </tr>
* <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
@@ -620,7 +620,7 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">ROMM RGB ISO 22028-2:2013</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D50</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(\begin{equation}
* C_{ROMM} = \begin{cases} 16 \times C_{linear} & C_{linear} \lt 0.001953 \\
* C_{linear}^{\frac{1}{1.8}} & C_{linear} \ge 0.001953 \end{cases}
@@ -628,7 +628,7 @@ public abstract class ColorSpace {
* </td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(\begin{equation}
* C_{linear} = \begin{cases}\frac{C_{ROMM}}{16} & C_{ROMM} \lt 0.031248 \\
* C_{ROMM}^{1.8} & C_{ROMM} \ge 0.031248 \end{cases}
@@ -655,11 +655,11 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">SMPTE ST 2065-1:2012 ACES</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D60</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(C_{ACES} = C_{linear}\)</td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(C_{linear} = C_{ACES}\)</td>
* </tr>
* <tr><td>Range</td><td colspan="4">\([-65504.0, 65504.0]\)</td></tr>
@@ -682,11 +682,11 @@ public abstract class ColorSpace {
* <tr><td>Name</td><td colspan="4">Academy S-2014-004 ACEScg</td></tr>
* <tr><td>CIE standard illuminant</td><td colspan="4">D60</td></tr>
* <tr>
- * <td>Opto-electronic transfer function</td>
+ * <td>Opto-electronic transfer function (OETF)</td>
* <td colspan="4">\(C_{ACEScg} = C_{linear}\)</td>
* </tr>
* <tr>
- * <td>Electro-optical transfer function</td>
+ * <td>Electro-optical transfer function (EOTF)</td>
* <td colspan="4">\(C_{linear} = C_{ACEScg}\)</td>
* </tr>
* <tr><td>Range</td><td colspan="4">\([-65504.0, 65504.0]\)</td></tr>
@@ -1379,6 +1379,37 @@ public abstract class ColorSpace {
}
/**
+ * <p>Returns a {@link Named} instance of {@link ColorSpace} that matches
+ * the specified RGB to CIE XYZ transform and transfer functions. If no
+ * instance can be found, this method returns null.</p>
+ *
+ * <p>The color transform matrix is assumed to target the CIE XYZ space
+ * a {@link #ILLUMINANT_D50 D50} standard illuminant.</p>
+ *
+ * @param toXYZD50 3x3 column-major transform matrix from RGB to the profile
+ * connection space CIE XYZ as an array of 9 floats, cannot be null
+ * @param function Parameters for the transfer functions
+ * @return A non-null {@link ColorSpace} if a match is found, null otherwise
+ */
+ @Nullable
+ public static ColorSpace match(
+ @NonNull @Size(9) float[] toXYZD50,
+ @NonNull Rgb.TransferParameters function) {
+
+ for (ColorSpace colorSpace : sNamedColorSpaces) {
+ if (colorSpace.getModel() == Model.RGB) {
+ ColorSpace.Rgb rgb = (ColorSpace.Rgb) adapt(colorSpace, ILLUMINANT_D50_XYZ);
+ if (compare(toXYZD50, rgb.mTransform) &&
+ compare(function, rgb.mTransferParameters)) {
+ return colorSpace;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
* <p>Creates a new {@link Renderer} that can be used to visualize and
* debug color spaces. See the documentation of {@link Renderer} for
* more information.</p>
@@ -1397,17 +1428,14 @@ public abstract class ColorSpace {
"sRGB IEC61966-2.1",
SRGB_PRIMARIES,
ILLUMINANT_D65,
- x -> rcpResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
- x -> response(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
- 0.0f, 1.0f,
+ new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
Named.SRGB.ordinal()
);
sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb(
"sRGB IEC61966-2.1 (Linear)",
SRGB_PRIMARIES,
ILLUMINANT_D65,
- DoubleUnaryOperator.identity(),
- DoubleUnaryOperator.identity(),
+ 1.0,
0.0f, 1.0f,
Named.LINEAR_SRGB.ordinal()
);
@@ -1415,17 +1443,16 @@ public abstract class ColorSpace {
"scRGB-nl IEC 61966-2-2:2003",
SRGB_PRIMARIES,
ILLUMINANT_D65,
- x -> absRcpResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
- x -> absResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
+ 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,
Named.EXTENDED_SRGB.ordinal()
);
sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
- "scRGB- IEC 61966-2-2:2003",
+ "scRGB IEC 61966-2-2:2003",
SRGB_PRIMARIES,
ILLUMINANT_D65,
- DoubleUnaryOperator.identity(),
- DoubleUnaryOperator.identity(),
+ 1.0,
-0.5f, 7.499f,
Named.LINEAR_EXTENDED_SRGB.ordinal()
);
@@ -1433,26 +1460,21 @@ public abstract class ColorSpace {
"Rec. ITU-R BT.709-5",
new float[] { 0.640f, 0.330f, 0.300f, 0.600f, 0.150f, 0.060f },
ILLUMINANT_D65,
- x -> rcpResponse(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
- x -> response(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
- 0.0f, 1.0f,
+ new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
Named.BT709.ordinal()
);
sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb(
"Rec. ITU-R BT.2020-1",
new float[] { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f },
ILLUMINANT_D65,
- x -> rcpResponse(x, 1 / 0.45, 1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145),
- x -> response(x, 1 / 0.45, 1 / 1.0993, 0.099 / 1.0993, 1 / 4.5, 0.08145),
- 0.0f, 1.0f,
+ new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45),
Named.BT2020.ordinal()
);
sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb(
"SMPTE RP 431-2-2007 DCI (P3)",
new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
new float[] { 0.314f, 0.351f },
- x -> Math.pow(x < 0.0f ? 0.0f : x, 1 / 2.6),
- x -> Math.pow(x < 0.0f ? 0.0f : x, 2.6),
+ 2.6,
0.0f, 1.0f,
Named.DCI_P3.ordinal()
);
@@ -1460,35 +1482,28 @@ public abstract class ColorSpace {
"Display P3",
new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
ILLUMINANT_D65,
- x -> rcpResponse(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
- x -> response(x, 2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045),
- 0.0f, 1.0f,
+ new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
Named.DISPLAY_P3.ordinal()
);
sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
"NTSC (1953)",
NTSC_1953_PRIMARIES,
ILLUMINANT_C,
- x -> rcpResponse(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
- x -> response(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
- 0.0f, 1.0f,
+ new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
Named.NTSC_1953.ordinal()
);
sNamedColorSpaces[Named.SMPTE_C.ordinal()] = new ColorSpace.Rgb(
"SMPTE-C RGB",
new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f },
ILLUMINANT_D65,
- x -> rcpResponse(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
- x -> response(x, 1 / 0.45, 1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081),
- 0.0f, 1.0f,
+ new Rgb.TransferParameters(1 / 1.099, 0.099 / 1.099, 1 / 4.5, 0.081, 1 / 0.45),
Named.SMPTE_C.ordinal()
);
sNamedColorSpaces[Named.ADOBE_RGB.ordinal()] = new ColorSpace.Rgb(
"Adobe RGB (1998)",
new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f },
ILLUMINANT_D65,
- x -> Math.pow(x < 0.0f ? 0.0f : x, 1 / 2.2),
- x -> Math.pow(x < 0.0f ? 0.0f : x, 2.2),
+ 2.2,
0.0f, 1.0f,
Named.ADOBE_RGB.ordinal()
);
@@ -1496,17 +1511,14 @@ public abstract class ColorSpace {
"ROMM RGB ISO 22028-2:2013",
new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
ILLUMINANT_D50,
- x -> rcpResponse(x, 1.8, 1.0, 0.0, 1 / 16.0, 0.031248),
- x -> response(x, 1.8, 1.0, 0.0, 1 / 16.0, 0.031248),
- 0.0f, 1.0f,
+ new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8),
Named.PRO_PHOTO_RGB.ordinal()
);
sNamedColorSpaces[Named.ACES.ordinal()] = new ColorSpace.Rgb(
"SMPTE ST 2065-1:2012 ACES",
new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f },
ILLUMINANT_D60,
- DoubleUnaryOperator.identity(),
- DoubleUnaryOperator.identity(),
+ 1.0,
-65504.0f, 65504.0f,
Named.ACES.ordinal()
);
@@ -1514,8 +1526,7 @@ public abstract class ColorSpace {
"Academy S-2014-004 ACEScg",
new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f },
ILLUMINANT_D60,
- DoubleUnaryOperator.identity(),
- DoubleUnaryOperator.identity(),
+ 1.0,
-65504.0f, 65504.0f,
Named.ACESCG.ordinal()
);
@@ -1530,27 +1541,61 @@ public abstract class ColorSpace {
}
// Reciprocal piecewise gamma response
- private static double rcpResponse(double x, double g,double a, double b, double c, double d) {
+ private static double rcpResponse(double x, double a, double b, double c, double d, double g) {
return x >= d * c ? (Math.pow(x, 1.0 / g) - b) / a : x / c;
}
// Piecewise gamma response
- private static double response(double x, double g, double a, double b, double c, double d) {
+ private static double response(double x, double a, double b, double c, double d, double g) {
return x >= d ? Math.pow(a * x + b, g) : c * x;
}
+ // Reciprocal piecewise gamma response
+ private static double rcpResponse(double x, double a, double b, double c, double d,
+ double e, double f, double g) {
+ return x >= d * c ? (Math.pow(x - e, 1.0 / g) - b) / a : (x - f) / c;
+ }
+
+ // Piecewise gamma response
+ private static double response(double x, double a, double b, double c, double d,
+ double e, double f, double g) {
+ return x >= d ? Math.pow(a * x + b, g) + e : c * x + f;
+ }
+
// Reciprocal piecewise gamma response, encoded as sign(x).f(abs(x)) for color
// spaces that allow negative values
@SuppressWarnings("SameParameterValue")
- private static double absRcpResponse(double x, double g, double a, double b, double c, double d) {
- return Math.copySign(rcpResponse(x < 0.0 ? -x : x, g, a, b, c, d), x);
+ private static double absRcpResponse(double x, double a, double b, double c, double d, double g) {
+ return Math.copySign(rcpResponse(x < 0.0 ? -x : x, a, b, c, d, g), x);
}
// Piecewise gamma response, encoded as sign(x).f(abs(x)) for color spaces that
// allow negative values
@SuppressWarnings("SameParameterValue")
- private static double absResponse(double x, double g, double a, double b, double c, double d) {
- return Math.copySign(response(x < 0.0 ? -x : x, g, a, b, c, d), x);
+ private static double absResponse(double x, double a, double b, double c, double d, double g) {
+ return Math.copySign(response(x < 0.0 ? -x : x, a, b, c, d, g), x);
+ }
+
+ /**
+ * Compares two sets of parametric transfer functions parameters with a precision of 1e-3.
+ *
+ * @param a The first set of parameters to compare
+ * @param b The second set of parameters to compare
+ * @return True if the two sets are equal, false otherwise
+ */
+ private static boolean compare(
+ @Nullable Rgb.TransferParameters a,
+ @Nullable Rgb.TransferParameters b) {
+ //noinspection SimplifiableIfStatement
+ if (a == null && b == null) return true;
+ return a != null && b != null &&
+ Math.abs(a.a - b.a) < 1e-3 &&
+ Math.abs(a.b - b.b) < 1e-3 &&
+ Math.abs(a.c - b.c) < 1e-3 &&
+ Math.abs(a.d - b.d) < 1e-3 &&
+ Math.abs(a.e - b.e) < 1e-3 &&
+ Math.abs(a.f - b.f) < 1e-3 &&
+ Math.abs(a.g - b.g) < 1e-3;
}
/**
@@ -1710,7 +1755,7 @@ public abstract class ColorSpace {
* <p>Computes the chromatic adaptation transform from the specified
* source white point to the specified destination white point.</p>
*
- * <p>The transform is computed using the von Kris method, described
+ * <p>The transform is computed using the von Kries method, described
* in more details in the documentation of {@link Adaptation}. The
* {@link Adaptation} enum provides different matrices that can be
* used to perform the adaptation.</p>
@@ -1925,6 +1970,11 @@ public abstract class ColorSpace {
*
* $$RGB_{out} = OETF(f(EOTF(RGB_{in})))$$
*
+ * <p>If the transfer functions of the color space can be expressed as an
+ * ICC parametric curve as defined in ICC.1:2004-10, the numeric parameters
+ * can be retrieved by calling {@link #getTransferParameters()}. This can
+ * be useful to match color spaces for instance.</p>
+ *
* <p class="note">Some RGB color spaces, such as {@link Named#ACES} and
* {@link Named#LINEAR_EXTENDED_SRGB scRGB}, are said to be linear because
* their transfer functions are the identity function: \(f(x) = x\).
@@ -1967,14 +2017,175 @@ public abstract class ColorSpace {
*/
@AnyThread
public static class Rgb extends ColorSpace {
+ /**
+ * {@usesMathJax}
+ *
+ * <p>Defines the parameters for the ICC parametric curve type 4, as
+ * defined in ICC.1:2004-10, section 10.15.</p>
+ *
+ * <p>The EOTF is of the form:</p>
+ *
+ * \(\begin{equation}
+ * Y = \begin{cases}c X + f & X \lt d \\
+ * \left( a X + b \right) ^{g} + e & X \ge d \end{cases}
+ * \end{equation}\)
+ *
+ * <p>The corresponding OETF is simply the inverse function.</p>
+ *
+ * <p>The parameters defined by this class form a valid transfer
+ * function only if all the following conditions are met:</p>
+ * <ul>
+ * <li>No parameter is a {@link Double#isNaN(double) Not-a-Number}</li>
+ * <li>\(d\) is in the range \([0..1]\)</li>
+ * <li>The function is not constant</li>
+ * <li>The function is positive and increasing</li>
+ * </ul>
+ */
+ public static class TransferParameters {
+ /** Variable \(a\) in the equation of the EOTF described above. */
+ public final double a;
+ /** Variable \(b\) in the equation of the EOTF described above. */
+ public final double b;
+ /** Variable \(c\) in the equation of the EOTF described above. */
+ public final double c;
+ /** Variable \(d\) in the equation of the EOTF described above. */
+ public final double d;
+ /** Variable \(e\) in the equation of the EOTF described above. */
+ public final double e;
+ /** Variable \(f\) in the equation of the EOTF described above. */
+ public final double f;
+ /** Variable \(g\) in the equation of the EOTF described above. */
+ public final double g;
+
+ /**
+ * <p>Defines the parameters for the ICC parametric curve type 3, as
+ * defined in ICC.1:2004-10, section 10.15.</p>
+ *
+ * <p>The EOTF is of the form:</p>
+ *
+ * \(\begin{equation}
+ * Y = \begin{cases}c X & X \lt d \\
+ * \left( a X + b \right) ^{g} & X \ge d \end{cases}
+ * \end{equation}\)
+ *
+ * <p>This constructor is equivalent to setting \(e\) and \(f\) to 0.</p>
+ *
+ * @param a The value of \(a\) in the equation of the EOTF described above
+ * @param b The value of \(b\) in the equation of the EOTF described above
+ * @param c The value of \(c\) in the equation of the EOTF described above
+ * @param d The value of \(d\) in the equation of the EOTF described above
+ * @param g The value of \(g\) in the equation of the EOTF described above
+ *
+ * @throws IllegalArgumentException If the parameters form an invalid transfer function
+ */
+ public TransferParameters(double a, double b, double c, double d, double g) {
+ this(a, b, c, d, 0.0, 0.0, g);
+ }
+
+ /**
+ * <p>Defines the parameters for the ICC parametric curve type 4, as
+ * defined in ICC.1:2004-10, section 10.15.</p>
+ *
+ * @param a The value of \(a\) in the equation of the EOTF described above
+ * @param b The value of \(b\) in the equation of the EOTF described above
+ * @param c The value of \(c\) in the equation of the EOTF described above
+ * @param d The value of \(d\) in the equation of the EOTF described above
+ * @param e The value of \(e\) in the equation of the EOTF described above
+ * @param f The value of \(f\) in the equation of the EOTF described above
+ * @param g The value of \(g\) in the equation of the EOTF described above
+ *
+ * @throws IllegalArgumentException If the parameters form an invalid transfer function
+ */
+ public TransferParameters(double a, double b, double c, double d, double e,
+ double f, double g) {
+
+ if (Double.isNaN(a) || Double.isNaN(b) || Double.isNaN(c) ||
+ Double.isNaN(d) || Double.isNaN(e) || Double.isNaN(f) ||
+ Double.isNaN(g)) {
+ throw new IllegalArgumentException("Parameters cannot be NaN");
+ }
+
+ if (!(d >= 0.0 && d <= 1.0 + Math.ulp(1.0))) {
+ throw new IllegalArgumentException("Parameter d must be in the range [0..1]");
+ }
+
+ if (d == 0.0 && (a == 0.0 || g == 0.0)) {
+ throw new IllegalArgumentException(
+ "Parameter a or g is zero, the transfer function is constant");
+ }
+
+ if (d >= 1.0 && c == 0.0) {
+ throw new IllegalArgumentException(
+ "Parameter c is zero, the transfer function is constant");
+ }
+
+ if ((a == 0.0 || g == 0.0) && c == 0.0) {
+ throw new IllegalArgumentException("Parameter a or g is zero," +
+ " and c is zero, the transfer function is constant");
+ }
+
+ if (c < 0.0) {
+ throw new IllegalArgumentException("The transfer function must be increasing");
+ }
+
+ if (a < 0.0 || g < 0.0) {
+ throw new IllegalArgumentException("The transfer function must be " +
+ "positive or increasing");
+ }
+
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = d;
+ this.e = e;
+ this.f = f;
+ this.g = g;
+ }
+
+ @SuppressWarnings("SimplifiableIfStatement")
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TransferParameters that = (TransferParameters) o;
+
+ if (Double.compare(that.a, a) != 0) return false;
+ if (Double.compare(that.b, b) != 0) return false;
+ if (Double.compare(that.c, c) != 0) return false;
+ if (Double.compare(that.d, d) != 0) return false;
+ if (Double.compare(that.e, e) != 0) return false;
+ if (Double.compare(that.f, f) != 0) return false;
+ return Double.compare(that.g, g) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ int result;
+ long temp;
+ temp = Double.doubleToLongBits(a);
+ result = (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(b);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(c);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(d);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(e);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(f);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(g);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ return result;
+ }
+ }
+
@NonNull private final float[] mWhitePoint;
@NonNull private final float[] mPrimaries;
@NonNull private final float[] mTransform;
@NonNull private final float[] mInverseTransform;
- @NonNull private final boolean mIsWideGamut;
- @NonNull private final boolean mIsSrgb;
-
@NonNull private final DoubleUnaryOperator mOetf;
@NonNull private final DoubleUnaryOperator mEotf;
@NonNull private final DoubleUnaryOperator mClampedOetf;
@@ -1983,6 +2194,11 @@ public abstract class ColorSpace {
private final float mMin;
private final float mMax;
+ private final boolean mIsWideGamut;
+ private final boolean mIsSrgb;
+
+ @Nullable private TransferParameters mTransferParameters;
+
/**
* <p>Creates a new RGB color space using a 3x3 column-major transform matrix.
* The transform matrix must convert from the RGB space to the profile connection
@@ -2010,7 +2226,7 @@ public abstract class ColorSpace {
@NonNull @Size(9) float[] toXYZ,
@NonNull DoubleUnaryOperator oetf,
@NonNull DoubleUnaryOperator eotf) {
- this(name, computePrimaries(toXYZ, eotf), computeWhitePoint(toXYZ, eotf),
+ this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ),
oetf, eotf, 0.0f, 1.0f, MIN_ID);
}
@@ -2065,6 +2281,251 @@ public abstract class ColorSpace {
}
/**
+ * <p>Creates a new RGB color space using a 3x3 column-major transform matrix.
+ * The transform matrix must convert from the RGB space to the profile connection
+ * space CIE XYZ.</p>
+ *
+ * <p class="note">The range of the color space is imposed to be \([0..1]\).</p>
+ *
+ * @param name Name of the color space, cannot be null, its length must be >= 1
+ * @param toXYZ 3x3 column-major transform matrix from RGB to the profile
+ * connection space CIE XYZ as an array of 9 floats, cannot be null
+ * @param function Parameters for the transfer functions
+ *
+ * @throws IllegalArgumentException If any of the following conditions is met:
+ * <ul>
+ * <li>The name is null or has a length of 0.</li>
+ * <li>Gamma is negative.</li>
+ * </ul>
+ *
+ * @see #get(Named)
+ */
+ public Rgb(
+ @NonNull @Size(min = 1) String name,
+ @NonNull @Size(9) float[] toXYZ,
+ @NonNull TransferParameters function) {
+ this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), function, MIN_ID);
+ }
+
+ /**
+ * <p>Creates a new RGB color space using a specified set of primaries
+ * and a specified white point.</p>
+ *
+ * <p>The primaries and white point can be specified in the CIE xyY space
+ * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
+ *
+ * <table summary="Parameters length">
+ * <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
+ * <tr><td>xyY</td><td>6</td><td>2</td></tr>
+ * <tr><td>XYZ</td><td>9</td><td>3</td></tr>
+ * </table>
+ *
+ * <p>When the primaries and/or white point are specified in xyY, the Y component
+ * does not need to be specified and is assumed to be 1.0. Only the xy components
+ * are required.</p>
+ *
+ * @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 function Parameters for the transfer functions
+ *
+ * @throws IllegalArgumentException If any of the following conditions is met:
+ * <ul>
+ * <li>The name is null or has a length of 0.</li>
+ * <li>The primaries array is null or has a length that is neither 6 or 9.</li>
+ * <li>The white point array is null or has a length that is neither 2 or 3.</li>
+ * <li>The transfer parameters are invalid.</li>
+ * </ul>
+ *
+ * @see #get(Named)
+ */
+ public Rgb(
+ @NonNull @Size(min = 1) String name,
+ @NonNull @Size(min = 6, max = 9) float[] primaries,
+ @NonNull @Size(min = 2, max = 3) float[] whitePoint,
+ @NonNull TransferParameters function) {
+ this(name, primaries, whitePoint, function, MIN_ID);
+ }
+
+ /**
+ * <p>Creates a new RGB color space using a specified set of primaries
+ * and a specified white point.</p>
+ *
+ * <p>The primaries and white point can be specified in the CIE xyY space
+ * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
+ *
+ * <table summary="Parameters length">
+ * <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
+ * <tr><td>xyY</td><td>6</td><td>2</td></tr>
+ * <tr><td>XYZ</td><td>9</td><td>3</td></tr>
+ * </table>
+ *
+ * <p>When the primaries and/or white point are specified in xyY, the Y component
+ * does not need to be specified and is assumed to be 1.0. Only the xy components
+ * are required.</p>
+ *
+ * @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 function 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:
+ * <ul>
+ * <li>The name is null or has a length of 0.</li>
+ * <li>The primaries array is null or has a length that is neither 6 or 9.</li>
+ * <li>The white point array is null or has a length that is neither 2 or 3.</li>
+ * <li>The ID is not between {@link #MIN_ID} and {@link #MAX_ID}.</li>
+ * <li>The transfer parameters are invalid.</li>
+ * </ul>
+ *
+ * @see #get(Named)
+ */
+ private Rgb(
+ @NonNull @Size(min = 1) String name,
+ @NonNull @Size(min = 6, max = 9) float[] primaries,
+ @NonNull @Size(min = 2, max = 3) float[] whitePoint,
+ @NonNull TransferParameters function,
+ @IntRange(from = MIN_ID, to = MAX_ID) int id) {
+ this(name, primaries, whitePoint,
+ function.e == 0.0 && function.f == 0.0 ?
+ x -> rcpResponse(x, function.a, function.b,
+ function.c, function.d, function.g) :
+ x -> rcpResponse(x, function.a, function.b, function.c,
+ function.d, function.e, function.f, function.g),
+ function.e == 0.0 && function.f == 0.0 ?
+ x -> response(x, function.a, function.b,
+ 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;
+ }
+
+ /**
+ * <p>Creates a new RGB color space using a 3x3 column-major transform matrix.
+ * The transform matrix must convert from the RGB space to the profile connection
+ * space CIE XYZ.</p>
+ *
+ * <p class="note">The range of the color space is imposed to be \([0..1]\).</p>
+ *
+ * @param name Name of the color space, cannot be null, its length must be >= 1
+ * @param toXYZ 3x3 column-major transform matrix from RGB to the profile
+ * connection space CIE XYZ as an array of 9 floats, cannot be null
+ * @param gamma Gamma to use as the transfer function
+ *
+ * @throws IllegalArgumentException If any of the following conditions is met:
+ * <ul>
+ * <li>The name is null or has a length of 0.</li>
+ * <li>Gamma is negative.</li>
+ * </ul>
+ *
+ * @see #get(Named)
+ */
+ public Rgb(
+ @NonNull @Size(min = 1) String name,
+ @NonNull @Size(9) float[] toXYZ,
+ double gamma) {
+ this(name, computePrimaries(toXYZ), computeWhitePoint(toXYZ), gamma, 0.0f, 1.0f, MIN_ID);
+ }
+
+ /**
+ * <p>Creates a new RGB color space using a specified set of primaries
+ * and a specified white point.</p>
+ *
+ * <p>The primaries and white point can be specified in the CIE xyY space
+ * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
+ *
+ * <table summary="Parameters length">
+ * <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
+ * <tr><td>xyY</td><td>6</td><td>2</td></tr>
+ * <tr><td>XYZ</td><td>9</td><td>3</td></tr>
+ * </table>
+ *
+ * <p>When the primaries and/or white point are specified in xyY, the Y component
+ * does not need to be specified and is assumed to be 1.0. Only the xy components
+ * are required.</p>
+ *
+ * @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 gamma Gamma to use as the transfer function
+ *
+ * @throws IllegalArgumentException If any of the following conditions is met:
+ * <ul>
+ * <li>The name is null or has a length of 0.</li>
+ * <li>The primaries array is null or has a length that is neither 6 or 9.</li>
+ * <li>The white point array is null or has a length that is neither 2 or 3.</li>
+ * <li>Gamma is negative.</li>
+ * </ul>
+ *
+ * @see #get(Named)
+ */
+ public Rgb(
+ @NonNull @Size(min = 1) String name,
+ @NonNull @Size(min = 6, max = 9) float[] primaries,
+ @NonNull @Size(min = 2, max = 3) float[] whitePoint,
+ double gamma) {
+ this(name, primaries, whitePoint, gamma, 0.0f, 1.0f, MIN_ID);
+ }
+
+ /**
+ * <p>Creates a new RGB color space using a specified set of primaries
+ * and a specified white point.</p>
+ *
+ * <p>The primaries and white point can be specified in the CIE xyY space
+ * or in CIE XYZ. The length of the arrays depends on the chosen space:</p>
+ *
+ * <table summary="Parameters length">
+ * <tr><th>Space</th><th>Primaries length</th><th>White point length</th></tr>
+ * <tr><td>xyY</td><td>6</td><td>2</td></tr>
+ * <tr><td>XYZ</td><td>9</td><td>3</td></tr>
+ * </table>
+ *
+ * <p>When the primaries and/or white point are specified in xyY, the Y component
+ * does not need to be specified and is assumed to be 1.0. Only the xy components
+ * are required.</p>
+ *
+ * @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 gamma Gamma to use as the transfer function
+ * @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 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:
+ * <ul>
+ * <li>The name is null or has a length of 0.</li>
+ * <li>The primaries array is null or has a length that is neither 6 or 9.</li>
+ * <li>The white point array is null or has a length that is neither 2 or 3.</li>
+ * <li>The minimum valid value is >= the maximum valid value.</li>
+ * <li>The ID is not between {@link #MIN_ID} and {@link #MAX_ID}.</li>
+ * <li>Gamma is negative.</li>
+ * </ul>
+ *
+ * @see #get(Named)
+ */
+ private Rgb(
+ @NonNull @Size(min = 1) String name,
+ @NonNull @Size(min = 6, max = 9) float[] primaries,
+ @NonNull @Size(min = 2, max = 3) float[] whitePoint,
+ double gamma,
+ float min,
+ float max,
+ @IntRange(from = MIN_ID, to = MAX_ID) int id) {
+ this(name, primaries, whitePoint,
+ 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 = gamma == 1.0 ?
+ new TransferParameters(0.0, 0.0, 1.0, 1.0 + Math.ulp(1.0), gamma) :
+ new TransferParameters(1.0, 0.0, 0.0, 0.0, gamma);
+ }
+
+ /**
* <p>Creates a new RGB color space using a specified set of primaries
* and a specified white point.</p>
*
@@ -2183,6 +2644,8 @@ public abstract class ColorSpace {
mIsWideGamut = colorSpace.mIsWideGamut;
mIsSrgb = colorSpace.mIsSrgb;
+
+ mTransferParameters = colorSpace.mTransferParameters;
}
/**
@@ -2360,6 +2823,7 @@ public abstract class ColorSpace {
* @return A transfer function that converts from linear space to "gamma space"
*
* @see #getEotf()
+ * @see #getTransferParameters()
*/
@NonNull
public DoubleUnaryOperator getOetf() {
@@ -2383,12 +2847,31 @@ public abstract class ColorSpace {
* @return A transfer function that converts from "gamma space" to linear space
*
* @see #getOetf()
+ * @see #getTransferParameters()
*/
@NonNull
public DoubleUnaryOperator getEotf() {
return mClampedEotf;
}
+ /**
+ * <p>Returns the parameters used by the {@link #getEotf() electro-optical}
+ * and {@link #getOetf() opto-electronic} transfer functions. If the transfer
+ * functions do not match the ICC parametric curves defined in ICC.1:2004-10
+ * (section 10.15), this method returns null.</p>
+ *
+ * <p>See {@link TransferParameters} for a full description of the transfer
+ * functions.</p>
+ *
+ * @return An instance of {@link TransferParameters} or null if this color
+ * space's transfer functions do not match the equation defined in
+ * {@link TransferParameters}
+ */
+ @Nullable
+ public TransferParameters getTransferParameters() {
+ return mTransferParameters;
+ }
+
@Override
public boolean isSrgb() {
return mIsSrgb;
@@ -2544,6 +3027,11 @@ public abstract class ColorSpace {
if (Float.compare(rgb.mMax, mMax) != 0) return false;
if (!Arrays.equals(mWhitePoint, rgb.mWhitePoint)) return false;
if (!Arrays.equals(mPrimaries, rgb.mPrimaries)) return false;
+ if (mTransferParameters != null) {
+ return mTransferParameters.equals(rgb.mTransferParameters);
+ } else if (rgb.mTransferParameters == null) {
+ return true;
+ }
//noinspection SimplifiableIfStatement
if (!mOetf.equals(rgb.mOetf)) return false;
return mEotf.equals(rgb.mEotf);
@@ -2554,10 +3042,14 @@ public abstract class ColorSpace {
int result = super.hashCode();
result = 31 * result + Arrays.hashCode(mWhitePoint);
result = 31 * result + Arrays.hashCode(mPrimaries);
- result = 31 * result + mOetf.hashCode();
- result = 31 * result + mEotf.hashCode();
result = 31 * result + (mMin != +0.0f ? Float.floatToIntBits(mMin) : 0);
result = 31 * result + (mMax != +0.0f ? Float.floatToIntBits(mMax) : 0);
+ result = 31 * result +
+ (mTransferParameters != null ? mTransferParameters.hashCode() : 0);
+ if (mTransferParameters == null) {
+ result = 31 * result + mOetf.hashCode();
+ result = 31 * result + mEotf.hashCode();
+ }
return result;
}
@@ -2746,18 +3238,15 @@ public abstract class ColorSpace {
* range of the color space is [0..1].
*
* @param toXYZ The color space's 3x3 transform matrix to XYZ
- * @param EOTF The color space's electro-optical transfer function
* @return A new array of 6 floats containing the color space's
* primaries in CIE xyY
*/
@NonNull
@Size(6)
- private static float[] computePrimaries(@NonNull @Size(9) float[] toXYZ,
- DoubleUnaryOperator EOTF) {
- float one = (float) EOTF.applyAsDouble(1.0);
- float[] r = mul3x3Float3(toXYZ, new float[] { one, 0.0f, 0.0f });
- float[] g = mul3x3Float3(toXYZ, new float[] { 0.0f, one, 0.0f });
- float[] b = mul3x3Float3(toXYZ, new float[] { 0.0f, 0.0f, one });
+ private static float[] computePrimaries(@NonNull @Size(9) float[] toXYZ) {
+ float[] r = mul3x3Float3(toXYZ, new float[] { 1.0f, 0.0f, 0.0f });
+ float[] g = mul3x3Float3(toXYZ, new float[] { 0.0f, 1.0f, 0.0f });
+ float[] b = mul3x3Float3(toXYZ, new float[] { 0.0f, 0.0f, 1.0f });
float rSum = r[0] + r[1] + r[2];
float gSum = g[0] + g[1] + g[2];
@@ -2776,16 +3265,13 @@ public abstract class ColorSpace {
* range of the color space is [0..1].
*
* @param toXYZ The color space's 3x3 transform matrix to XYZ
- * @param EOTF The color space's electro-optical transfer function
* @return A new array of 2 floats containing the color space's
* white point in CIE xyY
*/
@NonNull
@Size(2)
- private static float[] computeWhitePoint(@NonNull @Size(9) float[] toXYZ,
- @NonNull DoubleUnaryOperator EOTF) {
- float one = (float) EOTF.applyAsDouble(1.0);
- float[] w = mul3x3Float3(toXYZ, new float[] { one, one, one });
+ private static float[] computeWhitePoint(@NonNull @Size(9) float[] toXYZ) {
+ float[] w = mul3x3Float3(toXYZ, new float[] { 1.0f, 1.0f, 1.0f });
float sum = w[0] + w[1] + w[2];
return new float[] { w[0] / sum, w[1] / sum };
}
@@ -2988,6 +3474,7 @@ public abstract class ColorSpace {
* Computes an extra transform to apply in XYZ space depending on the
* selected rendering intent.
*/
+ @Nullable
private static float[] computeTransform(@NonNull ColorSpace source,
@NonNull ColorSpace destination, @NonNull RenderIntent intent) {
if (intent != RenderIntent.ABSOLUTE) return null;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 52e18a9095cf..8b309035c013 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -186,8 +186,8 @@ public class Typeface {
}
// Downloaded font and it wasn't cached, request it again and return a
// default font instead (nothing we can do now).
- create(new FontRequest(family.getProviderAuthority(), family.getQuery()),
- NO_OP_REQUEST_CALLBACK);
+ create(new FontRequest(family.getProviderAuthority(), family.getProviderPackage(),
+ family.getQuery()), NO_OP_REQUEST_CALLBACK);
return DEFAULT;
}
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 9896a88bba19..0722c1856372 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -256,9 +256,12 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
}
}
+ /**
+ * Set the child layer bounds bigger than the view port size by {@link #DEFAULT_VIEW_PORT_SCALE}
+ */
private void updateLayerBoundsInternal(Rect bounds) {
- int cX = bounds.centerX();
- int cY = bounds.centerY();
+ int cX = bounds.width() / 2;
+ int cY = bounds.height() / 2;
for (int i = 0, count = mLayerState.N_CHILDREN; i < count; i++) {
final ChildDrawable r = mLayerState.mChildren[i];
@@ -283,7 +286,11 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
sMask.transform(mMaskMatrix, mMask);
- mMaskBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ALPHA_8);
+ if (mMaskBitmap == null || mMaskBitmap.getWidth() != b.width() ||
+ mMaskBitmap.getHeight() != b.height()) {
+ mMaskBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ALPHA_8);
+ mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
+ }
mCanvas.setBitmap(mMaskBitmap);
mPaint.setShader(null);
mCanvas.drawPath(mMask, mPaint);
@@ -291,7 +298,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
// reset everything that depends on the view bounds
mTransparentRegion.setEmpty();
mLayersShader = null;
- mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
}
@Override
@@ -310,7 +316,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint.setShader(mLayersShader);
}
- canvas.drawBitmap(mMaskBitmap, 0.0f, 0.0f, mPaint);
+ if (mMaskBitmap != null) {
+ Rect bounds = getBounds();
+ canvas.drawBitmap(mMaskBitmap, bounds.left, bounds.top, mPaint);
+ }
}
@Override
diff --git a/graphics/java/android/graphics/fonts/FontRequest.java b/graphics/java/android/graphics/fonts/FontRequest.java
index e50df6faa38e..c7a583056b76 100644
--- a/graphics/java/android/graphics/fonts/FontRequest.java
+++ b/graphics/java/android/graphics/fonts/FontRequest.java
@@ -18,24 +18,56 @@ package android.graphics.fonts;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Base64;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* Information about a font request that may be sent to a Font Provider.
*/
public final class FontRequest implements Parcelable {
private final String mProviderAuthority;
+ private final String mProviderPackage;
private final String mQuery;
+ private final List<List<byte[]>> mCertificates;
+
+ /**
+ * @param providerAuthority The authority of the Font Provider to be used for the request. This
+ * should be a system installed app.
+ * @param providerPackage The package for the Font Provider to be used for the request. This is
+ * used to verify the identity of the provider.
+ * @param query The query to be sent over to the provider. Refer to your font provider's
+ * documentation on the format of this string.
+ */
+ public FontRequest(@NonNull String providerAuthority, @NonNull String providerPackage,
+ @NonNull String query) {
+ mProviderAuthority = Preconditions.checkNotNull(providerAuthority);
+ mQuery = Preconditions.checkNotNull(query);
+ mProviderPackage = Preconditions.checkNotNull(providerPackage);
+ mCertificates = Collections.emptyList();
+ }
/**
* @param providerAuthority The authority of the Font Provider to be used for the request.
* @param query The query to be sent over to the provider. Refer to your font provider's
- * documentation on the format of this string.
+ * documentation on the format of this string.
+ * @param providerPackage The package for the Font Provider to be used for the request. This is
+ * used to verify the identity of the provider.
+ * @param certificates The list of sets of hashes for the certificates the provider should be
+ * signed with. This is used to verify the identity of the provider. Each set in the
+ * list represents one collection of signature hashes. Refer to your font provider's
+ * documentation for these values.
*/
- public FontRequest(@NonNull String providerAuthority, @NonNull String query) {
+ public FontRequest(@NonNull String providerAuthority, @NonNull String providerPackage,
+ @NonNull String query, @NonNull List<List<byte[]>> certificates) {
mProviderAuthority = Preconditions.checkNotNull(providerAuthority);
+ mProviderPackage = Preconditions.checkNotNull(providerPackage);
mQuery = Preconditions.checkNotNull(query);
+ mCertificates = Preconditions.checkNotNull(certificates);
}
/**
@@ -47,6 +79,14 @@ public final class FontRequest implements Parcelable {
}
/**
+ * Returns the selected font provider's package. This helps the system verify that the provider
+ * identified by the given authority is the one requested.
+ */
+ public String getProviderPackage() {
+ return mProviderPackage;
+ }
+
+ /**
* Returns the query string. Refer to your font provider's documentation on the format of this
* string.
*/
@@ -54,6 +94,14 @@ public final class FontRequest implements Parcelable {
return mQuery;
}
+ /**
+ * Returns the list of certificate sets given for this provider. This helps the system verify
+ * that the provider identified by the given authority is the one requested.
+ */
+ public List<List<byte[]>> getCertificates() {
+ return mCertificates;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -62,12 +110,17 @@ public final class FontRequest implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mProviderAuthority);
+ dest.writeString(mProviderPackage);
dest.writeString(mQuery);
+ dest.writeList(mCertificates);
}
private FontRequest(Parcel in) {
mProviderAuthority = in.readString();
+ mProviderPackage = in.readString();
mQuery = in.readString();
+ mCertificates = new ArrayList<>();
+ in.readList(mCertificates, null);
}
public static final Parcelable.Creator<FontRequest> CREATOR =
@@ -85,9 +138,24 @@ public final class FontRequest implements Parcelable {
@Override
public String toString() {
- return "FontRequest {"
+ StringBuilder builder = new StringBuilder();
+ builder.append("FontRequest {"
+ "mProviderAuthority: " + mProviderAuthority
+ + ", mProviderPackage: " + mProviderPackage
+ ", mQuery: " + mQuery
- + "}";
+ + ", mCertificates:");
+ for (int i = 0; i < mCertificates.size(); i++) {
+ builder.append(" [");
+ List<byte[]> set = mCertificates.get(i);
+ for (int j = 0; j < set.size(); j++) {
+ builder.append(" \"");
+ byte[] array = set.get(j);
+ builder.append(Base64.encodeToString(array, Base64.DEFAULT));
+ builder.append("\"");
+ }
+ builder.append(" ]");
+ }
+ builder.append("}");
+ return builder.toString();
}
}
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 84111ae0d499..acacd7654cf1 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -202,6 +202,15 @@ bool AssetManager::addAssetPath(
*cookie = static_cast<int32_t>(mAssetPaths.size());
}
+#ifdef __ANDROID__
+ // Load overlays, if any
+ asset_path oap;
+ for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
+ oap.isSystemAsset = isSystemAsset;
+ mAssetPaths.add(oap);
+ }
+#endif
+
if (mResources != NULL) {
appendPathToResTable(ap, appAsLib);
}
@@ -484,6 +493,11 @@ FileType AssetManager::getFileType(const char* fileName)
}
bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const {
+ // skip those ap's that correspond to system overlays
+ if (ap.isSystemOverlay) {
+ return true;
+ }
+
Asset* ass = NULL;
ResTable* sharedRes = NULL;
bool shared = true;
@@ -525,6 +539,14 @@ bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) con
ALOGV("Creating shared resources for %s", ap.path.string());
sharedRes = new ResTable();
sharedRes->add(ass, idmap, nextEntryIdx + 1, false);
+#ifdef __ANDROID__
+ const char* data = getenv("ANDROID_DATA");
+ LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
+ String8 overlaysListPath(data);
+ overlaysListPath.appendPath(kResourceCache);
+ overlaysListPath.appendPath("overlays.list");
+ addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx);
+#endif
sharedRes = const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTable(ap.path, sharedRes);
}
@@ -633,6 +655,58 @@ Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const
return ass;
}
+void AssetManager::addSystemOverlays(const char* pathOverlaysList,
+ const String8& targetPackagePath, ResTable* sharedRes, size_t offset) const
+{
+ FILE* fin = fopen(pathOverlaysList, "r");
+ if (fin == NULL) {
+ return;
+ }
+
+#ifndef _WIN32
+ if (TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_SH)) != 0) {
+ fclose(fin);
+ return;
+ }
+#endif
+ char buf[1024];
+ while (fgets(buf, sizeof(buf), fin)) {
+ // format of each line:
+ // <path to apk><space><path to idmap><newline>
+ char* space = strchr(buf, ' ');
+ char* newline = strchr(buf, '\n');
+ asset_path oap;
+
+ if (space == NULL || newline == NULL || newline < space) {
+ continue;
+ }
+
+ oap.path = String8(buf, space - buf);
+ oap.type = kFileTypeRegular;
+ oap.idmap = String8(space + 1, newline - space - 1);
+ oap.isSystemOverlay = true;
+
+ Asset* oass = const_cast<AssetManager*>(this)->
+ openNonAssetInPathLocked("resources.arsc",
+ Asset::ACCESS_BUFFER,
+ oap);
+
+ if (oass != NULL) {
+ Asset* oidmap = openIdmapLocked(oap);
+ offset++;
+ sharedRes->add(oass, oidmap, offset + 1, false);
+ const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
+ const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
+ delete oidmap;
+ }
+ }
+
+#ifndef _WIN32
+ TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_UN));
+#endif
+ fclose(fin);
+}
+
const ResTable& AssetManager::getResources(bool required) const
{
const ResTable* rt = getResTable(required);
@@ -1372,6 +1446,20 @@ bool AssetManager::SharedZip::isUpToDate()
return mModWhen == modWhen;
}
+void AssetManager::SharedZip::addOverlay(const asset_path& ap)
+{
+ mOverlays.add(ap);
+}
+
+bool AssetManager::SharedZip::getOverlay(size_t idx, asset_path* out) const
+{
+ if (idx >= mOverlays.size()) {
+ return false;
+ }
+ *out = mOverlays[idx];
+ return true;
+}
+
AssetManager::SharedZip::~SharedZip()
{
if (kIsDebug) {
@@ -1490,6 +1578,22 @@ bool AssetManager::ZipSet::isUpToDate()
return true;
}
+void AssetManager::ZipSet::addOverlay(const String8& path, const asset_path& overlay)
+{
+ int idx = getIndex(path);
+ sp<SharedZip> zip = mZipFile[idx];
+ zip->addOverlay(overlay);
+}
+
+bool AssetManager::ZipSet::getOverlay(const String8& path, size_t idx, asset_path* out) const
+{
+ sp<SharedZip> zip = SharedZip::get(path, false);
+ if (zip == NULL) {
+ return false;
+ }
+ return zip->getOverlay(idx, out);
+}
+
/*
* Compute the zip file's index.
*
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index f1e8b9364915..becd307d114d 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -202,10 +202,12 @@ public:
private:
struct asset_path
{
- asset_path() : path(""), type(kFileTypeRegular), idmap(""), isSystemAsset(false) {}
+ asset_path() : path(""), type(kFileTypeRegular), idmap(""),
+ isSystemOverlay(false), isSystemAsset(false) {}
String8 path;
FileType type;
String8 idmap;
+ bool isSystemOverlay;
bool isSystemAsset;
};
@@ -235,6 +237,9 @@ private:
Asset* openIdmapLocked(const struct asset_path& ap) const;
+ void addSystemOverlays(const char* pathOverlaysList, const String8& targetPackagePath,
+ ResTable* sharedRes, size_t offset) const;
+
class SharedZip : public RefBase {
public:
static sp<SharedZip> get(const String8& path, bool createIfNotPresent = true);
@@ -249,6 +254,9 @@ private:
bool isUpToDate();
+ void addOverlay(const asset_path& ap);
+ bool getOverlay(size_t idx, asset_path* out) const;
+
protected:
~SharedZip();
@@ -263,6 +271,8 @@ private:
Asset* mResourceTableAsset;
ResTable* mResourceTable;
+ Vector<asset_path> mOverlays;
+
static Mutex gLock;
static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
};
@@ -296,6 +306,9 @@ private:
bool isUpToDate();
+ void addOverlay(const String8& path, const asset_path& overlay);
+ bool getOverlay(const String8& path, size_t idx, asset_path* out) const;
+
private:
void closeZip(int idx);
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index 9f98241c6caa..9823a02dc847 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -31,7 +31,7 @@ static int computeClipSideFlags(const Rect& clip, const Rect& bounds) {
}
ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp, bool expandForStroke) {
+ const RecordedOp& recordedOp, bool expandForStroke, bool expandForPathTexture) {
// resolvedMatrix = parentMatrix * localMatrix
transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);
@@ -40,6 +40,8 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s
if (CC_UNLIKELY(expandForStroke)) {
// account for non-hairline stroke
clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f);
+ } else if (CC_UNLIKELY(expandForPathTexture)) {
+ clippedBounds.outset(1);
}
transform.mapRect(clippedBounds);
if (CC_UNLIKELY(expandForStroke
@@ -111,7 +113,7 @@ BakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator,
Snapshot& snapshot, const RecordedOp& recordedOp) {
if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
- allocator, snapshot, recordedOp, false);
+ allocator, snapshot, recordedOp, false, false);
if (bakedState->computedState.clippedBounds.isEmpty()) {
// bounds are empty, so op is rejected
allocator.rewindIfLastAlloc(bakedState);
@@ -127,14 +129,14 @@ BakedOpState* BakedOpState::tryConstructUnbounded(LinearAllocator& allocator,
}
BakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator,
- Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
+ Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior,
+ bool expandForPathTexture) {
if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
- ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
- : true;
+ bool expandForStroke = (strokeBehavior == StrokeBehavior::Forced
+ || (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style));
BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
- allocator, snapshot, recordedOp, expandForStroke);
+ allocator, snapshot, recordedOp, expandForStroke, expandForPathTexture);
if (bakedState->computedState.clippedBounds.isEmpty()) {
// bounds are empty, so op is rejected
// NOTE: this won't succeed if a clip was allocated
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index e1441fca5ee2..7b0b34f3d9c0 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -53,7 +53,7 @@ struct MergedBakedOpList {
class ResolvedRenderState {
public:
ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp, bool expandForStroke);
+ const RecordedOp& recordedOp, bool expandForStroke, bool expandForPathTexture);
// Constructor for unbounded ops *with* transform/clip
ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
@@ -117,7 +117,8 @@ public:
};
static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator,
- Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior);
+ Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior,
+ bool expandForPathTexture);
static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
Snapshot& snapshot, const ShadowOp* shadowOpPtr);
@@ -140,8 +141,8 @@ private:
friend class LinearAllocator;
BakedOpState(LinearAllocator& allocator, Snapshot& snapshot,
- const RecordedOp& recordedOp, bool expandForStroke)
- : computedState(allocator, snapshot, recordedOp, expandForStroke)
+ const RecordedOp& recordedOp, bool expandForStroke, bool expandForPathTexture)
+ : computedState(allocator, snapshot, recordedOp, expandForStroke, expandForPathTexture)
, alpha(snapshot.alpha)
, roundRectClipState(snapshot.roundRectClipState)
, op(&recordedOp) {}
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 7c2e78c7d3b8..19063e3768cd 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -133,7 +133,7 @@ public:
* this function returns GL_SRGB8_ALPHA8, otherwise it returns GL_RGBA
*/
constexpr GLint rgbaInternalFormat(bool needSRGB = true) const {
- return extensions().hasSRGB() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA;
+ return extensions().hasLinearBlending() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA;
}
/**
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 00238a25ebde..1e71cb081b39 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -20,6 +20,8 @@
#include "Properties.h"
#include "utils/StringUtils.h"
+#include <cutils/compiler.h>
+
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
@@ -28,14 +30,6 @@
namespace android {
namespace uirenderer {
-// Debug
-#if DEBUG_EXTENSIONS
- #define EXT_LOGD(...) ALOGD(__VA_ARGS__)
-#else
- #define EXT_LOGD(...)
-#endif
-
-
Extensions::Extensions() {
const char* version = (const char*) glGetString(GL_VERSION);
@@ -66,17 +60,18 @@ Extensions::Extensions() {
mHas4BitStencil = extensions.has("GL_OES_stencil4");
mHasUnpackSubImage = extensions.has("GL_EXT_unpack_subimage");
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
mHasSRGB = mVersionMajor >= 3 || extensions.has("GL_EXT_sRGB");
mHasSRGBWriteControl = extensions.has("GL_EXT_sRGB_write_control");
+#ifdef ANDROID_ENABLE_LINEAR_BLENDING
// If linear blending is enabled, the device must have (ES3.0 or EXT_sRGB)
// and EXT_sRGB_write_control
LOG_ALWAYS_FATAL_IF(!mHasSRGB, "Linear blending requires ES 3.0 or EXT_sRGB");
LOG_ALWAYS_FATAL_IF(!mHasSRGBWriteControl, "Linear blending requires EXT_sRGB_write_control");
+
+ mHasLinearBlending = true;
#else
- mHasSRGB = false;
- mHasSRGBWriteControl = false;
+ mHasLinearBlending = false;
#endif
}
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 2c38507bd79a..0ecfdb1b3e0a 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -17,11 +17,6 @@
#ifndef ANDROID_HWUI_EXTENSIONS_H
#define ANDROID_HWUI_EXTENSIONS_H
-#include <cutils/compiler.h>
-
-#include <string>
-#include <unordered_set>
-
namespace android {
namespace uirenderer {
@@ -45,6 +40,7 @@ public:
inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
inline bool hasSRGB() const { return mHasSRGB; }
inline bool hasSRGBWriteControl() const { return hasSRGB() && mHasSRGBWriteControl; }
+ inline bool hasLinearBlending() const { return hasSRGB() && mHasLinearBlending; }
inline int getMajorGlVersion() const { return mVersionMajor; }
inline int getMinorGlVersion() const { return mVersionMinor; }
@@ -59,6 +55,7 @@ private:
bool mHasUnpackSubImage;
bool mHasSRGB;
bool mHasSRGBWriteControl;
+ bool mHasLinearBlending;
int mVersionMajor;
int mVersionMinor;
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 1b57e290c198..86f9a5d73fd1 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -562,10 +562,11 @@ void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) {
* for paint's style on the bounds being computed.
*/
BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
- BakedOpState::StrokeBehavior strokeBehavior) {
+ BakedOpState::StrokeBehavior strokeBehavior, bool expandForPathTexture) {
// Note: here we account for stroke when baking the op
BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
- mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior);
+ mAllocator, *mCanvasState.writableSnapshot(), op,
+ strokeBehavior, expandForPathTexture);
if (!bakedState) return nullptr; // quick rejected
if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) {
@@ -590,7 +591,10 @@ static batchid_t tessBatchId(const RecordedOp& op) {
}
void FrameBuilder::deferArcOp(const ArcOp& op) {
- deferStrokeableOp(op, tessBatchId(op));
+ // Pass true below since arcs have a tendency to draw outside their expected bounds within
+ // their path textures. Passing true makes it more likely that we'll scissor, instead of
+ // corrupting the frame by drawing outside of clip bounds.
+ deferStrokeableOp(op, tessBatchId(op), BakedOpState::StrokeBehavior::StyleDefined, true);
}
static bool hasMergeableClip(const BakedOpState& state) {
@@ -748,7 +752,7 @@ static batchid_t textBatchId(const SkPaint& paint) {
void FrameBuilder::deferTextOp(const TextOp& op) {
BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
mAllocator, *mCanvasState.writableSnapshot(), op,
- BakedOpState::StrokeBehavior::StyleDefined);
+ BakedOpState::StrokeBehavior::StyleDefined, false);
if (!bakedState) return; // quick rejected
batchid_t batchId = textBatchId(*(op.paint));
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index b9154435c1e5..46048f7125ce 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -211,7 +211,8 @@ private:
}
BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
- BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
+ BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined,
+ bool expandForPathTexture = false);
/**
* Declares all FrameBuilder::deferXXXXOp() methods for every RecordedOp type.
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 71bee93fc4c7..18bfcc2bbddf 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -68,7 +68,7 @@ GradientCache::GradientCache(Extensions& extensions)
, mMaxSize(Properties::gradientCacheSize)
, mUseFloatTexture(extensions.hasFloatTextures())
, mHasNpot(extensions.hasNPot())
- , mHasSRGB(extensions.hasSRGB()) {
+ , mHasLinearBlending(extensions.hasLinearBlending()) {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
mCache.setOnEntryRemovedListener(this);
@@ -263,7 +263,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions,
if (mUseFloatTexture) {
texture->upload(GL_RGBA16F, width, height, GL_RGBA, GL_FLOAT, pixels);
} else {
- GLint internalFormat = mHasSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA;
+ GLint internalFormat = mHasLinearBlending ? GL_SRGB8_ALPHA8 : GL_RGBA;
texture->upload(internalFormat, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
}
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 5e35435ed64c..f299a40e994f 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -170,7 +170,7 @@ private:
GLint mMaxTextureSize;
bool mUseFloatTexture;
bool mHasNpot;
- bool mHasSRGB;
+ bool mHasLinearBlending;
mutable Mutex mLock;
}; // class GradientCache
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index ca056487a3ba..40ab7788f218 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -418,7 +418,7 @@ const char* gBlendOps[18] = {
ProgramCache::ProgramCache(Extensions& extensions)
: mHasES3(extensions.getMajorGlVersion() >= 3)
- , mHasSRGB(extensions.hasSRGB()) {
+ , mHasLinearBlending(extensions.hasLinearBlending()) {
}
ProgramCache::~ProgramCache() {
@@ -642,11 +642,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
}
if (description.hasBitmap || ((description.hasTexture || description.hasExternalTexture) &&
!description.hasAlpha8Texture)) {
- shader.append(gFS_OETF[description.hasLinearTexture && !mHasSRGB]);
+ shader.append(gFS_OETF[description.hasLinearTexture && !mHasLinearBlending]);
}
if (description.hasGradient) {
shader.append(gFS_Gradient_Functions);
- shader.append(gFS_Gradient_Preamble[mHasSRGB]);
+ shader.append(gFS_Gradient_Preamble[mHasLinearBlending]);
}
// Begin the shader
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index c2f715de70c3..cedd854bb48b 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -59,7 +59,7 @@ private:
std::map<programid, std::unique_ptr<Program>> mCache;
const bool mHasES3;
- const bool mHasSRGB;
+ const bool mHasLinearBlending;
}; // class ProgramCache
}; // namespace uirenderer
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 0dbd7674e2aa..cfc2744e61b2 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -241,21 +241,23 @@ void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType color
}
}
-SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB) {
+SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending,
+ sk_sp<SkColorSpace> sRGB) {
SkBitmap rgbaBitmap;
rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
- bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
+ bitmap.info().alphaType(), hasLinearBlending ? sRGB : nullptr));
rgbaBitmap.eraseColor(0);
SkCanvas canvas(rgbaBitmap);
canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
return rgbaBitmap;
}
-bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB) {
+bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending,
+ SkColorSpace* sRGB) {
bool needSRGB = info.colorSpace() == sRGB;
return info.colorType() == kARGB_4444_SkColorType
|| info.colorType() == kIndex_8_SkColorType
- || (info.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB);
+ || (info.colorType() == kRGB_565_SkColorType && hasLinearBlending && needSRGB);
}
@@ -295,11 +297,11 @@ void Texture::upload(Bitmap& bitmap) {
mCaches.textureState().bindTexture(mTarget, mId);
// TODO: Handle sRGB gray bitmaps
- bool hasSRGB = mCaches.extensions().hasSRGB();
- if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasSRGB, sRGB.get()))) {
+ bool hasLinearBlending = mCaches.extensions().hasLinearBlending();
+ if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending, sRGB.get()))) {
SkBitmap skBitmap;
bitmap.getSkBitmap(&skBitmap);
- SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
+ SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB));
uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(),
rgbaBitmap.height(), rgbaBitmap.getPixels());
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index ce9d4dc234a8..e7fbf20cd898 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -40,8 +40,10 @@ class Layer;
*/
class Texture : public GpuMemoryTracker {
public:
- static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasSRGB, sk_sp<SkColorSpace> sRGB);
- static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasSRGB, SkColorSpace* sRGB);
+ static SkBitmap uploadToN32(const SkBitmap& bitmap,
+ bool hasLinearBlending, sk_sp<SkColorSpace> sRGB);
+ static bool hasUnsupportedColorType(const SkImageInfo& info,
+ bool hasLinearBlending, SkColorSpace* sRGB);
static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType);
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index a55b061818a3..db4ff39aed83 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -19,6 +19,8 @@
#include <SkBitmap.h>
+#include <cutils/compiler.h>
+
#include <utils/LruCache.h>
#include <utils/Mutex.h>
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index e39614b6a5ea..c6fbe2bd55de 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -91,6 +91,8 @@ public:
LayerUpdateQueue* layerUpdateQueue = nullptr;
ErrorHandler* errorHandler = nullptr;
+ int32_t windowInsetLeft = 0;
+ int32_t windowInsetTop = 0;
bool updateWindowPositions = false;
struct Out {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index f9730c9ca273..72a9f4e34a2e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -30,8 +30,6 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <gui/IGraphicBufferAlloc.h>
-#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
#include <binder/IServiceManager.h>
#include <ui/PixelFormat.h>
@@ -219,13 +217,6 @@ sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThr
renderThread.eglManager().initialize();
uirenderer::Caches& caches = uirenderer::Caches::getInstance();
- sp<ISurfaceComposer> composer(ComposerService::getComposerService());
- sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
- if (alloc == NULL) {
- ALOGW("createGraphicBufferAlloc() failed in GraphicBuffer.create()");
- return nullptr;
- }
-
const SkImageInfo& info = skBitmap.info();
if (info.colorType() == kUnknown_SkColorType || info.colorType() == kAlpha_8_SkColorType) {
ALOGW("unable to create hardware bitmap of colortype: %d", info.colorType());
@@ -234,26 +225,28 @@ sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(uirenderer::renderthread::RenderThr
sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
bool needSRGB = skBitmap.info().colorSpace() == sRGB.get();
- bool hasSRGB = caches.extensions().hasSRGB();
+ bool hasLinearBlending = caches.extensions().hasLinearBlending();
GLint format, type, internalFormat;
uirenderer::Texture::colorTypeToGlFormatAndType(caches, skBitmap.colorType(),
needSRGB, &internalFormat, &format, &type);
PixelFormat pixelFormat = internalFormatToPixelFormat(internalFormat);
- status_t error;
- sp<GraphicBuffer> buffer = alloc->createGraphicBuffer(info.width(), info.height(), pixelFormat,
- 1, GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER
- | GraphicBuffer::USAGE_SW_READ_NEVER , &error);
-
- if (!buffer.get()) {
+ sp<GraphicBuffer> buffer = new GraphicBuffer(info.width(), info.height(), pixelFormat,
+ GraphicBuffer::USAGE_HW_TEXTURE |
+ GraphicBuffer::USAGE_SW_WRITE_NEVER |
+ GraphicBuffer::USAGE_SW_READ_NEVER,
+ std::string("Bitmap::allocateHardwareBitmap pid [") + std::to_string(getpid()) + "]");
+
+ status_t error = buffer->initCheck();
+ if (error < 0) {
ALOGW("createGraphicBuffer() failed in GraphicBuffer.create()");
return nullptr;
}
SkBitmap bitmap;
if (CC_UNLIKELY(uirenderer::Texture::hasUnsupportedColorType(skBitmap.info(),
- hasSRGB, sRGB.get()))) {
- bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasSRGB, std::move(sRGB));
+ hasLinearBlending, sRGB.get()))) {
+ bitmap = uirenderer::Texture::uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB));
} else {
bitmap = skBitmap;
}
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 7dfc2ee4fbe5..8bce990129de 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -174,13 +174,15 @@ void RenderState::interruptForFunctorInvoke() {
meshState().disableTexCoordsVertexArray();
debugOverdraw(false, false);
// TODO: We need a way to know whether the functor is sRGB aware (b/32072673)
- if (mCaches->extensions().hasSRGBWriteControl()) {
+ if (mCaches->extensions().hasLinearBlending() &&
+ mCaches->extensions().hasSRGBWriteControl()) {
glDisable(GL_FRAMEBUFFER_SRGB_EXT);
}
}
void RenderState::resumeFromFunctorInvoke() {
- if (mCaches->extensions().hasSRGBWriteControl()) {
+ if (mCaches->extensions().hasLinearBlending() &&
+ mCaches->extensions().hasSRGBWriteControl()) {
glEnable(GL_FRAMEBUFFER_SRGB_EXT);
}
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 83b01e906427..a46142642ed4 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -17,10 +17,6 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
-#include <gui/IGraphicBufferAlloc.h>
-#include <gui/ISurfaceComposer.h>
-#include <private/gui/ComposerService.h>
-#include <binder/IServiceManager.h>
#include <ui/PixelFormat.h>
#include <SkGradientShader.h>
#include <SkImagePriv.h>
@@ -39,14 +35,11 @@ public:
void createContent(int width, int height, Canvas& canvas) override {
canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
- status_t error;
- sp<ISurfaceComposer> composer(ComposerService::getComposerService());
- sp<IGraphicBufferAlloc> alloc(composer->createGraphicBufferAlloc());
uint32_t usage = GraphicBuffer::USAGE_HW_TEXTURE
| GraphicBuffer::USAGE_SW_READ_NEVER
| GRALLOC_USAGE_SW_WRITE_RARELY;
- sp<GraphicBuffer> buffer = alloc->createGraphicBuffer(400, 200, PIXEL_FORMAT_RGBA_8888, 1,
- usage, &error);
+
+ sp<GraphicBuffer> buffer = new GraphicBuffer(400, 200, PIXEL_FORMAT_RGBA_8888, usage);
unsigned char* pixels = nullptr;
buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, ((void**)&pixels));
@@ -88,4 +81,4 @@ public:
return image->makeShader(SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode);
}
-}; \ No newline at end of file
+};
diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp
index 0f8e0471556f..d51db2ebb169 100644
--- a/libs/hwui/tests/unit/BakedOpStateTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpStateTests.cpp
@@ -35,7 +35,7 @@ TEST(ResolvedRenderState, construct) {
{
// recorded with transform, no parent transform
auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
- ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false);
+ ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20);
EXPECT_EQ(Rect(100, 200), state.clipRect());
EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds); // translated and also clipped
@@ -44,7 +44,7 @@ TEST(ResolvedRenderState, construct) {
{
// recorded with transform and parent transform
auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
- ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false);
+ ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
Matrix4 expectedTranslate;
expectedTranslate.loadTranslate(20, 40, 0);
@@ -70,14 +70,14 @@ TEST(ResolvedRenderState, computeLocalSpaceClip) {
{
// recorded with transform, no parent transform
auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
- ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false);
+ ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
EXPECT_EQ(Rect(-10, -20, 90, 180), state.computeLocalSpaceClip())
<< "Local clip rect should be 100x200, offset by -10,-20";
}
{
// recorded with transform + parent transform
auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
- ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false);
+ ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
EXPECT_EQ(Rect(-10, -20, 80, 160), state.computeLocalSpaceClip())
<< "Local clip rect should be 90x190, offset by -10,-20";
}
@@ -170,7 +170,7 @@ TEST(ResolvedRenderState, construct_expandForStroke) {
snapshotMatrix.loadScale(testCase.scale, testCase.scale, 1);
auto parentSnapshot = TestUtils::makeSnapshot(snapshotMatrix, Rect(200, 200));
- ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, true);
+ ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, true, false);
testCase.validator(state);
}
}
@@ -234,7 +234,7 @@ TEST(BakedOpState, tryStrokeableOpConstruct) {
RectOp rejectOp(Rect(100, 200), Matrix4::identity(), &clip, &paint);
auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
- BakedOpState::StrokeBehavior::StyleDefined);
+ BakedOpState::StrokeBehavior::StyleDefined, false);
EXPECT_EQ(nullptr, bakedState);
EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
@@ -248,7 +248,7 @@ TEST(BakedOpState, tryStrokeableOpConstruct) {
RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
- BakedOpState::StrokeBehavior::StyleDefined);
+ BakedOpState::StrokeBehavior::StyleDefined, false);
ASSERT_NE(nullptr, bakedState);
EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
@@ -263,7 +263,7 @@ TEST(BakedOpState, tryStrokeableOpConstruct) {
RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
- BakedOpState::StrokeBehavior::Forced);
+ BakedOpState::StrokeBehavior::Forced, false);
ASSERT_NE(nullptr, bakedState);
EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 95d9459e898c..a329980b7609 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -171,6 +171,35 @@ RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleStroke) {
EXPECT_EQ(1, renderer.getIndex());
}
+
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, arcStrokeClip) {
+ class ArcStrokeClipTestRenderer : public TestRendererBase {
+ public:
+ void onArcOp(const ArcOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(Rect(25, 25, 175, 175), op.unmappedBounds);
+ EXPECT_EQ(Rect(25, 25, 175, 175), state.computedState.clippedBounds);
+ EXPECT_EQ(OpClipSideFlags::Full, state.computedState.clipSideFlags)
+ << "Arc op clipped conservatively, since path texture may be expanded";
+ }
+ };
+
+ auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.clipRect(25, 25, 175, 175, SkClipOp::kIntersect);
+ SkPaint aaPaint;
+ aaPaint.setAntiAlias(true);
+ canvas.drawArc(25, 25, 175, 175, 40, 180, true, aaPaint);
+ });
+ FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
+ ArcStrokeClipTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex());
+}
+
RENDERTHREAD_OPENGL_PIPELINE_TEST(FrameBuilder, simpleRejection) {
auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 0950eb8e7134..4a27ca2f327a 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -121,4 +121,4 @@ namespace uirenderer {
} /* namespace uirenderer */
} /* namespace android */
-#endif /* TEST_UTILS_H */
+#endif /* COLOR_H */
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 3ac4c3471ee3..c157a479cfe8 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1119,6 +1119,10 @@ public final class MediaCodecInfo {
sampleRates = new int[] { 8000 };
bitRates = Range.create(13000, 13000);
maxChannels = 1;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_AC3)) {
+ maxChannels = 6;
+ } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_EAC3)) {
+ maxChannels = 16;
} else {
Log.w(TAG, "Unsupported mime " + mime);
mParent.mError |= ERROR_UNSUPPORTED;
diff --git a/media/java/android/media/VolumeShaper.java b/media/java/android/media/VolumeShaper.java
index cb27d1089aae..796d6f312640 100644
--- a/media/java/android/media/VolumeShaper.java
+++ b/media/java/android/media/VolumeShaper.java
@@ -25,6 +25,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.AutoCloseable;
import java.lang.ref.WeakReference;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -115,6 +116,7 @@ public final class VolumeShaper implements AutoCloseable {
* @param configuration
* @param operation
* @return id a non-negative shaper id.
+ * @throws IllegalStateException if the player has been deallocated or is uninitialized.
*/
private int applyPlayer(
@NonNull VolumeShaper.Configuration configuration,
@@ -147,6 +149,7 @@ public final class VolumeShaper implements AutoCloseable {
* Internal call to retrieve the current VolumeShaper state.
* @param id
* @return the current {@vode VolumeShaper.State}
+ * @throws IllegalStateException if the player has been deallocated or is uninitialized.
*/
private @NonNull VolumeShaper.State getStatePlayer(int id) {
final VolumeShaper.State state;
@@ -172,10 +175,10 @@ public final class VolumeShaper implements AutoCloseable {
* <p>
* A {@code VolumeShaper.Configuration} is used by
* {@link VolumeAutomation#createVolumeShaper(Configuration)
- * VolumeAutomation#createVolumeShaper(Configuration)} to create
+ * VolumeAutomation.createVolumeShaper(Configuration)} to create
* a {@code VolumeShaper} and
* by {@link VolumeShaper#replace(Configuration, Operation, boolean)
- * VolumeShaper#replace(Configuration, Operation, boolean)}
+ * VolumeShaper.replace(Configuration, Operation, boolean)}
* to replace an existing {@code configuration}.
*/
public static final class Configuration implements Parcelable {
@@ -365,31 +368,34 @@ public final class VolumeShaper implements AutoCloseable {
private final int mId;
// valid when mType is TYPE_SCALE
- private final int mInterpolatorType;
private final int mOptionFlags;
private final double mDurationMs;
+ private final int mInterpolatorType;
private final float[] mTimes;
private final float[] mVolumes;
@Override
public String toString() {
- return "VolumeShaper.Configuration["
- + "mType=" + mType
+ return "VolumeShaper.Configuration{"
+ + "mType = " + mType
+ + ", mId = " + mId
+ (mType == TYPE_ID
- ? ",mId" + mId
- : ",mInterpolatorType=" + mInterpolatorType
- + ",mOptionFlags=" + mOptionFlags
- + ",mDurationMs=" + mDurationMs
- + ",mTimes[]=" + mTimes
- + ",mVolumes[]=" + mVolumes
- + "]");
+ ? "}"
+ : ", mOptionFlags = 0x" + Integer.toHexString(mOptionFlags).toUpperCase()
+ + ", mDurationMs = " + mDurationMs
+ + ", mInterpolatorType = " + mInterpolatorType
+ + ", mTimes[] = " + Arrays.toString(mTimes)
+ + ", mVolumes[] = " + Arrays.toString(mVolumes)
+ + "}");
}
@Override
public int hashCode() {
return mType == TYPE_ID
? Objects.hash(mType, mId)
- : Objects.hash(mType, mInterpolatorType, mDurationMs, mTimes, mVolumes);
+ : Objects.hash(mType, mId,
+ mOptionFlags, mDurationMs, mInterpolatorType,
+ Arrays.hashCode(mTimes), Arrays.hashCode(mVolumes));
}
@Override
@@ -397,12 +403,17 @@ public final class VolumeShaper implements AutoCloseable {
if (!(o instanceof Configuration)) return false;
if (o == this) return true;
final Configuration other = (Configuration) o;
- return mType == other.mType &&
- (mType == TYPE_ID ? mId == other.mId
- : mInterpolatorType == other.mInterpolatorType
- && mDurationMs == other.mDurationMs
- && mTimes == other.mTimes
- && mVolumes == other.mVolumes);
+ // Note that exact floating point equality may not be guaranteed
+ // for a theoretically idempotent operation; for example,
+ // there are many cases where a + b - b != a.
+ return mType == other.mType
+ && mId == other.mId
+ && (mType == TYPE_ID
+ || (mOptionFlags == other.mOptionFlags
+ && mDurationMs == other.mDurationMs
+ && mInterpolatorType == other.mInterpolatorType
+ && Arrays.equals(mTimes, other.mTimes)
+ && Arrays.equals(mVolumes, other.mVolumes)));
}
@Override
@@ -412,14 +423,22 @@ public final class VolumeShaper implements AutoCloseable {
@Override
public void writeToParcel(Parcel dest, int flags) {
+ // this needs to match the native VolumeShaper.Configuration parceling
dest.writeInt(mType);
dest.writeInt(mId);
if (mType != TYPE_ID) {
- dest.writeInt(mInterpolatorType);
dest.writeInt(mOptionFlags);
dest.writeDouble(mDurationMs);
- dest.writeFloatArray(mTimes);
- dest.writeFloatArray(mVolumes);
+ // this needs to match the native Interpolator parceling
+ dest.writeInt(mInterpolatorType);
+ dest.writeFloat(0.f); // first slope
+ dest.writeFloat(0.f); // last slope
+ // mTimes and mVolumes should have the same length.
+ dest.writeInt(mTimes.length);
+ for (int i = 0; i < mTimes.length; ++i) {
+ dest.writeFloat(mTimes[i]);
+ dest.writeFloat(mVolumes[i]);
+ }
}
}
@@ -427,19 +446,34 @@ public final class VolumeShaper implements AutoCloseable {
= new Parcelable.Creator<VolumeShaper.Configuration>() {
@Override
public VolumeShaper.Configuration createFromParcel(Parcel p) {
+ // this needs to match the native VolumeShaper.Configuration parceling
final int type = p.readInt();
final int id = p.readInt();
if (type == TYPE_ID) {
return new VolumeShaper.Configuration(id);
} else {
+ final int optionFlags = p.readInt();
+ final double durationMs = p.readDouble();
+ // this needs to match the native Interpolator parceling
+ final int interpolatorType = p.readInt();
+ final float firstSlope = p.readFloat(); // ignored
+ final float lastSlope = p.readFloat(); // ignored
+ final int length = p.readInt();
+ final float[] times = new float[length];
+ final float[] volumes = new float[length];
+ for (int i = 0; i < length; ++i) {
+ times[i] = p.readFloat();
+ volumes[i] = p.readFloat();
+ }
+
return new VolumeShaper.Configuration(
type,
- id, // id
- p.readInt(), // interpolatorType
- p.readInt(), // optionFlags
- p.readDouble(), // durationMs
- p.createFloatArray(), // times
- p.createFloatArray()); // volumes
+ id,
+ optionFlags,
+ durationMs,
+ interpolatorType,
+ times,
+ volumes);
}
}
@@ -482,16 +516,16 @@ public final class VolumeShaper implements AutoCloseable {
*/
private Configuration(@Type int type,
int id,
- @InterpolatorType int interpolatorType,
@OptionFlag int optionFlags,
double durationMs,
+ @InterpolatorType int interpolatorType,
@NonNull float[] times,
@NonNull float[] volumes) {
mType = type;
mId = id;
- mInterpolatorType = interpolatorType;
mOptionFlags = optionFlags;
mDurationMs = durationMs;
+ mInterpolatorType = interpolatorType;
// Builder should have cloned these arrays already.
mTimes = times;
mVolumes = volumes;
@@ -568,8 +602,12 @@ public final class VolumeShaper implements AutoCloseable {
* @return null if no error, or the reason in a {@code String} for an error.
*/
private static @Nullable String checkCurveForErrors(
- @NonNull float[] times, @NonNull float[] volumes, boolean log) {
- if (times.length != volumes.length) {
+ @Nullable float[] times, @Nullable float[] volumes, boolean log) {
+ if (times == null) {
+ return "times array must be non-null";
+ } else if (volumes == null) {
+ return "volumes array must be non-null";
+ } else if (times.length != volumes.length) {
return "array length must match";
} else if (times.length < 2) {
return "array length must be at least 2";
@@ -605,7 +643,15 @@ public final class VolumeShaper implements AutoCloseable {
return null; // no errors
}
- private static void checkValidVolume(float volume, boolean log) {
+ private static void checkCurveForErrorsAndThrowException(
+ @Nullable float[] times, @Nullable float[] volumes, boolean log) {
+ final String error = checkCurveForErrors(times, volumes, log);
+ if (error != null) {
+ throw new IllegalArgumentException(error);
+ }
+ }
+
+ private static void checkValidVolumeAndThrowException(float volume, boolean log) {
if (log) {
if (!(volume <= 0.f) /* handle nan */) {
throw new IllegalArgumentException("dbfs volume must be 0.f or less");
@@ -678,17 +724,22 @@ public final class VolumeShaper implements AutoCloseable {
mOptionFlags = configuration.getAllOptionFlags();
mInterpolatorType = configuration.getInterpolatorType();
mDurationMs = configuration.getDurationMs();
- mTimes = configuration.getTimes();
- mVolumes = configuration.getVolumes();
+ mTimes = configuration.getTimes().clone();
+ mVolumes = configuration.getVolumes().clone();
}
/**
* @hide
- * Set the id for system defined shapers.
- * @param id
- * @return
+ * Set the {@code id} for system defined shapers.
+ * @param id the {@code id} to set. If non-negative, then it is used.
+ * If -1, then the system is expected to assign one.
+ * @return the same {@code Builder} instance.
+ * @throws IllegalArgumentException if {@code id} < -1.
*/
public @NonNull Builder setId(int id) {
+ if (id < -1) {
+ throw new IllegalArgumentException("invalid id: " + id);
+ }
mId = id;
return this;
}
@@ -789,11 +840,8 @@ public final class VolumeShaper implements AutoCloseable {
*/
public @NonNull Builder setCurve(@NonNull float[] times, @NonNull float[] volumes) {
- String error = checkCurveForErrors(
+ checkCurveForErrorsAndThrowException(
times, volumes, (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0);
- if (error != null) {
- throw new IllegalArgumentException(error);
- }
mTimes = times.clone();
mVolumes = volumes.clone();
return this;
@@ -805,13 +853,19 @@ public final class VolumeShaper implements AutoCloseable {
* to the start.
*
* @return the same {@code Builder} instance.
+ * @throws IllegalArgumentException if curve has not been set.
*/
public @NonNull Builder reflectTimes() {
+ checkCurveForErrorsAndThrowException(
+ mTimes, mVolumes, (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0);
int i;
for (i = 0; i < mTimes.length / 2; ++i) {
- float temp = mTimes[0];
+ float temp = mTimes[i];
mTimes[i] = 1.f - mTimes[mTimes.length - 1 - i];
mTimes[mTimes.length - 1 - i] = 1.f - temp;
+ temp = mVolumes[i];
+ mVolumes[i] = mVolumes[mVolumes.length - 1 - i];
+ mVolumes[mVolumes.length - 1 - i] = temp;
}
if ((mTimes.length & 1) != 0) {
mTimes[i] = 1.f - mTimes[i];
@@ -824,23 +878,24 @@ public final class VolumeShaper implements AutoCloseable {
* becomes the min volume and vice versa.
*
* @return the same {@code Builder} instance.
+ * @throws IllegalArgumentException if curve has not been set.
*/
public @NonNull Builder invertVolumes() {
- if (mVolumes.length >= 2) {
- float min = mVolumes[0];
- float max = mVolumes[0];
- for (int i = 1; i < mVolumes.length; ++i) {
- if (mVolumes[i] < min) {
- min = mVolumes[i];
- } else if (mVolumes[i] > max) {
- max = mVolumes[i];
- }
+ checkCurveForErrorsAndThrowException(
+ mTimes, mVolumes, (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0);
+ float min = mVolumes[0];
+ float max = mVolumes[0];
+ for (int i = 1; i < mVolumes.length; ++i) {
+ if (mVolumes[i] < min) {
+ min = mVolumes[i];
+ } else if (mVolumes[i] > max) {
+ max = mVolumes[i];
}
+ }
- final float maxmin = max + min;
- for (int i = 0; i < mVolumes.length; ++i) {
- mVolumes[i] = maxmin - mVolumes[i];
- }
+ final float maxmin = max + min;
+ for (int i = 0; i < mVolumes.length; ++i) {
+ mVolumes[i] = maxmin - mVolumes[i];
}
return this;
}
@@ -853,11 +908,13 @@ public final class VolumeShaper implements AutoCloseable {
*
* @param volume the target end volume to use.
* @return the same {@code Builder} instance.
- * @throws IllegalArgumentException if {@code volume} is not valid.
+ * @throws IllegalArgumentException if {@code volume}
+ * is not valid or if curve has not been set.
*/
public @NonNull Builder scaleToEndVolume(float volume) {
final boolean log = (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0;
- checkValidVolume(volume, log);
+ checkCurveForErrorsAndThrowException(mTimes, mVolumes, log);
+ checkValidVolumeAndThrowException(volume, log);
final float startVolume = mVolumes[0];
final float endVolume = mVolumes[mVolumes.length - 1];
if (endVolume == startVolume) {
@@ -885,11 +942,13 @@ public final class VolumeShaper implements AutoCloseable {
*
* @param volume the target start volume to use.
* @return the same {@code Builder} instance.
- * @throws IllegalArgumentException if {@code volume} is not valid.
+ * @throws IllegalArgumentException if {@code volume}
+ * is not valid or if curve has not been set.
*/
public @NonNull Builder scaleToStartVolume(float volume) {
final boolean log = (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0;
- checkValidVolume(volume, log);
+ checkCurveForErrorsAndThrowException(mTimes, mVolumes, log);
+ checkValidVolumeAndThrowException(volume, log);
final float startVolume = mVolumes[0];
final float endVolume = mVolumes[mVolumes.length - 1];
if (endVolume == startVolume) {
@@ -911,16 +970,14 @@ public final class VolumeShaper implements AutoCloseable {
/**
* Builds a new {@link VolumeShaper} object.
*
- * @return a new {@link VolumeShaper} object
+ * @return a new {@link VolumeShaper} object.
+ * @throws IllegalArgumentException if curve is not properly set.
*/
public @NonNull Configuration build() {
- String error = checkCurveForErrors(
+ checkCurveForErrorsAndThrowException(
mTimes, mVolumes, (mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0);
- if (error != null) {
- throw new IllegalArgumentException(error);
- }
- return new Configuration(mType, mId, mInterpolatorType, mOptionFlags,
- mDurationMs, mTimes, mVolumes);
+ return new Configuration(mType, mId, mOptionFlags, mDurationMs,
+ mInterpolatorType, mTimes, mVolumes);
}
} // Configuration.Builder
} // Configuration
@@ -1011,10 +1068,10 @@ public final class VolumeShaper implements AutoCloseable {
@Override
public String toString() {
- return "VolumeShaper.Operation["
- + "mFlags=" + mFlags
- + ",mReplaceId" + mReplaceId
- + "]";
+ return "VolumeShaper.Operation{"
+ + "mFlags = 0x" + Integer.toHexString(mFlags).toUpperCase()
+ + ", mReplaceId = " + mReplaceId
+ + "}";
}
@Override
@@ -1027,6 +1084,8 @@ public final class VolumeShaper implements AutoCloseable {
if (!(o instanceof Operation)) return false;
if (o == this) return true;
final Operation other = (Operation) o;
+ // if xOffset (native field only) is brought into Java
+ // we need to do proper NaN comparison as that is allowed.
return mFlags == other.mFlags
&& mReplaceId == other.mReplaceId;
}
@@ -1038,17 +1097,24 @@ public final class VolumeShaper implements AutoCloseable {
@Override
public void writeToParcel(Parcel dest, int flags) {
+ // this needs to match the native VolumeShaper.Operation parceling
dest.writeInt(mFlags);
dest.writeInt(mReplaceId);
+ dest.writeFloat(Float.NaN); // xOffset (ignored at Java level)
}
public static final Parcelable.Creator<VolumeShaper.Operation> CREATOR
= new Parcelable.Creator<VolumeShaper.Operation>() {
@Override
public VolumeShaper.Operation createFromParcel(Parcel p) {
+ // this needs to match the native VolumeShaper.Operation parceling
+ final int flags = p.readInt();
+ final int replaceId = p.readInt();
+ final float xOffset = p.readFloat(); // ignored at Java level
+
return new VolumeShaper.Operation(
- p.readInt() // flags
- , p.readInt()); // replaceId
+ flags
+ , replaceId);
}
@Override
@@ -1154,6 +1220,7 @@ public final class VolumeShaper implements AutoCloseable {
*
* @param flags new value for {@code flags}, consisting of ORed flags.
* @return the same {@code Builder} instance.
+ * @throws IllegalArgumentException if {@code flags} contains invalid set bits.
*/
private @NonNull Builder setFlags(@Flag int flags) {
if ((flags & ~FLAG_PUBLIC_ALL) != 0) {
@@ -1187,10 +1254,10 @@ public final class VolumeShaper implements AutoCloseable {
@Override
public String toString() {
- return "VolumeShaper.State["
- + "mVolume=" + mVolume
- + ",mXOffset" + mXOffset
- + "]";
+ return "VolumeShaper.State{"
+ + "mVolume = " + mVolume
+ + ", mXOffset = " + mXOffset
+ + "}";
}
@Override
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index e5af35711311..aee9d38e0a27 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -776,8 +776,8 @@ public class TvView extends ViewGroup {
mSurface = null;
mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr) {
@Override
- protected void updateSurface() {
- super.updateSurface();
+ protected void updateWindow() {
+ super.updateWindow();
relayoutSessionOverlayView();
}};
// The surface view's content should be treated as secure all the time.
diff --git a/media/jni/android_media_VolumeShaper.h b/media/jni/android_media_VolumeShaper.h
index dbbc4786c0bc..73498a2b44aa 100644
--- a/media/jni/android_media_VolumeShaper.h
+++ b/media/jni/android_media_VolumeShaper.h
@@ -29,9 +29,9 @@ struct VolumeShaperHelper {
jmethodID coConstructId;
jfieldID coTypeId;
jfieldID coIdId;
- jfieldID coInterpolatorTypeId;
jfieldID coOptionFlagsId;
jfieldID coDurationMsId;
+ jfieldID coInterpolatorTypeId;
jfieldID coTimesId;
jfieldID coVolumesId;
@@ -56,12 +56,12 @@ struct VolumeShaperHelper {
if (coClazz == nullptr) {
return;
}
- coConstructId = env->GetMethodID(coClazz, "<init>", "(IIIID[F[F)V");
+ coConstructId = env->GetMethodID(coClazz, "<init>", "(IIIDI[F[F)V");
coTypeId = env->GetFieldID(coClazz, "mType", "I");
coIdId = env->GetFieldID(coClazz, "mId", "I");
- coInterpolatorTypeId = env->GetFieldID(coClazz, "mInterpolatorType", "I");
coOptionFlagsId = env->GetFieldID(coClazz, "mOptionFlags", "I");
coDurationMsId = env->GetFieldID(coClazz, "mDurationMs", "D");
+ coInterpolatorTypeId = env->GetFieldID(coClazz, "mInterpolatorType", "I");
coTimesId = env->GetFieldID(coClazz, "mTimes", "[F");
coVolumesId = env->GetFieldID(coClazz, "mVolumes", "[F");
env->DeleteLocalRef(lclazz);
@@ -108,14 +108,14 @@ struct VolumeShaperHelper {
configuration->setId(
(int)env->GetIntField(jshaper, fields.coIdId));
if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
- configuration->setInterpolatorType(
- (VolumeShaper::Configuration::InterpolatorType)
- env->GetIntField(jshaper, fields.coInterpolatorTypeId));
configuration->setOptionFlags(
(VolumeShaper::Configuration::OptionFlag)
env->GetIntField(jshaper, fields.coOptionFlagsId));
configuration->setDurationMs(
(double)env->GetDoubleField(jshaper, fields.coDurationMsId));
+ configuration->setInterpolatorType(
+ (VolumeShaper::Configuration::InterpolatorType)
+ env->GetIntField(jshaper, fields.coInterpolatorTypeId));
// convert point arrays
jobject xobj = env->GetObjectField(jshaper, fields.coTimesId);
@@ -165,9 +165,9 @@ struct VolumeShaperHelper {
jvalue args[7];
args[0].i = (jint)configuration->getType();
args[1].i = (jint)configuration->getId();
- args[2].i = (jint)configuration->getInterpolatorType();
- args[3].i = (jint)configuration->getOptionFlags();
- args[4].d = (jdouble)configuration->getDurationMs();
+ args[2].i = (jint)configuration->getOptionFlags();
+ args[3].d = (jdouble)configuration->getDurationMs();
+ args[4].i = (jint)configuration->getInterpolatorType();
args[5].l = xarray;
args[6].l = yarray;
jobject jshaper = env->NewObjectA(fields.coClazz, fields.coConstructId, args);
diff --git a/native/android/Android.mk b/native/android/Android.mk
index 57f996cf17ec..6e15331e4baa 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -32,6 +32,7 @@ LOCAL_SHARED_LIBRARIES := \
libbinder \
libui \
libgui \
+ libsensor \
libandroid_runtime \
libnetd_client \
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 1b1f28c37469..c7bed9bc1685 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -169,6 +169,7 @@ LIBANDROID {
ASensorEventQueue_enableSensor;
ASensorEventQueue_getEvents;
ASensorEventQueue_hasEvents;
+ ASensorEventQueue_registerSensor; # introduced=26
ASensorEventQueue_setEventRate;
ASensorManager_configureDirectReport; # introduced=26
ASensorManager_createEventQueue;
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index c7bc885ea4b0..ae16949791bc 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -22,9 +22,9 @@
#include <android/sensor.h>
#include <android/sharedmem.h>
#include <cutils/native_handle.h>
-#include <gui/Sensor.h>
-#include <gui/SensorManager.h>
-#include <gui/SensorEventQueue.h>
+#include <sensor/Sensor.h>
+#include <sensor/SensorManager.h>
+#include <sensor/SensorEventQueue.h>
#include <utils/Looper.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
@@ -41,7 +41,7 @@ using android::String16;
/*****************************************************************************/
#define ERROR_INVALID_PARAMETER(message) ALOGE("%s: " message, __func__)
-// frequently used check
+// frequently used checks
#define RETURN_IF_MANAGER_IS_NULL(retval) do {\
if (manager == nullptr) { \
ERROR_INVALID_PARAMETER("manager cannot be NULL"); \
@@ -54,14 +54,18 @@ using android::String16;
return retval; \
} \
} while (false)
+#define RETURN_IF_QUEUE_IS_NULL(retval) do {\
+ if (queue == nullptr) { \
+ ERROR_INVALID_PARAMETER("queue cannot be NULL"); \
+ return retval; \
+ } \
+ } while (false)
-ASensorManager* ASensorManager_getInstance()
-{
- return ASensorManager_getInstanceForPackage(NULL);
+ASensorManager* ASensorManager_getInstance() {
+ return ASensorManager_getInstanceForPackage(nullptr);
}
-ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName)
-{
+ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName) {
if (packageName) {
return &SensorManager::getInstanceForPackage(String16(packageName));
} else {
@@ -69,9 +73,8 @@ ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName)
}
}
-int ASensorManager_getSensorList(ASensorManager* manager,
- ASensorList* list)
-{
+int ASensorManager_getSensorList(ASensorManager* manager, ASensorList* list) {
+ RETURN_IF_MANAGER_IS_NULL(android::BAD_VALUE);
Sensor const* const* l;
int c = static_cast<SensorManager*>(manager)->getSensorList(&l);
if (list) {
@@ -80,13 +83,13 @@ int ASensorManager_getSensorList(ASensorManager* manager,
return c;
}
-ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type)
-{
+ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type) {
+ RETURN_IF_MANAGER_IS_NULL(nullptr);
return static_cast<SensorManager*>(manager)->getDefaultSensor(type);
}
-ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager,
- int type, bool wakeUp) {
+ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type, bool wakeUp) {
+ RETURN_IF_MANAGER_IS_NULL(nullptr);
Sensor const* const* sensorList;
size_t size = static_cast<SensorManager*>(manager)->getSensorList(&sensorList);
for (size_t i = 0; i < size; ++i) {
@@ -95,12 +98,18 @@ ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager,
return reinterpret_cast<ASensor const *>(sensorList[i]);
}
}
- return NULL;
+ return nullptr;
}
ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager,
- ALooper* looper, int ident, ALooper_callbackFunc callback, void* data)
-{
+ ALooper* looper, int ident, ALooper_callbackFunc callback, void* data) {
+ RETURN_IF_MANAGER_IS_NULL(nullptr);
+
+ if (looper == nullptr) {
+ ERROR_INVALID_PARAMETER("looper cannot be NULL");
+ return nullptr;
+ }
+
sp<SensorEventQueue> queue =
static_cast<SensorManager*>(manager)->createEventQueue();
if (queue != 0) {
@@ -111,17 +120,17 @@ ASensorEventQueue* ASensorManager_createEventQueue(ASensorManager* manager,
return static_cast<ASensorEventQueue*>(queue.get());
}
-int ASensorManager_destroyEventQueue(ASensorManager* manager,
- ASensorEventQueue* inQueue)
-{
- sp<SensorEventQueue> queue = static_cast<SensorEventQueue*>(inQueue);
- ALooper_removeFd(queue->looper, queue->getFd());
- queue->decStrong(manager);
+int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* queue) {
+ RETURN_IF_MANAGER_IS_NULL(android::BAD_VALUE);
+ RETURN_IF_QUEUE_IS_NULL(android::BAD_VALUE);
+
+ sp<SensorEventQueue> q = static_cast<SensorEventQueue*>(queue);
+ ALooper_removeFd(q->looper, q->getFd());
+ q->decStrong(manager);
return 0;
}
-int ASensorManager_createSharedMemoryDirectChannel(
- ASensorManager *manager, int fd, size_t size) {
+int ASensorManager_createSharedMemoryDirectChannel(ASensorManager *manager, int fd, size_t size) {
RETURN_IF_MANAGER_IS_NULL(android::BAD_VALUE);
if (fd < 0) {
@@ -131,6 +140,7 @@ int ASensorManager_createSharedMemoryDirectChannel(
if (size < sizeof(ASensorEvent)) {
ERROR_INVALID_PARAMETER("size has to be greater or equal to sizeof(ASensorEvent).");
+ return android::BAD_VALUE;
}
native_handle_t *resourceHandle = native_handle_create(1 /* nFd */, 0 /* nInt */);
@@ -156,6 +166,7 @@ int ASensorManager_createHardwareBufferDirectChannel(
if (size < sizeof(ASensorEvent)) {
ERROR_INVALID_PARAMETER("size has to be greater or equal to sizeof(ASensorEvent).");
+ return android::BAD_VALUE;
}
const native_handle_t *resourceHandle = AHardwareBuffer_getNativeHandle(buffer);
@@ -195,34 +206,51 @@ int ASensorManager_configureDirectReport(
/*****************************************************************************/
int ASensorEventQueue_registerSensor(ASensorEventQueue* queue, ASensor const* sensor,
- int32_t samplingPeriodUs, int maxBatchReportLatencyUs)
-{
+ int32_t samplingPeriodUs, int64_t maxBatchReportLatencyUs) {
+ RETURN_IF_QUEUE_IS_NULL(android::BAD_VALUE);
+ RETURN_IF_SENSOR_IS_NULL(android::BAD_VALUE);
+ if (samplingPeriodUs < 0 || maxBatchReportLatencyUs < 0) {
+ ERROR_INVALID_PARAMETER("samplingPeriodUs and maxBatchReportLatencyUs cannot be negative");
+ return android::BAD_VALUE;
+ }
+
return static_cast<SensorEventQueue*>(queue)->enableSensor(
static_cast<Sensor const*>(sensor)->getHandle(), samplingPeriodUs,
maxBatchReportLatencyUs, 0);
}
-int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor)
-{
+int ASensorEventQueue_enableSensor(ASensorEventQueue* queue, ASensor const* sensor) {
+ RETURN_IF_QUEUE_IS_NULL(android::BAD_VALUE);
+ RETURN_IF_SENSOR_IS_NULL(android::BAD_VALUE);
+
return static_cast<SensorEventQueue*>(queue)->enableSensor(
static_cast<Sensor const*>(sensor));
}
-int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor)
-{
+int ASensorEventQueue_disableSensor(ASensorEventQueue* queue, ASensor const* sensor) {
+ RETURN_IF_QUEUE_IS_NULL(android::BAD_VALUE);
+ RETURN_IF_SENSOR_IS_NULL(android::BAD_VALUE);
+
return static_cast<SensorEventQueue*>(queue)->disableSensor(
static_cast<Sensor const*>(sensor));
}
-int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor,
- int32_t usec)
-{
+int ASensorEventQueue_setEventRate(ASensorEventQueue* queue, ASensor const* sensor, int32_t usec) {
+ RETURN_IF_QUEUE_IS_NULL(android::BAD_VALUE);
+ RETURN_IF_SENSOR_IS_NULL(android::BAD_VALUE);
+
+ if (usec < 0) {
+ ERROR_INVALID_PARAMETER("usec cannot be negative");
+ return android::BAD_VALUE;
+ }
+
return static_cast<SensorEventQueue*>(queue)->setEventRate(
static_cast<Sensor const*>(sensor), us2ns(usec));
}
-int ASensorEventQueue_hasEvents(ASensorEventQueue* queue)
-{
+int ASensorEventQueue_hasEvents(ASensorEventQueue* queue) {
+ RETURN_IF_QUEUE_IS_NULL(android::BAD_VALUE);
+
struct pollfd pfd;
pfd.fd = static_cast<SensorEventQueue*>(queue)->getFd();
pfd.events = POLLIN;
@@ -239,9 +267,13 @@ int ASensorEventQueue_hasEvents(ASensorEventQueue* queue)
return (nfd == 0) ? 0 : 1;
}
-ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue,
- ASensorEvent* events, size_t count)
-{
+ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count) {
+ RETURN_IF_QUEUE_IS_NULL(android::BAD_VALUE);
+ if (events == nullptr) {
+ ERROR_INVALID_PARAMETER("events cannot be NULL");
+ return android::BAD_VALUE;
+ }
+
ssize_t actual = static_cast<SensorEventQueue*>(queue)->read(events, count);
if (actual > 0) {
static_cast<SensorEventQueue*>(queue)->sendAck(events, actual);
@@ -251,53 +283,53 @@ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue,
/*****************************************************************************/
-const char* ASensor_getName(ASensor const* sensor)
-{
+const char* ASensor_getName(ASensor const* sensor) {
+ RETURN_IF_SENSOR_IS_NULL(nullptr);
return static_cast<Sensor const*>(sensor)->getName().string();
}
-const char* ASensor_getVendor(ASensor const* sensor)
-{
+const char* ASensor_getVendor(ASensor const* sensor) {
+ RETURN_IF_SENSOR_IS_NULL(nullptr);
return static_cast<Sensor const*>(sensor)->getVendor().string();
}
-int ASensor_getType(ASensor const* sensor)
-{
+int ASensor_getType(ASensor const* sensor) {
+ RETURN_IF_SENSOR_IS_NULL(ASENSOR_TYPE_INVALID);
return static_cast<Sensor const*>(sensor)->getType();
}
-float ASensor_getResolution(ASensor const* sensor)
-{
+float ASensor_getResolution(ASensor const* sensor) {
+ RETURN_IF_SENSOR_IS_NULL(ASENSOR_RESOLUTION_INVALID);
return static_cast<Sensor const*>(sensor)->getResolution();
}
-int ASensor_getMinDelay(ASensor const* sensor)
-{
+int ASensor_getMinDelay(ASensor const* sensor) {
+ RETURN_IF_SENSOR_IS_NULL(ASENSOR_DELAY_INVALID);
return static_cast<Sensor const*>(sensor)->getMinDelay();
}
-int ASensor_getFifoMaxEventCount(ASensor const* sensor)
-{
+int ASensor_getFifoMaxEventCount(ASensor const* sensor) {
+ RETURN_IF_SENSOR_IS_NULL(ASENSOR_FIFO_COUNT_INVALID);
return static_cast<Sensor const*>(sensor)->getFifoMaxEventCount();
}
-int ASensor_getFifoReservedEventCount(ASensor const* sensor)
-{
+int ASensor_getFifoReservedEventCount(ASensor const* sensor) {
+ RETURN_IF_SENSOR_IS_NULL(ASENSOR_FIFO_COUNT_INVALID);
return static_cast<Sensor const*>(sensor)->getFifoReservedEventCount();
}
-const char* ASensor_getStringType(ASensor const* sensor)
-{
+const char* ASensor_getStringType(ASensor const* sensor) {
+ RETURN_IF_SENSOR_IS_NULL(nullptr);
return static_cast<Sensor const*>(sensor)->getStringType().string();
}
-int ASensor_getReportingMode(ASensor const* sensor)
-{
+int ASensor_getReportingMode(ASensor const* sensor) {
+ RETURN_IF_SENSOR_IS_NULL(AREPORTING_MODE_INVALID);
return static_cast<Sensor const*>(sensor)->getReportingMode();
}
-bool ASensor_isWakeUpSensor(ASensor const* sensor)
-{
+bool ASensor_isWakeUpSensor(ASensor const* sensor) {
+ RETURN_IF_SENSOR_IS_NULL(false);
return static_cast<Sensor const*>(sensor)->isWakeUpSensor();
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 25127efad502..12bab18c88c9 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -92,7 +92,7 @@ public class DeviceChooserActivity extends Activity {
try {
final PackageManager packageManager = getPackageManager();
return packageManager.getApplicationLabel(
- packageManager.getApplicationInfo(getService().mCallingPackage, 0));
+ packageManager.getApplicationInfo(getCallingPackage(), 0));
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
@@ -128,7 +128,7 @@ public class DeviceChooserActivity extends Activity {
}
protected void onPairTapped(BluetoothDevice selectedDevice) {
- getService().onDeviceSelected();
+ getService().onDeviceSelected(getCallingPackage(), selectedDevice.getAddress());
setResult(RESULT_OK,
new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice));
finish();
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 11c722d7676c..f0f910848943 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -33,6 +33,7 @@ import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.companion.AssociationRequest;
import android.companion.BluetoothLEDeviceFilter;
+import android.companion.CompanionDeviceManager;
import android.companion.ICompanionDeviceDiscoveryService;
import android.companion.ICompanionDeviceDiscoveryServiceCallback;
import android.companion.IFindDeviceCallback;
@@ -71,7 +72,6 @@ public class DeviceDiscoveryService extends Service {
DevicesAdapter mDevicesAdapter;
IFindDeviceCallback mFindCallback;
ICompanionDeviceDiscoveryServiceCallback mServiceCallback;
- String mCallingPackage;
private final ICompanionDeviceDiscoveryService mBinder =
new ICompanionDeviceDiscoveryService.Stub() {
@@ -88,7 +88,6 @@ public class DeviceDiscoveryService extends Service {
}
mFindCallback = findCallback;
mServiceCallback = serviceCallback;
- mCallingPackage = callingPackage;
DeviceDiscoveryService.this.startDiscovery(request);
}
};
@@ -174,14 +173,6 @@ public class DeviceDiscoveryService extends Service {
return super.onUnbind(intent);
}
- public void onDeviceSelected() {
- try {
- mServiceCallback.onDeviceSelected(mCallingPackage, getUserId());
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Error reporting selected device");
- }
- }
-
private void stopScan() {
if (DEBUG) Log.i(LOG_TAG, "stopScan() called");
mBluetoothAdapter.cancelDiscovery();
@@ -234,6 +225,17 @@ public class DeviceDiscoveryService extends Service {
}
}
+ void onDeviceSelected(String callingPackage, String deviceAddress) {
+ try {
+ mServiceCallback.onDeviceSelected(
+ //TODO is this the right userId?
+ callingPackage, getUserId(), deviceAddress);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Failed to record association: "
+ + callingPackage + " <-> " + deviceAddress);
+ }
+ }
+
class DevicesAdapter extends ArrayAdapter<BluetoothDevice> {
private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth);
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index fda3914d1fc0..8f7efb59d8c4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -19,6 +19,8 @@ package com.android.settingslib.applications;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.Application;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -92,6 +94,7 @@ public class ApplicationsState {
final PackageManager mPm;
final IPackageManager mIpm;
final UserManager mUm;
+ final StorageStatsManager mStats;
final int mAdminRetrieveFlags;
final int mRetrieveFlags;
PackageIntentReceiver mPackageIntentReceiver;
@@ -111,6 +114,7 @@ public class ApplicationsState {
final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
long mCurId = 1;
+ String mCurComputingSizeUuid;
String mCurComputingSizePkg;
int mCurComputingSizeUserId;
boolean mSessionsChanged;
@@ -126,7 +130,8 @@ public class ApplicationsState {
mContext = app;
mPm = mContext.getPackageManager();
mIpm = AppGlobals.getPackageManager();
- mUm = (UserManager) app.getSystemService(Context.USER_SERVICE);
+ mUm = mContext.getSystemService(UserManager.class);
+ mStats = mContext.getSystemService(StorageStatsManager.class);
for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
mEntriesMap.put(userId, new HashMap<String, AppEntry>());
}
@@ -328,7 +333,18 @@ public class ApplicationsState {
synchronized (mEntriesMap) {
AppEntry entry = mEntriesMap.get(userId).get(packageName);
if (entry != null) {
- mPm.getPackageSizeInfoAsUser(packageName, userId, mBackgroundHandler.mStatsObserver);
+ mBackgroundHandler.post(() -> {
+ final StorageStats stats = mStats.queryStatsForPackage(entry.info.volumeUuid,
+ packageName, UserHandle.of(userId));
+ final PackageStats legacyStats = new PackageStats(packageName, userId);
+ legacyStats.codeSize = stats.getCodeBytes();
+ legacyStats.dataSize = stats.getDataBytes();
+ legacyStats.cacheSize = stats.getCacheBytes();
+ try {
+ mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacyStats, true);
+ } catch (RemoteException ignored) {
+ }
+ });
}
if (DEBUG_LOCKING) Log.v(TAG, "...requestSize releasing lock");
}
@@ -958,10 +974,24 @@ public class ApplicationsState {
mMainHandler.sendMessage(m);
}
entry.sizeLoadStart = now;
+ mCurComputingSizeUuid = entry.info.volumeUuid;
mCurComputingSizePkg = entry.info.packageName;
mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
- mPm.getPackageSizeInfoAsUser(mCurComputingSizePkg,
- mCurComputingSizeUserId, mStatsObserver);
+
+ mBackgroundHandler.post(() -> {
+ final StorageStats stats = mStats.queryStatsForPackage(
+ mCurComputingSizeUuid, mCurComputingSizePkg,
+ UserHandle.of(mCurComputingSizeUserId));
+ final PackageStats legacyStats = new PackageStats(
+ mCurComputingSizePkg, mCurComputingSizeUserId);
+ legacyStats.codeSize = stats.getCodeBytes();
+ legacyStats.dataSize = stats.getDataBytes();
+ legacyStats.cacheSize = stats.getCacheBytes();
+ try {
+ mStatsObserver.onGetStatsCompleted(legacyStats, true);
+ } catch (RemoteException ignored) {
+ }
+ });
}
if (DEBUG_LOCKING) Log.v(TAG, "MSG_LOAD_SIZES releasing: now computing");
return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index e520319c1eb7..953dda29d7c4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -16,22 +16,14 @@
package com.android.settingslib.deviceinfo;
-import android.app.ActivityManager;
-import android.content.ComponentName;
+import android.app.usage.ExternalStorageStats;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageStatsObserver;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageStats;
import android.content.pm.UserInfo;
+import android.os.AsyncTask;
import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageVolume;
@@ -40,93 +32,54 @@ import android.util.Log;
import android.util.SparseArray;
import android.util.SparseLongArray;
-import com.android.internal.app.IMediaContainerService;
-import com.android.internal.util.ArrayUtils;
-import com.google.android.collect.Sets;
-
-import java.io.File;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.Objects;
-import java.util.Set;
/**
* Utility for measuring the disk usage of internal storage or a physical
- * {@link StorageVolume}. Connects with a remote {@link IMediaContainerService}
- * and delivers results to {@link MeasurementReceiver}.
+ * {@link StorageVolume}.
*/
public class StorageMeasurement {
private static final String TAG = "StorageMeasurement";
- private static final boolean LOCAL_LOGV = false;
- static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
-
- private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
-
- public static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
- DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService");
-
- /** Media types to measure on external storage. */
- private static final Set<String> sMeasureMediaTypes = Sets.newHashSet(
- Environment.DIRECTORY_DCIM, Environment.DIRECTORY_MOVIES,
- Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_MUSIC,
- Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
- Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS,
- Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID);
-
public static class MeasurementDetails {
+ /** Size of storage device. */
public long totalSize;
+ /** Size of available space. */
public long availSize;
+ /** Size of all cached data. */
+ public long cacheSize;
/**
- * Total apps disk usage per profiles of the current user.
- * <p>
- * When measuring internal storage, this value includes the code size of
- * all apps (regardless of install status for the given profile), and
- * internal disk used by the profile's apps. When the device
- * emulates external storage, this value also includes emulated storage
- * used by the profile's apps.
- * <p>
- * When measuring a physical {@link StorageVolume}, this value includes
- * usage by all apps on that volume and only for the primary profile.
+ * Total disk space used by everything.
* <p>
* Key is {@link UserHandle}.
*/
- public SparseLongArray appsSize = new SparseLongArray();
+ public SparseLongArray usersSize = new SparseLongArray();
/**
- * Total cache disk usage by apps (over all users and profiles).
+ * Total disk space used by apps.
+ * <p>
+ * Key is {@link UserHandle}.
*/
- public long cacheSize;
+ public SparseLongArray appsSize = new SparseLongArray();
/**
- * Total media disk usage, categorized by types such as
- * {@link Environment#DIRECTORY_MUSIC} for every user profile of the current user.
- * <p>
- * When measuring internal storage, this reflects media on emulated
- * storage for the respective profile.
+ * Total disk space used by media on shared storage.
* <p>
- * When measuring a physical {@link StorageVolume}, this reflects media
- * on that volume.
- * <p>
- * Key of the {@link SparseArray} is {@link UserHandle}.
+ * First key is {@link UserHandle}. Second key is media type, such as
+ * {@link Environment#DIRECTORY_PICTURES}.
*/
public SparseArray<HashMap<String, Long>> mediaSize = new SparseArray<>();
/**
- * Misc external disk usage for the current user's profiles, unaccounted in
- * {@link #mediaSize}. Key is {@link UserHandle}.
+ * Total disk space used by non-media on shared storage.
+ * <p>
+ * Key is {@link UserHandle}.
*/
public SparseLongArray miscSize = new SparseLongArray();
- /**
- * Total disk usage for users, which is only meaningful for emulated
- * internal storage. Key is {@link UserHandle}.
- */
- public SparseLongArray usersSize = new SparseLongArray();
-
@Override
public String toString() {
return "MeasurementDetails: [totalSize: " + totalSize + " availSize: " + availSize
@@ -142,25 +95,19 @@ public class StorageMeasurement {
private WeakReference<MeasurementReceiver> mReceiver;
private final Context mContext;
+ private final UserManager mUser;
+ private final StorageStatsManager mStats;
private final VolumeInfo mVolume;
private final VolumeInfo mSharedVolume;
- private final MainHandler mMainHandler;
- private final MeasurementHandler mMeasurementHandler;
-
public StorageMeasurement(Context context, VolumeInfo volume, VolumeInfo sharedVolume) {
mContext = context.getApplicationContext();
+ mUser = mContext.getSystemService(UserManager.class);
+ mStats = mContext.getSystemService(StorageStatsManager.class);
mVolume = volume;
mSharedVolume = sharedVolume;
-
- // Start the thread that will measure the disk usage.
- final HandlerThread handlerThread = new HandlerThread("MemoryMeasurement");
- handlerThread.start();
-
- mMainHandler = new MainHandler();
- mMeasurementHandler = new MeasurementHandler(handlerThread.getLooper());
}
public void setReceiver(MeasurementReceiver receiver) {
@@ -170,315 +117,94 @@ public class StorageMeasurement {
}
public void forceMeasure() {
- invalidate();
measure();
}
public void measure() {
- if (!mMeasurementHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
- mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
- }
+ new MeasureTask().execute();
}
public void onDestroy() {
mReceiver = null;
- mMeasurementHandler.removeMessages(MeasurementHandler.MSG_MEASURE);
- mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_DISCONNECT);
}
- private void invalidate() {
- mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE);
- }
-
- private static class StatsObserver extends IPackageStatsObserver.Stub {
- private final boolean mIsPrivate;
- private final MeasurementDetails mDetails;
- private final int mCurrentUser;
- private final Message mFinished;
-
- private int mRemaining;
-
- public StatsObserver(boolean isPrivate, MeasurementDetails details, int currentUser,
- List<UserInfo> profiles, Message finished, int remaining) {
- mIsPrivate = isPrivate;
- mDetails = details;
- mCurrentUser = currentUser;
- if (isPrivate) {
- // Add the profile ids as keys to detail's app sizes.
- for (UserInfo userInfo : profiles) {
- mDetails.appsSize.put(userInfo.id, 0);
- }
- }
- mFinished = finished;
- mRemaining = remaining;
- }
-
+ private class MeasureTask extends AsyncTask<Void, Void, MeasurementDetails> {
@Override
- public void onGetStatsCompleted(PackageStats stats, boolean succeeded) {
- synchronized (mDetails) {
- if (succeeded) {
- addStatsLocked(stats);
- }
- if (--mRemaining == 0) {
- mFinished.sendToTarget();
- }
- }
+ protected MeasurementDetails doInBackground(Void... params) {
+ return measureExactStorage();
}
- private void addStatsLocked(PackageStats stats) {
- if (mIsPrivate) {
- long codeSize = stats.codeSize;
- long dataSize = stats.dataSize;
- long cacheSize = stats.cacheSize;
- if (Environment.isExternalStorageEmulated()) {
- // Include emulated storage when measuring internal. OBB is
- // shared on emulated storage, so treat as code.
- codeSize += stats.externalCodeSize + stats.externalObbSize;
- dataSize += stats.externalDataSize + stats.externalMediaSize;
- cacheSize += stats.externalCacheSize;
- }
-
- // Count code and data for current user's profiles (keys prepared in constructor)
- addValueIfKeyExists(mDetails.appsSize, stats.userHandle, codeSize + dataSize);
-
- // User summary only includes data (code is only counted once
- // for the current user)
- addValue(mDetails.usersSize, stats.userHandle, dataSize);
-
- // Include cache for all users
- mDetails.cacheSize += cacheSize;
-
- } else {
- // Physical storage; only count external sizes
- addValue(mDetails.appsSize, mCurrentUser,
- stats.externalCodeSize + stats.externalDataSize
- + stats.externalMediaSize + stats.externalObbSize);
- mDetails.cacheSize += stats.externalCacheSize;
- }
- }
- }
-
- private class MainHandler extends Handler {
@Override
- public void handleMessage(Message msg) {
- final MeasurementDetails details = (MeasurementDetails) msg.obj;
+ protected void onPostExecute(MeasurementDetails result) {
final MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
if (receiver != null) {
- receiver.onDetailsChanged(details);
+ receiver.onDetailsChanged(result);
}
}
}
- private class MeasurementHandler extends Handler {
- public static final int MSG_MEASURE = 1;
- public static final int MSG_CONNECTED = 2;
- public static final int MSG_DISCONNECT = 3;
- public static final int MSG_COMPLETED = 4;
- public static final int MSG_INVALIDATE = 5;
-
- private Object mLock = new Object();
+ private MeasurementDetails measureExactStorage() {
+ final List<UserInfo> users = mUser.getUsers();
- private IMediaContainerService mDefaultContainer;
+ final long start = SystemClock.elapsedRealtime();
- private volatile boolean mBound = false;
-
- private MeasurementDetails mCached;
-
- private final ServiceConnection mDefContainerConn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- final IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(
- service);
- mDefaultContainer = imcs;
- mBound = true;
- sendMessage(obtainMessage(MSG_CONNECTED, imcs));
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mBound = false;
- removeMessages(MSG_CONNECTED);
- }
- };
-
- public MeasurementHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_MEASURE: {
- if (mCached != null) {
- mMainHandler.obtainMessage(0, mCached).sendToTarget();
- break;
- }
-
- synchronized (mLock) {
- if (mBound) {
- removeMessages(MSG_DISCONNECT);
- sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
- } else {
- Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
- mContext.bindServiceAsUser(service, mDefContainerConn,
- Context.BIND_AUTO_CREATE, UserHandle.SYSTEM);
- }
- }
- break;
- }
- case MSG_CONNECTED: {
- final IMediaContainerService imcs = (IMediaContainerService) msg.obj;
- measureExactStorage(imcs);
- break;
- }
- case MSG_DISCONNECT: {
- synchronized (mLock) {
- if (mBound) {
- mBound = false;
- mContext.unbindService(mDefContainerConn);
- }
- }
- break;
- }
- case MSG_COMPLETED: {
- mCached = (MeasurementDetails) msg.obj;
- mMainHandler.obtainMessage(0, mCached).sendToTarget();
- break;
- }
- case MSG_INVALIDATE: {
- mCached = null;
- break;
- }
- }
- }
- }
+ final MeasurementDetails details = new MeasurementDetails();
+ if (mVolume == null) return details;
- private void measureExactStorage(IMediaContainerService imcs) {
- final UserManager userManager = mContext.getSystemService(UserManager.class);
- final PackageManager packageManager = mContext.getPackageManager();
+ details.totalSize = mStats.getTotalBytes(mVolume.fsUuid);
+ details.availSize = mStats.getFreeBytes(mVolume.fsUuid);
- final List<UserInfo> users = userManager.getUsers();
- final List<UserInfo> currentProfiles = userManager.getEnabledProfiles(
- ActivityManager.getCurrentUser());
+ final long finishTotal = SystemClock.elapsedRealtime();
+ Log.d(TAG, "Measured total storage in " + (finishTotal - start) + "ms");
- final MeasurementDetails details = new MeasurementDetails();
- final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED,
- details);
+ if (mSharedVolume != null && mSharedVolume.isMountedReadable()) {
+ for (UserInfo user : users) {
+ final HashMap<String, Long> mediaMap = new HashMap<>();
+ details.mediaSize.put(user.id, mediaMap);
- if (mVolume == null || !mVolume.isMountedReadable()) {
- finished.sendToTarget();
- return;
- }
+ final ExternalStorageStats stats = mStats
+ .queryExternalStatsForUser(mSharedVolume.fsUuid, UserHandle.of(user.id));
- if (mSharedVolume != null && mSharedVolume.isMountedReadable()) {
- for (UserInfo currentUserInfo : currentProfiles) {
- final int userId = currentUserInfo.id;
- final File basePath = mSharedVolume.getPathForUser(userId);
- HashMap<String, Long> mediaMap = new HashMap<>(sMeasureMediaTypes.size());
- details.mediaSize.put(userId, mediaMap);
-
- // Measure media types for emulated storage, or for primary physical
- // external volume
- for (String type : sMeasureMediaTypes) {
- final File path = new File(basePath, type);
- final long size = getDirectorySize(imcs, path);
- mediaMap.put(type, size);
- }
+ addValue(details.usersSize, user.id, stats.getTotalBytes());
- // Measure misc files not counted under media
- addValue(details.miscSize, userId, measureMisc(imcs, basePath));
- }
+ // Track detailed data types
+ mediaMap.put(Environment.DIRECTORY_MUSIC, stats.getAudioBytes());
+ mediaMap.put(Environment.DIRECTORY_MOVIES, stats.getVideoBytes());
+ mediaMap.put(Environment.DIRECTORY_PICTURES, stats.getImageBytes());
- if (mSharedVolume.getType() == VolumeInfo.TYPE_EMULATED) {
- // Measure total emulated storage of all users; internal apps data
- // will be spliced in later
- for (UserInfo user : users) {
- final File userPath = mSharedVolume.getPathForUser(user.id);
- final long size = getDirectorySize(imcs, userPath);
- addValue(details.usersSize, user.id, size);
- }
+ final long miscBytes = stats.getTotalBytes() - stats.getAudioBytes()
+ - stats.getVideoBytes() - stats.getImageBytes();
+ addValue(details.miscSize, user.id, miscBytes);
}
}
- final File file = mVolume.getPath();
- if (file != null) {
- details.totalSize = file.getTotalSpace();
- details.availSize = file.getFreeSpace();
- }
+ final long finishShared = SystemClock.elapsedRealtime();
+ Log.d(TAG, "Measured shared storage in " + (finishShared - finishTotal) + "ms");
- // Measure all apps hosted on this volume for all users
- if (mVolume.getType() == VolumeInfo.TYPE_PRIVATE) {
- final List<ApplicationInfo> apps = packageManager.getInstalledApplications(
- PackageManager.MATCH_ANY_USER
- | PackageManager.MATCH_DISABLED_COMPONENTS);
+ if ((mVolume.getType() == VolumeInfo.TYPE_PRIVATE) && mVolume.isMountedReadable()) {
+ for (UserInfo user : users) {
+ final StorageStats stats = mStats.queryStatsForUser(mVolume.fsUuid,
+ UserHandle.of(user.id));
- final List<ApplicationInfo> volumeApps = new ArrayList<>();
- for (ApplicationInfo app : apps) {
- if (Objects.equals(app.volumeUuid, mVolume.getFsUuid())) {
- volumeApps.add(app);
+ // Only count code once against current user
+ if (user.id == UserHandle.myUserId()) {
+ addValue(details.usersSize, user.id, stats.getCodeBytes());
}
- }
- final int count = users.size() * volumeApps.size();
- if (count == 0) {
- finished.sendToTarget();
- return;
- }
+ addValue(details.usersSize, user.id, stats.getDataBytes());
+ addValue(details.appsSize, user.id, stats.getCodeBytes() + stats.getDataBytes());
- final StatsObserver observer = new StatsObserver(true, details,
- ActivityManager.getCurrentUser(), currentProfiles, finished, count);
- for (UserInfo user : users) {
- for (ApplicationInfo app : volumeApps) {
- packageManager.getPackageSizeInfoAsUser(app.packageName, user.id, observer);
- }
+ details.cacheSize += stats.getCacheBytes();
}
-
- } else {
- finished.sendToTarget();
- return;
}
- }
-
- private static long getDirectorySize(IMediaContainerService imcs, File path) {
- try {
- final long size = imcs.calculateDirectorySize(path.toString());
- if (LOGV) Log.v(TAG, "getDirectorySize(" + path + ") returned " + size);
- return size;
- } catch (Exception e) {
- Log.w(TAG, "Could not read memory from default container service for " + path, e);
- return 0;
- }
- }
-
- private long measureMisc(IMediaContainerService imcs, File dir) {
- final File[] files = dir.listFiles();
- if (ArrayUtils.isEmpty(files)) return 0;
- // Get sizes of all top level nodes except the ones already computed
- long miscSize = 0;
- for (File file : files) {
- final String name = file.getName();
- if (sMeasureMediaTypes.contains(name)) {
- continue;
- }
+ final long finishPrivate = SystemClock.elapsedRealtime();
+ Log.d(TAG, "Measured private storage in " + (finishPrivate - finishShared) + "ms");
- if (file.isFile()) {
- miscSize += file.length();
- } else if (file.isDirectory()) {
- miscSize += getDirectorySize(imcs, file);
- }
- }
- return miscSize;
+ return details;
}
private static void addValue(SparseLongArray array, int key, long value) {
array.put(key, array.get(key) + value);
}
-
- private static void addValueIfKeyExists(SparseLongArray array, int key, long value) {
- final int index = array.indexOfKey(key);
- if (index >= 0) {
- array.put(key, array.valueAt(index) + value);
- }
- }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index de79d3f98c32..5f4b2391a763 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -553,23 +553,23 @@ final class SettingsState {
}
private void doWriteState() {
+ boolean wroteState = false;
+ final int version;
+ final ArrayMap<String, Setting> settings;
+
+ synchronized (mLock) {
+ version = mVersion;
+ settings = new ArrayMap<>(mSettings);
+ mDirty = false;
+ mWriteScheduled = false;
+ }
+
synchronized (mWriteLock) {
if (DEBUG_PERSISTENCE) {
Slog.i(LOG_TAG, "[PERSIST START]");
}
AtomicFile destination = new AtomicFile(mStatePersistFile);
-
- final int version;
- final ArrayMap<String, Setting> settings;
-
- synchronized (mLock) {
- version = mVersion;
- settings = new ArrayMap<>(mSettings);
- mDirty = false;
- mWriteScheduled = false;
- }
-
FileOutputStream out = null;
try {
out = destination.startWrite();
@@ -600,9 +600,7 @@ final class SettingsState {
serializer.endDocument();
destination.finishWrite(out);
- synchronized (mLock) {
- addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null);
- }
+ wroteState = true;
if (DEBUG_PERSISTENCE) {
Slog.i(LOG_TAG, "[PERSIST END]");
@@ -614,6 +612,12 @@ final class SettingsState {
IoUtils.closeQuietly(out);
}
}
+
+ if (wroteState) {
+ synchronized (mLock) {
+ addHistoricalOperationLocked(HISTORICAL_OPERATION_PERSIST, null);
+ }
+ }
}
static void writeSingleSetting(int version, XmlSerializer serializer, String id,
diff --git a/packages/SystemUI/res/drawable/pip_expand.xml b/packages/SystemUI/res/drawable/pip_expand.xml
new file mode 100644
index 000000000000..e34a95d257ac
--- /dev/null
+++ b/packages/SystemUI/res/drawable/pip_expand.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="60dp"
+ android:height="60dp"
+ android:viewportWidth="60"
+ android:viewportHeight="60">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M8,7.5v45c0,2.75,2.25,5,5,5h34.05c2.75,0,4.95-2.25,4.95-5v-45c0-2.75-2.2-5-4.95-5H13
+C10.25,2.5,8,4.75,8,7.5z
+M13,6.5h34c0.55,0,1,0.45,1,1v45c0,0.55-0.45,1-1,1H13c-0.55,0-1-0.45-1-1v-45C12,6.95,12.45,6.5,13,6.5z" />
+ <path
+ android:pathData="M60,0L0,0l0,60h60V0z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M20.86,35h-2c-0.55,0-1,0.45-1,1v10.5c0,0.55,0.45,1,1,1h10.5c0.55,0,1-0.45,1-1v-2c0-0.55-0.45-1-1-1h-7.5
+V36C21.86,35.45,21.41,35,20.86,35z" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M29.64,13.5v2c0,0.55,0.45,1,1,1h7.5V24c0,0.55,0.45,1,1,1h2c0.55,0,1-0.45,1-1V13.5c0-0.55-0.45-1-1-1
+h-10.5C30.09,12.5,29.64,12.95,29.64,13.5z" />
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
index 0f5ca9bdf2fc..f38c8ff5da0c 100644
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ b/packages/SystemUI/res/layout/pip_menu_activity.xml
@@ -32,19 +32,32 @@
android:background="?android:selectableItemBackgroundBorderless" />
<FrameLayout
+ android:id="@+id/expand_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:layout_width="64dp"
+ android:layout_height="64dp"
+ android:layout_gravity="center"
+ android:contentDescription="@string/pip_phone_expand"
+ android:src="@drawable/pip_expand"
+ android:background="?android:selectableItemBackgroundBorderless" />
+ </FrameLayout>
+
+ <FrameLayout
android:id="@+id/actions_container"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom"
- android:paddingStart="24dp"
- android:paddingEnd="24dp"
android:background="#66000000"
android:visibility="invisible">
<LinearLayout
- android:id="@+id/actions"
+ android:id="@+id/actions_group"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
- android:orientation="horizontal" />
+ android:orientation="horizontal"
+ android:divider="@android:color/transparent"
+ android:showDividers="middle" />
</FrameLayout>
</FrameLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3512761afbd2..f331d8782a2d 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -731,6 +731,13 @@
<!-- The size of the PIP drag-to-dismiss target. -->
<dimen name="pip_dismiss_target_size">48dp</dimen>
+ <!-- The shortest-edge size of the expanded PiP. -->
+ <dimen name="pip_expanded_shortest_edge_size">160dp</dimen>
+
+ <!-- The padding between actions in the PiP in landscape Note that the PiP does not reflect
+ the configuration of the device, so we can't use -land resources. -->
+ <dimen name="pip_between_action_padding_land">8dp</dimen>
+
<dimen name="default_gear_space">18dp</dimen>
<dimen name="cell_overlay_padding">18dp</dimen>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 41135f57ed87..bc3edd58f252 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -133,7 +133,7 @@
<com.android.systemui.tuner.TunerSwitch
android:key="doze_sensors_wake_up_fully"
android:title="@string/tuner_doze_sensors_wake_up_fully"
- sysui:defValue="false" />
+ sysui:defValue="true" />
</PreferenceScreen>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
new file mode 100644
index 000000000000..7a1849ed741e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
@@ -0,0 +1,123 @@
+/*
+ * 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.systemui.pip.phone;
+
+import static android.view.WindowManager.INPUT_CONSUMER_PIP;
+
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.IWindowManager;
+import android.view.MotionEvent;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages the input consumer that allows the SystemUI to control the PiP.
+ */
+public class InputConsumerController {
+
+ private static final String TAG = InputConsumerController.class.getSimpleName();
+
+ /**
+ * Listener interface for callers to subscribe to touch events.
+ */
+ public interface TouchListener {
+ boolean onTouchEvent(MotionEvent ev);
+ }
+
+ /**
+ * Input handler used for the PiP input consumer.
+ */
+ private final class PipInputEventReceiver extends InputEventReceiver {
+
+ public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ boolean handled = true;
+ try {
+ // To be implemented for input handling over Pip windows
+ if (mListener != null && event instanceof MotionEvent) {
+ MotionEvent ev = (MotionEvent) event;
+ handled = mListener.onTouchEvent(ev);
+ }
+ } finally {
+ finishInputEvent(event, handled);
+ }
+ }
+ }
+
+ private IWindowManager mWindowManager;
+
+ private PipInputEventReceiver mInputEventReceiver;
+ private TouchListener mListener;
+
+ public InputConsumerController(IWindowManager windowManager) {
+ mWindowManager = windowManager;
+ registerInputConsumer();
+ }
+
+ /**
+ * Sets the touch listener.
+ */
+ public void setTouchListener(TouchListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Registers the input consumer.
+ */
+ public void registerInputConsumer() {
+ if (mInputEventReceiver == null) {
+ final InputChannel inputChannel = new InputChannel();
+ try {
+ mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+ mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to create PIP input consumer", e);
+ }
+ mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
+ }
+ }
+
+ /**
+ * Unregisters the input consumer.
+ */
+ public void unregisterInputConsumer() {
+ if (mInputEventReceiver != null) {
+ try {
+ mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to destroy PIP input consumer", e);
+ }
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + "registered=" + (mInputEventReceiver != null));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 714b263e40cc..ecc2fadf5ae2 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -53,6 +53,7 @@ public class PipManager implements BasePipManager {
private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
+ private InputConsumerController mInputConsumerController;
private PipMenuActivityController mMenuController;
private PipMediaController mMediaController;
private PipTouchHandler mTouchHandler;
@@ -68,6 +69,7 @@ public class PipManager implements BasePipManager {
}
mTouchHandler.onActivityPinned();
mMediaController.onActivityPinned();
+ mMenuController.onActivityPinned();
}
@Override
@@ -120,9 +122,10 @@ public class PipManager implements BasePipManager {
@Override
public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- boolean fromImeAdjustement) {
+ Rect animatingBounds, boolean fromImeAdjustement) {
mHandler.post(() -> {
- mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, fromImeAdjustement);
+ mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, animatingBounds,
+ fromImeAdjustement);
});
}
@@ -151,11 +154,12 @@ public class PipManager implements BasePipManager {
}
SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
+ mInputConsumerController = new InputConsumerController(mWindowManager);
mMediaController = new PipMediaController(context, mActivityManager);
- mMenuController = new PipMenuActivityController(context, mActivityManager, mWindowManager,
- mMediaController);
- mTouchHandler = new PipTouchHandler(context, mMenuController, mActivityManager,
- mWindowManager);
+ mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
+ mInputConsumerController);
+ mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController,
+ mInputConsumerController);
}
/**
@@ -178,6 +182,7 @@ public class PipManager implements BasePipManager {
public void dump(PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
+ mInputConsumerController.dump(pw, innerPrefix);
mMenuController.dump(pw, innerPrefix);
mTouchHandler.dump(pw, innerPrefix);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 4e28061dc21b..65de22e21775 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -40,8 +40,9 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager.LayoutParams;
+import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.TextView;
+import android.widget.LinearLayout;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -57,8 +58,9 @@ public class PipMenuActivity extends Activity {
private static final String TAG = "PipMenuActivity";
public static final int MESSAGE_SHOW_MENU = 1;
- public static final int MESSAGE_HIDE_MENU = 2;
- public static final int MESSAGE_UPDATE_ACTIONS = 3;
+ public static final int MESSAGE_POKE_MENU = 2;
+ public static final int MESSAGE_HIDE_MENU = 3;
+ public static final int MESSAGE_UPDATE_ACTIONS = 4;
private static final long INITIAL_DISMISS_DELAY = 2000;
private static final long POST_INTERACTION_DISMISS_DELAY = 1500;
@@ -67,7 +69,9 @@ public class PipMenuActivity extends Activity {
private boolean mMenuVisible;
private final List<RemoteAction> mActions = new ArrayList<>();
private View mMenuContainer;
+ private LinearLayout mActionsGroup;
private View mDismissButton;
+ private int mBetweenActionPaddingLand;
private ObjectAnimator mMenuContainerAnimator;
@@ -83,6 +87,9 @@ public class PipMenuActivity extends Activity {
case MESSAGE_SHOW_MENU:
showMenu();
break;
+ case MESSAGE_POKE_MENU:
+ cancelDelayedFinish();
+ break;
case MESSAGE_HIDE_MENU:
hideMenu();
break;
@@ -127,6 +134,9 @@ public class PipMenuActivity extends Activity {
mDismissButton.setOnClickListener((v) -> {
dismissPip();
});
+ mActionsGroup = (LinearLayout) findViewById(R.id.actions_group);
+ mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
+ R.dimen.pip_between_action_padding_land);
notifyActivityCallback(mMessenger);
showMenu();
@@ -139,6 +149,11 @@ public class PipMenuActivity extends Activity {
}
@Override
+ public void onUserInteraction() {
+ repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
+ }
+
+ @Override
protected void onUserLeaveHint() {
super.onUserLeaveHint();
@@ -164,11 +179,6 @@ public class PipMenuActivity extends Activity {
}
@Override
- public void onUserInteraction() {
- repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
- }
-
- @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// On the first action outside the window, hide the menu
switch (ev.getAction()) {
@@ -177,13 +187,16 @@ public class PipMenuActivity extends Activity {
break;
case MotionEvent.ACTION_DOWN:
mDownPosition.set(ev.getX(), ev.getY());
+ mDownDelta.set(0f, 0f);
break;
case MotionEvent.ACTION_MOVE:
mDownDelta.set(ev.getX() - mDownPosition.x, ev.getY() - mDownPosition.y);
if (mDownDelta.length() > mViewConfig.getScaledTouchSlop() && mMenuVisible) {
- hideMenu();
- mMenuVisible = false;
+ // Restore the input consumer and let that drive the movement of this menu
+ notifyRegisterInputConsumer();
+ cancelDelayedFinish();
}
+ break;
}
return super.dispatchTouchEvent(ev);
}
@@ -219,17 +232,21 @@ public class PipMenuActivity extends Activity {
}
});
mMenuContainerAnimator.start();
+ } else {
+ repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
}
}
private void hideMenu() {
- hideMenu(null /* animationFinishedRunnable */);
+ hideMenu(null /* animationFinishedRunnable */, true /* notifyMenuVisibility */);
}
- private void hideMenu(final Runnable animationFinishedRunnable) {
+ private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility) {
if (mMenuVisible) {
cancelDelayedFinish();
- notifyMenuVisibility(false);
+ if (notifyMenuVisibility) {
+ notifyMenuVisibility(false);
+ }
mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
mMenuContainer.getAlpha(), 0f);
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
@@ -253,26 +270,30 @@ public class PipMenuActivity extends Activity {
}
private void updateActionViews() {
+ ViewGroup expandContainer = (ViewGroup) findViewById(R.id.expand_container);
ViewGroup actionsContainer = (ViewGroup) findViewById(R.id.actions_container);
actionsContainer.setOnTouchListener((v, ev) -> {
// Do nothing, prevent click through to parent
return true;
});
+ int actionsContainerHeight = 0;
if (mActions.isEmpty()) {
actionsContainer.setVisibility(View.INVISIBLE);
} else {
actionsContainer.setVisibility(View.VISIBLE);
- ViewGroup actionsGroup = (ViewGroup) findViewById(R.id.actions);
- if (actionsGroup != null) {
- actionsGroup.removeAllViews();
+ if (mActionsGroup != null) {
+ mActionsGroup.removeAllViews();
// Recreate the layout
+ final View decorView = getWindow().getDecorView();
+ final boolean isLandscapePip = decorView.getMeasuredWidth()
+ > decorView.getMeasuredHeight();
final LayoutInflater inflater = LayoutInflater.from(this);
for (int i = 0; i < mActions.size(); i++) {
final RemoteAction action = mActions.get(i);
final ImageView actionView = (ImageView) inflater.inflate(
- R.layout.pip_menu_action, actionsGroup, false);
+ R.layout.pip_menu_action, mActionsGroup, false);
action.getIcon().loadDrawableAsync(this, d -> {
d.setTint(Color.WHITE);
actionView.setImageDrawable(d);
@@ -285,10 +306,27 @@ public class PipMenuActivity extends Activity {
Log.w(TAG, "Failed to send action", e);
}
});
- actionsGroup.addView(actionView);
+ if (isLandscapePip && i > 0) {
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
+ actionView.getLayoutParams();
+ lp.leftMargin = mBetweenActionPaddingLand;
+ }
+ mActionsGroup.addView(actionView);
}
}
+ actionsContainerHeight = actionsContainer.getLayoutParams().height;
}
+
+ // Update the expand container margin to account for the existence of the action container
+ ((FrameLayout.LayoutParams) expandContainer.getLayoutParams()).bottomMargin =
+ actionsContainerHeight;
+ expandContainer.requestLayout();
+ }
+
+ private void notifyRegisterInputConsumer() {
+ Message m = Message.obtain();
+ m.what = PipMenuActivityController.MESSAGE_REGISTER_INPUT_CONSUMER;
+ sendMessage(m, "Could not notify controller to register input consumer");
}
private void notifyMenuVisibility(boolean visible) {
@@ -300,10 +338,12 @@ public class PipMenuActivity extends Activity {
}
private void expandPip() {
+ // Do not notify menu visibility when hiding the menu, the controller will do this when it
+ // handles the message
hideMenu(() -> {
sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
"Could not notify controller to expand PIP");
- });
+ }, false /* notifyMenuVisibility */);
}
private void minimizePip() {
@@ -312,10 +352,12 @@ public class PipMenuActivity extends Activity {
}
private void dismissPip() {
+ // Do not notify menu visibility when hiding the menu, the controller will do this when it
+ // handles the message
hideMenu(() -> {
sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
"Could not notify controller to dismiss PIP");
- });
+ }, false /* notifyMenuVisibility */);
}
private void notifyActivityCallback(Messenger callback) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 91115d034671..0b1c3ecc47bd 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -57,6 +57,7 @@ public class PipMenuActivityController {
public static final int MESSAGE_MINIMIZE_PIP = 102;
public static final int MESSAGE_DISMISS_PIP = 103;
public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
+ public static final int MESSAGE_REGISTER_INPUT_CONSUMER = 105;
/**
* A listener interface to receive notification on changes in PIP.
@@ -64,8 +65,11 @@ public class PipMenuActivityController {
public interface Listener {
/**
* Called when the PIP menu visibility changes.
+ *
+ * @param menuVisible whether or not the menu is visible
+ * @param resize whether or not to resize the PiP with the visibility change
*/
- void onPipMenuVisibilityChanged(boolean visible);
+ void onPipMenuVisibilityChanged(boolean menuVisible, boolean resize);
/**
* Called when the PIP requested to be expanded.
@@ -85,13 +89,13 @@ public class PipMenuActivityController {
private Context mContext;
private IActivityManager mActivityManager;
- private IWindowManager mWindowManager;
private PipMediaController mMediaController;
+ private InputConsumerController mInputConsumerController;
private ArrayList<Listener> mListeners = new ArrayList<>();
private ParceledListSlice mAppActions;
private ParceledListSlice mMediaActions;
- private boolean mVisible;
+ private boolean mMenuVisible;
private Messenger mToActivityMessenger;
private Messenger mMessenger = new Messenger(new Handler() {
@@ -100,13 +104,14 @@ public class PipMenuActivityController {
switch (msg.what) {
case MESSAGE_MENU_VISIBILITY_CHANGED: {
boolean visible = msg.arg1 > 0;
- onMenuVisibilityChanged(visible);
+ onMenuVisibilityChanged(visible, true /* resize */);
break;
}
case MESSAGE_EXPAND_PIP: {
mListeners.forEach(l -> l.onPipExpand());
- // Preemptively mark the menu as invisible once we expand the PiP
- onMenuVisibilityChanged(false);
+ // Preemptively mark the menu as invisible once we expand the PiP, but don't
+ // resize as we will be animating the stack
+ onMenuVisibilityChanged(false, false /* resize */);
break;
}
case MESSAGE_MINIMIZE_PIP: {
@@ -115,15 +120,20 @@ public class PipMenuActivityController {
}
case MESSAGE_DISMISS_PIP: {
mListeners.forEach(l -> l.onPipDismiss());
- // Preemptively mark the menu as invisible once we dismiss the PiP
- onMenuVisibilityChanged(false);
+ // Preemptively mark the menu as invisible once we dismiss the PiP, but don't
+ // resize as we'll be removing the stack in place
+ onMenuVisibilityChanged(false, false /* resize */);
+ break;
+ }
+ case MESSAGE_REGISTER_INPUT_CONSUMER: {
+ mInputConsumerController.registerInputConsumer();
break;
}
case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
mToActivityMessenger = msg.replyTo;
// Mark the menu as invisible once the activity finishes as well
if (mToActivityMessenger == null) {
- onMenuVisibilityChanged(false);
+ onMenuVisibilityChanged(false, true /* resize */);
}
break;
}
@@ -140,11 +150,19 @@ public class PipMenuActivityController {
};
public PipMenuActivityController(Context context, IActivityManager activityManager,
- IWindowManager windowManager, PipMediaController mediaController) {
+ PipMediaController mediaController, InputConsumerController inputConsumerController) {
mContext = context;
mActivityManager = activityManager;
- mWindowManager = windowManager;
mMediaController = mediaController;
+ mInputConsumerController = inputConsumerController;
+ }
+
+ public void onActivityPinned() {
+ if (!mMenuVisible) {
+ // If the menu is not visible, then re-register the input consumer if it is not already
+ // registered
+ mInputConsumerController.registerInputConsumer();
+ }
}
/**
@@ -192,6 +210,21 @@ public class PipMenuActivityController {
}
/**
+ * Pokes the menu, indicating that the user is interacting with it.
+ */
+ public void pokeMenu() {
+ if (mToActivityMessenger != null) {
+ Message m = Message.obtain();
+ m.what = PipMenuActivity.MESSAGE_POKE_MENU;
+ try {
+ mToActivityMessenger.send(m);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not notify poke menu", e);
+ }
+ }
+ }
+
+ /**
* Hides the menu activity.
*/
public void hideMenu() {
@@ -207,6 +240,13 @@ public class PipMenuActivityController {
}
/**
+ * @return whether the menu is currently visible.
+ */
+ public boolean isMenuVisible() {
+ return mMenuVisible;
+ }
+
+ /**
* Sets the menu actions to the actions provided by the current PiP activity.
*/
public void setAppActions(ParceledListSlice appActions) {
@@ -250,9 +290,14 @@ public class PipMenuActivityController {
/**
* Handles changes in menu visibility.
*/
- private void onMenuVisibilityChanged(boolean visible) {
- mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible));
- if (visible != mVisible) {
+ private void onMenuVisibilityChanged(boolean visible, boolean resize) {
+ if (visible) {
+ mInputConsumerController.unregisterInputConsumer();
+ } else {
+ mInputConsumerController.registerInputConsumer();
+ }
+ if (visible != mMenuVisible) {
+ mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible, resize));
if (visible) {
// Once visible, start listening for media action changes. This call will trigger
// the menu actions to be updated again.
@@ -263,13 +308,13 @@ public class PipMenuActivityController {
mMediaController.removeListener(mMediaActionListener);
}
}
- mVisible = visible;
+ mMenuVisible = visible;
}
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
- pw.println(innerPrefix + "mVisible=" + mVisible);
+ pw.println(innerPrefix + "mMenuVisible=" + mMenuVisible);
pw.println(innerPrefix + "mListeners=" + mListeners.size());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index ed0a37fed52d..20c1136a5998 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -57,10 +57,11 @@ public class PipMotionHelper {
private static final int DEFAULT_MOVE_STACK_DURATION = 225;
private static final int SNAP_STACK_DURATION = 225;
private static final int DISMISS_STACK_DURATION = 375;
- private static final int SHRINK_STACK_FROM_MENU_DURATION = 175;
- private static final int EXPAND_STACK_TO_MENU_DURATION = 175;
- private static final int EXPAND_STACK_TO_FULLSCREEN_DURATION = 225;
+ private static final int SHRINK_STACK_FROM_MENU_DURATION = 250;
+ private static final int EXPAND_STACK_TO_MENU_DURATION = 250;
+ private static final int EXPAND_STACK_TO_FULLSCREEN_DURATION = 300;
private static final int MINIMIZE_STACK_MAX_DURATION = 200;
+ private static final int IME_SHIFT_DURATION = 300;
// The fraction of the stack width that the user has to drag offscreen to minimize the PiP
private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.2f;
@@ -217,19 +218,12 @@ public class PipMotionHelper {
/**
* Animates the PiP to the minimized state, slightly offscreen.
*/
- Rect animateToClosestMinimizedState(Rect movementBounds,
- final PipMenuActivityController menuController) {
+ Rect animateToClosestMinimizedState(Rect movementBounds) {
cancelAnimations();
Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);
if (!mBounds.equals(toBounds)) {
mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN, mUpdateBoundsListener);
- mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- menuController.hideMenu();
- }
- });
mBoundsAnimator.start();
}
return toBounds;
@@ -274,9 +268,7 @@ public class PipMotionHelper {
Rect expandedMovementBounds) {
float savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), movementBounds);
mSnapAlgorithm.applySnapFraction(expandedBounds, expandedMovementBounds, savedSnapFraction);
- mBoundsAnimator = createAnimationToBounds(mBounds, expandedBounds,
- EXPAND_STACK_TO_MENU_DURATION, FAST_OUT_SLOW_IN, mUpdateBoundsListener);
- mBoundsAnimator.start();
+ resizeAndAnimatePipUnchecked(expandedBounds, EXPAND_STACK_TO_MENU_DURATION);
return savedSnapFraction;
}
@@ -284,15 +276,25 @@ public class PipMotionHelper {
* Animates the PiP from the expanded state to the normal state after the menu is hidden.
*/
void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction,
- Rect normalMovementBounds) {
- if (savedSnapFraction >= 0f) {
- mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
- mBoundsAnimator = createAnimationToBounds(mBounds, normalBounds,
- SHRINK_STACK_FROM_MENU_DURATION, FAST_OUT_SLOW_IN, mUpdateBoundsListener);
- mBoundsAnimator.start();
- } else {
- animateToClosestSnapTarget(normalMovementBounds);
+ Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized) {
+ if (savedSnapFraction < 0f) {
+ // If there are no saved snap fractions, then just use the current bounds
+ savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds),
+ currentMovementBounds);
+ }
+ mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
+ if (minimized) {
+ normalBounds = getClosestMinimizedBounds(normalBounds, normalMovementBounds);
}
+ resizeAndAnimatePipUnchecked(normalBounds, SHRINK_STACK_FROM_MENU_DURATION);
+ }
+
+ /**
+ * Animates the PiP to offset it from the IME.
+ */
+ void animateToIMEOffset(Rect toBounds) {
+ cancelAnimations();
+ resizeAndAnimatePipUnchecked(toBounds, IME_SHIFT_DURATION);
}
/**
@@ -317,18 +319,6 @@ public class PipMotionHelper {
}
/**
- * Animates the PiP to some given bounds.
- */
- void animateToBounds(Rect toBounds) {
- cancelAnimations();
- if (!mBounds.equals(toBounds)) {
- mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
- DEFAULT_MOVE_STACK_DURATION, FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
- mBoundsAnimator.start();
- }
- }
-
- /**
* Cancels all existing animations.
*/
void cancelAnimations() {
@@ -365,7 +355,32 @@ public class PipMotionHelper {
mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
mBounds.set(toBounds);
} catch (RemoteException e) {
- Log.e(TAG, "Could not move pinned stack to bounds: " + toBounds, e);
+ Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e);
+ }
+ });
+ }
+ }
+
+ /**
+ * Directly resizes the PiP to the given {@param bounds}.
+ */
+ private void resizeAndAnimatePipUnchecked(Rect toBounds, int duration) {
+ if (!toBounds.equals(mBounds)) {
+ mHandler.post(() -> {
+ try {
+ StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (stackInfo == null) {
+ // In the case where we've already re-expanded or dismissed the PiP, then
+ // just skip the resize
+ return;
+ }
+
+ mActivityManager.resizeStack(PINNED_STACK_ID, toBounds,
+ false /* allowResizeInDockedMode */, true /* preserveWindows */,
+ true /* animate */, duration);
+ mBounds.set(toBounds);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 4100b66b07b6..010522d2818d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -16,27 +16,24 @@
package com.android.systemui.pip.phone;
-import static android.view.WindowManager.INPUT_CONSUMER_PIP;
-
import android.app.IActivityManager;
import android.content.Context;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Size;
import android.view.IPinnedStackController;
import android.view.IWindowManager;
-import android.view.InputChannel;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.PipSnapAlgorithm;
+import com.android.systemui.R;
import com.android.systemui.statusbar.FlingAnimationUtils;
import java.io.PrintWriter;
@@ -59,12 +56,10 @@ public class PipTouchHandler {
private final Context mContext;
private final IActivityManager mActivityManager;
- private final IWindowManager mWindowManager;
private final ViewConfiguration mViewConfig;
private final PipMenuListener mMenuListener = new PipMenuListener();
private IPinnedStackController mPinnedStackController;
- private PipInputEventReceiver mInputEventReceiver;
private final PipMenuActivityController mMenuController;
private final PipDismissViewController mDismissViewController;
private final PipSnapAlgorithm mSnapAlgorithm;
@@ -77,6 +72,7 @@ public class PipTouchHandler {
private Rect mNormalMovementBounds = new Rect();
private Rect mExpandedBounds = new Rect();
private Rect mExpandedMovementBounds = new Rect();
+ private int mExpandedShortestEdgeSize;
private Handler mHandler = new Handler();
private Runnable mShowDismissAffordance = new Runnable() {
@@ -89,9 +85,8 @@ public class PipTouchHandler {
};
// Behaviour states
- private boolean mIsTappingThrough;
- private boolean mIsMinimized;
private boolean mIsMenuVisible;
+ private boolean mIsMinimized;
private boolean mIsImeShowing;
private int mImeHeight;
private float mSavedSnapFraction = -1f;
@@ -106,36 +101,12 @@ public class PipTouchHandler {
private final Rect mTmpBounds = new Rect();
/**
- * Input handler used for Pip windows.
- */
- private final class PipInputEventReceiver extends InputEventReceiver {
-
- public PipInputEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
-
- @Override
- public void onInputEvent(InputEvent event) {
- boolean handled = true;
- try {
- // To be implemented for input handling over Pip windows
- if (event instanceof MotionEvent) {
- MotionEvent ev = (MotionEvent) event;
- handled = handleTouchEvent(ev);
- }
- } finally {
- finishInputEvent(event, handled);
- }
- }
- }
-
- /**
* A listener for the PIP menu activity.
*/
private class PipMenuListener implements PipMenuActivityController.Listener {
@Override
- public void onPipMenuVisibilityChanged(boolean visible) {
- setMenuVisibilityState(visible);
+ public void onPipMenuVisibilityChanged(boolean menuVisible, boolean resize) {
+ setMenuVisibilityState(menuVisible, resize);
}
@Override
@@ -148,7 +119,7 @@ public class PipTouchHandler {
@Override
public void onPipMinimize() {
setMinimizedStateInternal(true);
- mMotionHelper.animateToClosestMinimizedState(mMovementBounds, mMenuController);
+ mMotionHelper.animateToClosestMinimizedState(mMovementBounds);
}
@Override
@@ -159,13 +130,13 @@ public class PipTouchHandler {
}
}
- public PipTouchHandler(Context context, PipMenuActivityController menuController,
- IActivityManager activityManager, IWindowManager windowManager) {
+ public PipTouchHandler(Context context, IActivityManager activityManager,
+ PipMenuActivityController menuController,
+ InputConsumerController inputConsumerController) {
// Initialize the Pip input consumer
mContext = context;
mActivityManager = activityManager;
- mWindowManager = windowManager;
mViewConfig = ViewConfiguration.get(context);
mMenuController = menuController;
mMenuController.addListener(mMenuListener);
@@ -178,7 +149,11 @@ public class PipTouchHandler {
};
mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mSnapAlgorithm,
mFlingAnimationUtils);
- registerInputConsumer();
+ mExpandedShortestEdgeSize = context.getResources().getDimensionPixelSize(
+ R.dimen.pip_expanded_shortest_edge_size);
+
+ // Register the listener for input consumer touch events
+ inputConsumerController.setTouchListener(this::handleTouchEvent);
}
public void setTouchEnabled(boolean enabled) {
@@ -187,9 +162,8 @@ public class PipTouchHandler {
public void onActivityPinned() {
// Reset some states once we are pinned
- if (mIsTappingThrough) {
- mIsTappingThrough = false;
- registerInputConsumer();
+ if (mIsMenuVisible) {
+ mIsMenuVisible = false;
}
if (mIsMinimized) {
setMinimizedStateInternal(false);
@@ -206,15 +180,21 @@ public class PipTouchHandler {
mImeHeight = imeHeight;
}
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
+ public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
boolean fromImeAdjustement) {
// Re-calculate the expanded bounds
mNormalBounds = normalBounds;
Rect normalMovementBounds = new Rect();
mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds,
mIsImeShowing ? mImeHeight : 0);
- // TODO: Figure out the expanded size policy
- mExpandedBounds = new Rect(normalBounds);
+
+ // Calculate the expanded size
+ float aspectRatio = (float) normalBounds.width() / normalBounds.height();
+ Point displaySize = new Point();
+ mContext.getDisplay().getRealSize(displaySize);
+ Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
+ mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
+ mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
Rect expandedMovementBounds = new Rect();
mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
mIsImeShowing ? mImeHeight : 0);
@@ -227,7 +207,7 @@ public class PipTouchHandler {
// Defer the update of the current movement bounds until after the user finishes
// touching the screen
} else {
- final Rect bounds = new Rect(mMotionHelper.getBounds());
+ final Rect bounds = new Rect(animatingBounds);
final Rect toMovementBounds = mIsMenuVisible
? expandedMovementBounds
: normalMovementBounds;
@@ -247,7 +227,7 @@ public class PipTouchHandler {
bounds.offsetTo(bounds.left, toMovementBounds.bottom);
}
}
- mMotionHelper.animateToBounds(bounds);
+ mMotionHelper.animateToIMEOffset(bounds);
}
}
@@ -255,7 +235,7 @@ public class PipTouchHandler {
// above
mNormalMovementBounds = normalMovementBounds;
mExpandedMovementBounds = expandedMovementBounds;
- updateMovementBounds();
+ updateMovementBounds(mIsMenuVisible);
}
private boolean handleTouchEvent(MotionEvent ev) {
@@ -287,7 +267,7 @@ public class PipTouchHandler {
case MotionEvent.ACTION_UP: {
// Update the movement bounds again if the state has changed since the user started
// dragging (ie. when the IME shows)
- updateMovementBounds();
+ updateMovementBounds(mIsMenuVisible);
for (PipTouchGesture gesture : mGestures) {
if (gesture.onUp(mTouchState)) {
@@ -302,38 +282,7 @@ public class PipTouchHandler {
break;
}
}
- return !mIsTappingThrough;
- }
-
- /**
- * Registers the input consumer.
- */
- private void registerInputConsumer() {
- if (mInputEventReceiver == null) {
- final InputChannel inputChannel = new InputChannel();
- try {
- mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
- mWindowManager.createInputConsumer(INPUT_CONSUMER_PIP, inputChannel);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to create PIP input consumer", e);
- }
- mInputEventReceiver = new PipInputEventReceiver(inputChannel, Looper.myLooper());
- }
- }
-
- /**
- * Unregisters the input consumer.
- */
- private void unregisterInputConsumer() {
- if (mInputEventReceiver != null) {
- try {
- mWindowManager.destroyInputConsumer(INPUT_CONSUMER_PIP);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to destroy PIP input consumer", e);
- }
- mInputEventReceiver.dispose();
- mInputEventReceiver = null;
- }
+ return !mIsMenuVisible;
}
/**
@@ -379,34 +328,30 @@ public class PipTouchHandler {
/**
* Sets the menu visibility.
*/
- void setMenuVisibilityState(boolean isMenuVisible) {
- if (!isMenuVisible) {
- mIsTappingThrough = false;
- registerInputConsumer();
- } else {
- unregisterInputConsumer();
- }
- MetricsLogger.visibility(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
- isMenuVisible);
-
- if (isMenuVisible != mIsMenuVisible) {
- if (isMenuVisible) {
- // Save the current snap fraction and if we do not drag or move the PiP, then
- // we store back to this snap fraction. Otherwise, we'll reset the snap
- // fraction and snap to the closest edge
- Rect expandedBounds = new Rect(mExpandedBounds);
+ void setMenuVisibilityState(boolean menuVisible, boolean resize) {
+ if (menuVisible) {
+ // Save the current snap fraction and if we do not drag or move the PiP, then
+ // we store back to this snap fraction. Otherwise, we'll reset the snap
+ // fraction and snap to the closest edge
+ Rect expandedBounds = new Rect(mExpandedBounds);
+ if (resize) {
mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
mMovementBounds, mExpandedMovementBounds);
- } else {
- // Try and restore the PiP to the closest edge, using the saved snap fraction
- // if possible
+ }
+ } else {
+ // Try and restore the PiP to the closest edge, using the saved snap fraction
+ // if possible
+ if (resize) {
Rect normalBounds = new Rect(mNormalBounds);
mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
- mNormalMovementBounds);
+ mNormalMovementBounds, mMovementBounds, mIsMinimized);
}
- mIsMenuVisible = isMenuVisible;
- updateMovementBounds();
+ mSavedSnapFraction = -1f;
}
+ mIsMenuVisible = menuVisible;
+ updateMovementBounds(menuVisible);
+ MetricsLogger.visibility(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
+ menuVisible);
}
/**
@@ -427,6 +372,12 @@ public class PipTouchHandler {
return;
}
+ // If the menu is still visible, and we aren't minimized, then just poke the menu
+ // so that it will timeout after the user stops touching it
+ if (mMenuController.isMenuVisible() && !mIsMinimized) {
+ mMenuController.pokeMenu();
+ }
+
if (ENABLE_DRAG_TO_DISMISS) {
mDismissViewController.createDismissTarget();
mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
@@ -495,21 +446,35 @@ public class PipTouchHandler {
} finally {
mDismissViewController.destroyDismissTarget();
}
+
if (touchState.isDragging()) {
PointF vel = mTouchState.getVelocity();
if (!mIsMinimized && (mMotionHelper.shouldMinimizePip()
|| isHorizontalFlingTowardsCurrentEdge(vel))) {
// Pip should be minimized
setMinimizedStateInternal(true);
- mMotionHelper.animateToClosestMinimizedState(mMovementBounds, mMenuController);
+ if (mMenuController.isMenuVisible()) {
+ // If the user dragged the expanded PiP to the edge, then hiding the menu
+ // will trigger the PiP to be scaled back to the normal size with the
+ // minimize offset adjusted
+ mMenuController.hideMenu();
+ } else {
+ mMotionHelper.animateToClosestMinimizedState(mMovementBounds);
+ }
return true;
}
if (mIsMinimized) {
- // If we're dragging and it wasn't a minimize gesture
- // then we shouldn't be minimized.
+ // If we're dragging and it wasn't a minimize gesture then we shouldn't be
+ // minimized.
setMinimizedStateInternal(false);
}
+ // If the menu is still visible, and we aren't minimized, then just poke the menu
+ // so that it will timeout after the user stops touching it
+ if (mMenuController.isMenuVisible()) {
+ mMenuController.showMenu();
+ }
+
final float velocity = PointF.length(vel.x, vel.y);
if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds);
@@ -520,9 +485,8 @@ public class PipTouchHandler {
// This was a tap, so no longer minimized
mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
setMinimizedStateInternal(false);
- } else if (!mIsTappingThrough) {
+ } else if (!mIsMenuVisible) {
mMenuController.showMenu();
- mIsTappingThrough = true;
} else {
mMotionHelper.expandPip();
}
@@ -559,8 +523,8 @@ public class PipTouchHandler {
/**
* Updates the current movement bounds based on whether the menu is currently visible.
*/
- private void updateMovementBounds() {
- mMovementBounds = mIsMenuVisible
+ private void updateMovementBounds(boolean isExpanded) {
+ mMovementBounds = isExpanded
? mExpandedMovementBounds
: mNormalMovementBounds;
}
@@ -573,9 +537,8 @@ public class PipTouchHandler {
pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
- pw.println(innerPrefix + "mIsTappingThrough=" + mIsTappingThrough);
- pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
pw.println(innerPrefix + "mIsMenuVisible=" + mIsMenuVisible);
+ pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index d506669f74a8..9a8974d18905 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -197,7 +197,7 @@ public class PipManager implements BasePipManager {
@Override
public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- boolean fromImeAdjustement) {
+ Rect animatingBounds, boolean fromImeAdjustement) {
mHandler.post(() -> {
mDefaultPipBounds.set(normalBounds);
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 602c3dfde73e..00968ee8d0e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -927,12 +927,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
if (areGutsExposed()) {
return false;
}
- if (mIsSummaryWithChildren) {
- return true;
- }
- NotificationContentView showingLayout = getShowingLayout();
- NotificationHeaderView notificationHeader = showingLayout.getVisibleNotificationHeader();
- return notificationHeader != null;
+ return getVisibleNotificationHeader() != null;
}
/**
@@ -1273,7 +1268,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
if (mChildrenContainer != null) {
mChildrenContainer.setVisibility(!mShowingPublic && mIsSummaryWithChildren ? VISIBLE
: INVISIBLE);
- mChildrenContainer.setHeaderVisible(!mShowingPublic && mIsSummaryWithChildren);
}
// The limits might have changed if the view suddenly became a group or vice versa
updateLimits();
@@ -1720,6 +1714,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
@Override
public boolean isContentExpandable() {
+ if (mIsSummaryWithChildren && !mShowingPublic) {
+ return true;
+ }
NotificationContentView showingLayout = getShowingLayout();
return showingLayout.isContentExpandable();
}
@@ -1987,6 +1984,26 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
if (canViewBeDismissed()) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
}
+ boolean expandable = mShowingPublic;
+ boolean isExpanded = false;
+ if (!expandable) {
+ if (mIsSummaryWithChildren) {
+ expandable = true;
+ if (!mIsLowPriority || isExpanded()) {
+ isExpanded = isGroupExpanded();
+ }
+ } else {
+ expandable = mPrivateLayout.isContentExpandable();
+ isExpanded = isExpanded();
+ }
+ }
+ if (expandable) {
+ if (isExpanded) {
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+ } else {
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
+ }
+ }
}
@Override
@@ -1999,6 +2016,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
NotificationStackScrollLayout.performDismiss(this, mGroupManager,
true /* fromAccessibility */);
return true;
+ case AccessibilityNodeInfo.ACTION_COLLAPSE:
+ case AccessibilityNodeInfo.ACTION_EXPAND:
+ mExpandClickListener.onClick(this);
+ return true;
}
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 74e65fbd2432..8f160dc0384e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -131,6 +131,7 @@ public class NotificationContentView extends FrameLayout {
private boolean mIconsVisible;
private int mClipBottomAmount;
private boolean mIsLowPriority;
+ private boolean mIsContentExpandable;
public NotificationContentView(Context context, AttributeSet attrs) {
@@ -949,7 +950,7 @@ public class NotificationContentView extends FrameLayout {
}
public boolean isContentExpandable() {
- return mExpandedChild != null;
+ return mIsContentExpandable;
}
public void setDark(boolean dark, boolean fade, long delay) {
@@ -1198,10 +1199,10 @@ public class NotificationContentView extends FrameLayout {
if (mExpandedChild != null && mExpandedChild.getHeight() != 0) {
if ((!mIsHeadsUp && !mHeadsUpAnimatingAway)
|| mHeadsUpChild == null || mContainingNotification.isOnKeyguard()) {
- if (mExpandedChild.getHeight() == mContractedChild.getHeight()) {
+ if (mExpandedChild.getHeight() <= mContractedChild.getHeight()) {
expandable = false;
}
- } else if (mExpandedChild.getHeight() == mHeadsUpChild.getHeight()) {
+ } else if (mExpandedChild.getHeight() <= mHeadsUpChild.getHeight()) {
expandable = false;
}
}
@@ -1214,6 +1215,7 @@ public class NotificationContentView extends FrameLayout {
if (mHeadsUpChild != null) {
mHeadsUpWrapper.updateExpandability(expandable, mExpandClickListener);
}
+ mIsContentExpandable = expandable;
}
public NotificationHeaderView getNotificationHeader() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 938e76af1759..4a2ec88c7518 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -127,7 +127,7 @@ public class DozeParameters {
public boolean getSensorsWakeUpFully() {
return ALWAYS_ON_AVAILABLE
&& Settings.Secure.getIntForUser(mContext.getContentResolver(),
- DOZE_SENSORS_WAKE_UP_FULLY, 0, UserHandle.USER_CURRENT) != 0;
+ DOZE_SENSORS_WAKE_UP_FULLY, 1, UserHandle.USER_CURRENT) != 0;
}
private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index b52c26f143f7..4dc593b856e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -188,9 +188,6 @@ public class PhoneStatusBarView extends PanelBar {
public void panelScrimMinFractionChanged(float minFraction) {
if (mMinFraction != minFraction) {
mMinFraction = minFraction;
- if (minFraction != 0.0f) {
- mScrimController.animateNextChange();
- }
updateScrimFraction();
}
}
@@ -203,7 +200,11 @@ public class PhoneStatusBarView extends PanelBar {
}
private void updateScrimFraction() {
- float scrimFraction = Math.max(mPanelFraction, mMinFraction);
+ float scrimFraction = mPanelFraction;
+ if (mMinFraction < 1.0f) {
+ scrimFraction = Math.max((mPanelFraction - mMinFraction) / (1.0f - mMinFraction),
+ 0);
+ }
mScrimController.setPanelExpansion(scrimFraction);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index b30d3abc2375..dadb749af2d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -203,10 +203,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
scheduleUpdate();
}
- public void animateNextChange() {
- mAnimateChange = true;
- }
-
public void setDozing(boolean dozing) {
if (mDozing != dozing) {
mDozing = dozing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 19be586710ce..005b701c0d52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -715,6 +715,7 @@ public class StatusBar extends SystemUI implements DemoMode,
private NotificationIconAreaController mNotificationIconAreaController;
private ConfigurationListener mConfigurationListener;
private InflationExceptionHandler mInflationExceptionHandler = this::handleInflationException;
+ private boolean mReinflateNotificationsOnUserSwitched;
private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
final int N = array.size();
@@ -1274,16 +1275,10 @@ public class StatusBar extends SystemUI implements DemoMode,
protected void onDensityOrFontScaleChanged() {
// start old BaseStatusBar.onDensityOrFontScaleChanged().
- ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
- for (int i = 0; i < activeNotifications.size(); i++) {
- Entry entry = activeNotifications.get(i);
- boolean exposedGuts = mNotificationGutsExposed != null
- && entry.row.getGuts() == mNotificationGutsExposed;
- entry.row.onDensityOrFontScaleChanged();
- if (exposedGuts) {
- mNotificationGutsExposed = entry.row.getGuts();
- bindGuts(entry.row, mGutsMenuItem);
- }
+ if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
+ updateNotificationsOnDensityOrFontScaleChanged();
+ } else {
+ mReinflateNotificationsOnUserSwitched = true;
}
// end old BaseStatusBar.onDensityOrFontScaleChanged().
mScrimController.onDensityOrFontScaleChanged();
@@ -1308,6 +1303,20 @@ public class StatusBar extends SystemUI implements DemoMode,
}
}
+ private void updateNotificationsOnDensityOrFontScaleChanged() {
+ ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
+ for (int i = 0; i < activeNotifications.size(); i++) {
+ Entry entry = activeNotifications.get(i);
+ boolean exposedGuts = mNotificationGutsExposed != null
+ && entry.row.getGuts() == mNotificationGutsExposed;
+ entry.row.onDensityOrFontScaleChanged();
+ if (exposedGuts) {
+ mNotificationGutsExposed = entry.row.getGuts();
+ bindGuts(entry.row, mGutsMenuItem);
+ }
+ }
+ }
+
private void inflateSignalClusters() {
reinflateSignalCluster(mKeyguardStatusBar);
}
@@ -3598,7 +3607,12 @@ public class StatusBar extends SystemUI implements DemoMode,
if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
animateCollapsePanels();
updatePublicMode();
- updateNotifications();
+ mNotificationData.filterAndSort();
+ if (mReinflateNotificationsOnUserSwitched) {
+ updateNotificationsOnDensityOrFontScaleChanged();
+ mReinflateNotificationsOnUserSwitched = false;
+ }
+ updateNotificationShade();
clearCurrentMediaNotification();
setLockscreenUser(newUserId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 83cbd722703c..fe249a6dc8bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -89,7 +89,6 @@ public class NotificationChildrenContainer extends ViewGroup {
private ViewState mHeaderViewState;
private int mClipBottomAmount;
private boolean mIsLowPriority;
- private boolean mHeaderVisible = true;
private OnClickListener mHeaderClickListener;
private boolean mShowingNormalHeader;
@@ -324,6 +323,7 @@ public class NotificationChildrenContainer extends ViewGroup {
}
mNotificationHeaderWrapperLowPriority.notifyContentUpdated(mContainingNotification);
} else {
+ removeView(mNotificationHeaderLowPriority);
mNotificationHeaderLowPriority = null;
mNotificationHeaderWrapperLowPriority = null;
}
@@ -794,11 +794,6 @@ public class NotificationChildrenContainer extends ViewGroup {
return mNotificationHeaderLowPriority;
}
- public void setHeaderVisible(boolean visible) {
- mHeaderVisible = visible;
- updateHeaderVisibility(false /* animate */);
- }
-
private void updateHeaderVisibility(boolean animate) {
NotificationHeaderView visibleHeader = mNotificationHeader;
NotificationHeaderView hiddenHeader = mNotificationHeaderLowPriority;
@@ -809,7 +804,7 @@ public class NotificationChildrenContainer extends ViewGroup {
normalHeaderVisible = false;
}
if (animate) {
- if (mHeaderVisible && visibleHeader != null && hiddenHeader != null
+ if (visibleHeader != null && hiddenHeader != null
&& mShowingNormalHeader != normalHeaderVisible) {
hiddenHeader.setVisibility(VISIBLE);
visibleHeader.setVisibility(VISIBLE);
@@ -825,7 +820,7 @@ public class NotificationChildrenContainer extends ViewGroup {
if (!animate) {
if (visibleHeader != null) {
getWrapperForView(visibleHeader).setVisible(true);
- visibleHeader.setVisibility(mHeaderVisible ? VISIBLE : INVISIBLE);
+ visibleHeader.setVisibility(VISIBLE);
}
if (hiddenHeader != null) {
getWrapperForView(hiddenHeader).setVisible(false);
@@ -855,7 +850,7 @@ public class NotificationChildrenContainer extends ViewGroup {
private void updateHeaderTransformation() {
- if (mUserLocked && mHeaderVisible && showingAsLowPriority()) {
+ if (mUserLocked && showingAsLowPriority()) {
float fraction = getGroupExpandFraction();
mNotificationHeaderWrapper.transformFrom(mNotificationHeaderWrapperLowPriority,
fraction);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java b/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java
index 1607b7086c61..fd99d1d46db3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java
@@ -46,7 +46,7 @@ public class SysUIRunner extends BlockJUnit4ClassRunner {
@Override
protected Statement methodInvoker(FrameworkMethod method, Object test) {
- return UiThreadStatement.shouldRunOnUiThread(method) ? new UiThreadStatement(
+ return shouldRunOnUiThread(method) ? new UiThreadStatement(
methodInvokerInt(method, test), true) : methodInvokerInt(method, test);
}
@@ -84,4 +84,12 @@ public class SysUIRunner extends BlockJUnit4ClassRunner {
private long getTimeout(Test annotation) {
return annotation == null ? 0L : annotation.timeout();
}
+
+ public boolean shouldRunOnUiThread(FrameworkMethod method) {
+ if (mKlass.getAnnotation(UiThreadTest.class) != null) {
+ return true;
+ } else {
+ return UiThreadStatement.shouldRunOnUiThread(method);
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java b/packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java
new file mode 100644
index 000000000000..58369b119c49
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.systemui;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * When applied to a class, all tests, befores, and afters will behave as if
+ * they have @UiThreadTest applied to them.
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UiThreadTest {
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 32afee9744d5..0cccbe1721cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -37,11 +37,12 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.Display;
+import com.android.systemui.SysUIRunner;
+import com.android.systemui.UiThreadTest;
import com.android.systemui.statusbar.phone.DozeParameters;
import org.junit.Before;
@@ -49,7 +50,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(SysUIRunner.class)
+@UiThreadTest
public class DozeMachineTest {
DozeMachine mMachine;
@@ -72,7 +74,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testInitialize_initializesParts() {
mMachine.requestState(INITIALIZED);
@@ -80,7 +81,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testInitialize_goesToDoze() {
when(mParamsMock.getAlwaysOn()).thenReturn(false);
@@ -91,7 +91,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testInitialize_goesToAod() {
when(mParamsMock.getAlwaysOn()).thenReturn(true);
@@ -102,7 +101,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testPulseDone_goesToDoze() {
when(mParamsMock.getAlwaysOn()).thenReturn(false);
mMachine.requestState(INITIALIZED);
@@ -116,7 +114,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testPulseDone_goesToAoD() {
when(mParamsMock.getAlwaysOn()).thenReturn(true);
mMachine.requestState(INITIALIZED);
@@ -130,7 +127,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testFinished_staysFinished() {
mMachine.requestState(INITIALIZED);
mMachine.requestState(FINISH);
@@ -143,7 +139,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testFinish_finishesService() {
mMachine.requestState(INITIALIZED);
@@ -153,7 +148,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testWakeLock_heldInTransition() {
doAnswer((inv) -> {
assertTrue(mWakeLockFake.isHeld());
@@ -164,7 +158,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testWakeLock_heldInPulseStates() {
mMachine.requestState(INITIALIZED);
@@ -176,7 +169,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testWakeLock_notHeldInDozeStates() {
mMachine.requestState(INITIALIZED);
@@ -188,7 +180,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testWakeLock_releasedAfterPulse() {
mMachine.requestState(INITIALIZED);
@@ -201,7 +192,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testPulseDuringPulse_doesntCrash() {
mMachine.requestState(INITIALIZED);
@@ -213,7 +203,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testSuppressingPulse_doesntCrash() {
mMachine.requestState(INITIALIZED);
@@ -223,7 +212,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testScreen_offInDoze() {
mMachine.requestState(INITIALIZED);
@@ -233,7 +221,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testScreen_onInAod() {
mMachine.requestState(INITIALIZED);
@@ -243,7 +230,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testScreen_onInPulse() {
mMachine.requestState(INITIALIZED);
@@ -254,7 +240,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testScreen_offInRequestPulseWithoutAoD() {
mMachine.requestState(INITIALIZED);
@@ -265,7 +250,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testScreen_onInRequestPulseWithoutAoD() {
mMachine.requestState(INITIALIZED);
@@ -276,7 +260,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testTransitions_canRequestTransitions() {
mMachine.requestState(INITIALIZED);
mMachine.requestState(DOZE);
@@ -291,7 +274,6 @@ public class DozeMachineTest {
}
@Test
- @UiThreadTest
public void testWakeUp_wakesUp() {
mMachine.wakeUp();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
index 193250fe4e8f..53053fa6e2b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
@@ -16,7 +16,6 @@ package com.android.systemui.notification;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.support.test.annotation.UiThreadTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -28,7 +27,9 @@ import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.systemui.SysUIRunner;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.UiThreadTest;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.stack.AnimationFilter;
import com.android.systemui.statusbar.stack.AnimationProperties;
@@ -49,7 +50,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(SysUIRunner.class)
+@UiThreadTest
public class PropertyAnimatorTest extends SysuiTestCase {
private View mView;
@@ -106,13 +108,11 @@ public class PropertyAnimatorTest extends SysuiTestCase {
@Before
- @UiThreadTest
public void setUp() {
mView = new View(getContext());
}
@Test
- @UiThreadTest
public void testAnimationStarted() {
mAnimationFilter.reset();
mAnimationFilter.animate(mProperty.getProperty());
@@ -121,7 +121,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testNoAnimationStarted() {
mAnimationFilter.reset();
PropertyAnimator.startAnimation(mView, mProperty, 200, mAnimationProperties);
@@ -129,7 +128,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testEndValueUpdated() {
mAnimationFilter.reset();
mAnimationFilter.animate(mProperty.getProperty());
@@ -139,7 +137,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testStartTagUpdated() {
mEffectiveProperty.set(mView, 100f);
mAnimationFilter.reset();
@@ -150,7 +147,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testValueIsSetUnAnimated() {
mAnimationFilter.reset();
PropertyAnimator.startAnimation(mView, mProperty, 200f, mAnimationProperties);
@@ -158,7 +154,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testAnimationToRightValueUpdated() {
mAnimationFilter.reset();
mAnimationFilter.animate(mProperty.getProperty());
@@ -171,7 +166,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testAnimationToRightValueUpdateAnimated() {
mAnimationFilter.reset();
mAnimationFilter.animate(mProperty.getProperty());
@@ -185,7 +179,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testStartTagShiftedWhenChanging() {
mEffectiveProperty.set(mView, 100f);
mAnimationFilter.reset();
@@ -198,7 +191,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testUsingDuration() {
mAnimationFilter.reset();
mAnimationFilter.animate(mProperty.getProperty());
@@ -210,7 +202,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testUsingDelay() {
mAnimationFilter.reset();
mAnimationFilter.animate(mProperty.getProperty());
@@ -222,7 +213,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testUsingInterpolator() {
mAnimationFilter.reset();
mAnimationFilter.animate(mProperty.getProperty());
@@ -234,7 +224,6 @@ public class PropertyAnimatorTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testUsingListener() {
mAnimationFilter.reset();
mAnimationFilter.animate(mProperty.getProperty());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
new file mode 100644
index 000000000000..3db244048536
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.systemui.statusbar;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ExpandableNotificationRowTest {
+
+ private Context mContext;
+ private ExpandableNotificationRow mGroup;
+ private NotificationTestHelper mNotificationTestHelper;
+
+ @Before
+ @UiThreadTest
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mGroup = mNotificationTestHelper.createGroup();
+ }
+
+ @Test
+ public void testGroupSummaryNotShowingIconWhenPublic() {
+ mGroup.setSensitive(true, true);
+ mGroup.setHideSensitive(true, false, 0, 0);
+ Assert.assertTrue(mGroup.isSummaryWithChildren());
+ Assert.assertFalse(mGroup.isShowingIcon());
+ }
+
+ @Test
+ public void testNotificationHeaderVisibleWhenAnimating() {
+ mGroup.setSensitive(true, true);
+ mGroup.setHideSensitive(true, false, 0, 0);
+ mGroup.setHideSensitive(false, true, 0, 0);
+ Assert.assertTrue(mGroup.getChildrenContainer().getVisibleHeader().getVisibility()
+ == View.VISIBLE);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 92f8c7ce7fe4..8520bdb114aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -44,7 +44,6 @@ import android.content.pm.ParceledListSlice;
import android.graphics.drawable.Drawable;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -55,7 +54,10 @@ import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.util.CharSequences;
import com.android.systemui.R;
+import com.android.systemui.SysUIRunner;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.UiThreadTest;
+
import org.junit.Before;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -65,7 +67,8 @@ import java.util.Collections;
import java.util.concurrent.CountDownLatch;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(SysUIRunner.class)
+@UiThreadTest
public class NotificationInfoTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test_package";
private static final String TEST_CHANNEL = "test_channel";
@@ -79,7 +82,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mock(StatusBarNotification.class);
@Before
- @UiThreadTest
public void setUp() throws Exception {
// Inflate the layout
final LayoutInflater layoutInflater =
@@ -116,7 +118,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_SetsTextApplicationName() throws Exception {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -126,7 +127,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_SetsPackageIcon() throws Exception {
final Drawable iconDrawable = mock(Drawable.class);
when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
@@ -138,7 +138,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
mMockStatusBarNotification, mNotificationChannel, null, null, null);
@@ -150,7 +149,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_SetsGroupNameIfNonNull() throws Exception {
mNotificationChannel.setGroup("test_group_id");
final NotificationChannelGroup notificationChannelGroup =
@@ -169,7 +167,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_SetsGroupName_resId() throws Exception {
when(mMockPackageManager.getText(eq(TEST_PACKAGE_NAME),
eq(R.string.legacy_vpn_name), anyObject())).thenReturn(
@@ -191,7 +188,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_SetsTextChannelName() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
mMockStatusBarNotification, mNotificationChannel, null, null, null);
@@ -200,7 +196,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_SetsTextChannelName_resId() throws Exception {
when(mMockPackageManager.getText(eq(TEST_PACKAGE_NAME),
eq(R.string.notification_menu_accessibility), anyObject())).thenReturn(
@@ -216,7 +211,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -231,7 +225,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_SettingsTextWithOneChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
mMockStatusBarNotification, mNotificationChannel, (View v, int appUid) -> {}, null,
@@ -242,7 +235,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_SettingsTextWithMultipleChannels() throws Exception {
when(mMockINotificationManager.getNumNotificationChannelsForPackage(
eq(TEST_PACKAGE_NAME), anyInt(), anyBoolean())).thenReturn(2);
@@ -255,7 +247,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_SetsOnClickListenerForDone() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -270,7 +261,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_NumChannelsTextHiddenWhenDefaultChannel() throws Exception {
final NotificationChannel defaultChannel = new NotificationChannel(
NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
@@ -283,7 +273,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_NumChannelsTextDisplaysWhenNotDefaultChannel()
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -295,7 +284,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_NumChannelsTextScalesWithNumberOfChannels()
throws Exception {
when(mMockINotificationManager.getNumNotificationChannelsForPackage(
@@ -308,7 +296,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testbindNotification_ChannelDisabledTextGoneWhenNotDisabled() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
mMockStatusBarNotification, mNotificationChannel, null, null, null);
@@ -318,7 +305,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testbindNotification_ChannelDisabledTextVisibleWhenDisabled() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -333,7 +319,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testHasImportanceChanged_DefaultsToFalse() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
mMockStatusBarNotification, mNotificationChannel, null, null, null);
@@ -341,7 +326,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testHasImportanceChanged_ReturnsTrueAfterChannelDisabled() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -353,7 +337,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
mMockStatusBarNotification, mNotificationChannel, null, null, null);
@@ -362,7 +345,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -375,7 +357,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged()
throws Exception {
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -387,7 +368,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnspecified()
throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
@@ -400,7 +380,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testEnabledSwitchOnByDefault() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -411,7 +390,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testEnabledButtonOffWhenAlreadyBanned() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -422,7 +400,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testEnabledSwitchVisibleByDefault() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -433,7 +410,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testEnabledSwitchInvisibleIfNonBlockable() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -445,7 +421,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testEnabledSwitchChangedCallsUpdateNotificationChannel() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
@@ -460,7 +435,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- @UiThreadTest
public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
mNotificationChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
new file mode 100644
index 000000000000..96dbdb36b5a4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -0,0 +1,83 @@
+/*
+ * 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.systemui.statusbar;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.content.Context;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.view.LayoutInflater;
+import android.widget.RemoteViews;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+
+/**
+ * A helper class to create {@link ExpandableNotificationRow}
+ */
+public class NotificationTestHelper {
+
+ private final Context mContext;
+ private int mId;
+ private final NotificationGroupManager mGroupManager = new NotificationGroupManager();
+
+ public NotificationTestHelper(Context context) {
+ mContext = context;
+ }
+
+ public ExpandableNotificationRow createRow() {
+ LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+ mContext.LAYOUT_INFLATER_SERVICE);
+ ExpandableNotificationRow row = (ExpandableNotificationRow) inflater.inflate(
+ R.layout.status_bar_notification_row,
+ null, false);
+ row.setGroupManager(mGroupManager);
+ Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
+ R.drawable.ic_person)
+ .setCustomContentView(new RemoteViews(mContext.getPackageName(),
+ R.layout.custom_view_dark))
+ .build();
+ Notification notification = new Notification.Builder(mContext).setSmallIcon(
+ R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text")
+ .setPublicVersion(publicVersion)
+ .build();
+ UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+ StatusBarNotification sbn = new StatusBarNotification("com.android.systemui",
+ "com.android.systemui", mId++, null, 1000,
+ 2000, notification, mUser, null, System.currentTimeMillis());
+ NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ entry.row = row;
+ try {
+ entry.createIcons(mContext, sbn);
+ row.updateNotification(entry);
+ } catch (InflationException e) {
+ throw new RuntimeException(e.getMessage());
+ }
+ return row;
+ }
+
+ public ExpandableNotificationRow createGroup() {
+ ExpandableNotificationRow row = createRow();
+ row.addChildNotification(createRow());
+ row.addChildNotification(createRow());
+ return row;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
new file mode 100644
index 000000000000..dbe0de42fd0a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.systemui.statusbar.stack;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.NotificationHeaderView;
+import android.view.View;
+
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationTestHelper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationChildrenContainerTest {
+
+ private Context mContext;
+ private ExpandableNotificationRow mGroup;
+ private int mId;
+ private NotificationTestHelper mNotificationTestHelper;
+
+ @Before
+ @UiThreadTest
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mNotificationTestHelper = new NotificationTestHelper(mContext);
+ mGroup = mNotificationTestHelper.createGroup();
+ }
+
+ @Test
+ public void testLowPriorityHeaderCleared() {
+ mGroup.setIsLowPriority(true);
+ NotificationChildrenContainer childrenContainer = mGroup.getChildrenContainer();
+ NotificationHeaderView lowPriorityHeaderView = childrenContainer.getLowPriorityHeaderView();
+ Assert.assertTrue(lowPriorityHeaderView.getVisibility() == View.VISIBLE);
+ Assert.assertTrue(lowPriorityHeaderView.getParent() == childrenContainer);
+ mGroup.setIsLowPriority(false);
+ Assert.assertTrue(lowPriorityHeaderView.getParent() == null);
+ Assert.assertTrue(childrenContainer.getLowPriorityHeaderView() == null);
+ }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 2b219e637aed..c45de0d4ea44 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3486,6 +3486,60 @@ message MetricsEvent {
// ACTION: Settings > Battery > Menu > Apps Toggle
ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE = 852;
+ // ACTION: Settings > Any preference is changed
+ ACTION_SETTINGS_PREFERENCE_CHANGE = 853;
+
+ // FIELD: The name of preference when it is changed in Settings
+ FIELD_SETTINGS_PREFERENCE_CHANGE_NAME = 854;
+
+ // FIELD: The new value of preference when it is changed in Settings
+ FIELD_SETTINGS_PREFERENCE_CHANGE_VALUE = 855;
+
+ // OPEN: Notification channel created. CLOSE: Notification channel deleted. UPDATE: notification
+ // channel updated
+ // PACKAGE: the package the channel belongs too
+ // CATEGORY: NOTIFICATION
+ // OS: O
+ ACTION_NOTIFICATION_CHANNEL = 856;
+
+ // Tagged data for notification channel. String.
+ FIELD_NOTIFICATION_CHANNEL_ID = 857;
+
+ // Tagged data for notification channel. int.
+ FIELD_NOTIFICATION_CHANNEL_IMPORTANCE = 858;
+
+ // OPEN: Notification channel group created.
+ // PACKAGE: the package the group belongs to
+ // CATEGORY: NOTIFICATION
+ // OS: O
+ ACTION_NOTIFICATION_CHANNEL_GROUP = 859;
+
+ // Tagged data for notification channel group. String.
+ FIELD_NOTIFICATION_CHANNEL_GROUP_ID = 860;
+
+ // OPEN: Settings > Wi-Fi > Wifi Preferences -> Advanced -> Network Scorer
+ // CATEGORY: SETTINGS
+ // OS: O
+ SETTINGS_NETWORK_SCORER = 861;
+
+ // OPEN: Settings > About device > Model > Hardware info dialog
+ DIALOG_SETTINGS_HARDWARE_INFO = 862;
+
+ // ACTION: Checks whether a contact's phone still exists
+ // Value 0: It doesn't exist anymore
+ // Value 1: It still exists
+ // Value 2: A SecurityException was thrown
+ // CATEGORY: SETTINGS
+ // OS: N
+ ACTION_PHONE_EXISTS = 863;
+
+ // ACTION: Retrieves a contact from CP2
+ // Value 0: Contact retrieved without issues
+ // Value 1: An IllegalArgumentException was thrown
+ // CATEGORY: SETTINGS
+ // OS: N
+ ACTION_GET_CONTACT = 864;
+
// ---- End O Constants, all O constants go above this line ----
// Add new aosp constants above this line.
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 9d2f75080bb6..667bf71363da 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -1071,7 +1071,6 @@ public class Element extends BaseObj {
mSize += mElements[ct].mSize * mArraySizes[ct];
}
updateVisibleSubElements();
- guard.open("destroy");
}
Element(long id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
@@ -1091,7 +1090,6 @@ public class Element extends BaseObj {
mKind = dk;
mNormalized = norm;
mVectorSize = size;
- guard.open("destroy");
}
Element(long id, RenderScript rs) {
diff --git a/rs/java/android/renderscript/Type.java b/rs/java/android/renderscript/Type.java
index 9252898781f4..dc2378596d00 100644
--- a/rs/java/android/renderscript/Type.java
+++ b/rs/java/android/renderscript/Type.java
@@ -227,7 +227,6 @@ public class Type extends BaseObj {
Type(long id, RenderScript rs) {
super(id, rs);
- guard.open("destroy");
}
@Override
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
index 40491e91ea82..e943c4c388f5 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
@@ -305,14 +305,23 @@ public final class AutoFillManagerService extends SystemService {
}
@Override
+ public void setHasCallback(IBinder activityToken, int userId, boolean hasIt) {
+ synchronized (mLock) {
+ final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+ service.setHasCallback(activityToken, hasIt);
+ }
+ }
+
+ @Override
public void startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
- AutoFillId autoFillId, Rect bounds, AutoFillValue value, int userId) {
+ AutoFillId autoFillId, Rect bounds, AutoFillValue value, int userId,
+ boolean hasCallback) {
// TODO(b/33197203): make sure it's called by resumed / focused activity
synchronized (mLock) {
final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
service.startSessionLocked(activityToken, windowToken, appCallback,
- autoFillId, bounds, value);
+ autoFillId, bounds, value, hasCallback);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
index 5e852f1738f1..c4d06488f7b5 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
@@ -274,15 +274,25 @@ final class AutoFillManagerServiceImpl {
}
}
+ void setHasCallback(IBinder activityToken, boolean hasIt) {
+ if (!hasService()) {
+ return;
+ }
+ final Session session = mSessions.get(activityToken);
+ if (session != null) {
+ session.setHasCallback(hasIt);
+ }
+ }
+
void startSessionLocked(IBinder activityToken, IBinder windowToken, IBinder appCallbackToken,
- AutoFillId autoFillId, Rect bounds, AutoFillValue value) {
+ AutoFillId autoFillId, Rect bounds, AutoFillValue value, boolean hasCallback) {
if (!hasService()) {
return;
}
final String historyItem = "s=" + mInfo.getServiceInfo().packageName
+ " u=" + mUserId + " a=" + activityToken
- + " i=" + autoFillId + " b=" + bounds;
+ + " i=" + autoFillId + " b=" + bounds + " hc=" + hasCallback;
mRequestsHistory.log(historyItem);
// TODO(b/33197203): Handle partitioning
@@ -293,7 +303,7 @@ final class AutoFillManagerServiceImpl {
}
final Session newSession = createSessionByTokenLocked(activityToken,
- windowToken, appCallbackToken);
+ windowToken, appCallbackToken, hasCallback);
newSession.updateLocked(autoFillId, bounds, value, FLAG_START_SESSION);
}
@@ -312,9 +322,9 @@ final class AutoFillManagerServiceImpl {
}
private Session createSessionByTokenLocked(IBinder activityToken, IBinder windowToken,
- IBinder appCallbackToken) {
+ IBinder appCallbackToken, boolean hasCallback) {
final Session newSession = new Session(mContext, activityToken,
- windowToken, appCallbackToken);
+ windowToken, appCallbackToken, hasCallback);
mSessions.put(activityToken, newSession);
/*
@@ -599,12 +609,18 @@ final class AutoFillManagerServiceImpl {
@GuardedBy("mLock")
private AssistStructure mStructure;
+ /**
+ * Whether the client has an {@link android.view.autofill.AutoFillManager.AutofillCallback}.
+ */
+ private boolean mHasCallback;
+
private Session(Context context, IBinder activityToken, IBinder windowToken,
- IBinder client) {
+ IBinder client, boolean hasCallback) {
mRemoteFillService = new RemoteFillService(context,
mInfo.getServiceInfo().getComponentName(), mUserId, this);
mActivityToken = activityToken;
mWindowToken = windowToken;
+ mHasCallback = hasCallback;
mClient = IAutoFillManagerClient.Stub.asInterface(client);
try {
@@ -712,6 +728,14 @@ final class AutoFillManagerServiceImpl {
.sendToTarget();
}
+ // AutoFillUiCallback
+ @Override
+ public void onEvent(AutoFillId id, int event) {
+ mHandlerCaller.getHandler().post(() -> {
+ notifyChangeToClient(id, event);
+ });
+ }
+
public void setAuthenticationResultLocked(Bundle data) {
if (mCurrentResponse == null || data == null) {
removeSelf();
@@ -731,6 +755,10 @@ final class AutoFillManagerServiceImpl {
}
}
+ public void setHasCallback(boolean hasIt) {
+ mHasCallback = hasIt;
+ }
+
/**
* Show the save UI, when session can be saved.
*/
@@ -814,6 +842,7 @@ final class AutoFillManagerServiceImpl {
node.updateAutoFillValue(value);
}
+ // Sanitize structure before it's sent to service.
mStructure.sanitizeForParceling(false);
if (VERBOSE) {
@@ -871,7 +900,7 @@ final class AutoFillManagerServiceImpl {
if ((flags & FLAG_FOCUS_GAINED) != 0) {
// Remove the UI if the ViewState has changed.
if (mCurrentViewState != viewState) {
- mUi.hideFillUi();
+ mUi.hideFillUi(mCurrentViewState != null ? mCurrentViewState.mId : null);
mCurrentViewState = viewState;
}
@@ -888,7 +917,7 @@ final class AutoFillManagerServiceImpl {
if ((flags & FLAG_FOCUS_LOST) != 0) {
if (mCurrentViewState == viewState) {
- mUi.hideFillUi();
+ mUi.hideFillUi(viewState.mId);
mCurrentViewState = null;
}
return;
@@ -912,6 +941,15 @@ final class AutoFillManagerServiceImpl {
getUiForShowing().showFillUi(filledId, response, bounds, filterText);
}
+ private void notifyChangeToClient(AutoFillId id, int event) {
+ if (!mHasCallback) return;
+ try {
+ mClient.onAutofillEvent(mWindowToken, id, event);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying client on change: id=" + id + ", event=" + event, e);
+ }
+ }
+
private void processResponseLocked(FillResponse response) {
if (DEBUG) {
Slog.d(TAG, "processResponseLocked(auth=" + response.getAuthentication()
@@ -993,7 +1031,7 @@ final class AutoFillManagerServiceImpl {
pw.println("null");
}
}
-
+ pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
mRemoteFillService.dump(prefix, pw);
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 04eeb324ddf6..7058248b2802 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -135,13 +135,13 @@ final class RemoteFillService implements DeathRecipient {
public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
cancelScheduledUnbind();
- PendingFillRequest request = new PendingFillRequest(structure, extras, this);
+ final PendingFillRequest request = new PendingFillRequest(structure, extras, this);
mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
}
public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
cancelScheduledUnbind();
- PendingSaveRequest request = new PendingSaveRequest(structure, extras, this);
+ final PendingSaveRequest request = new PendingSaveRequest(structure, extras, this);
mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index f00d6c58d206..c86a8828d0b1 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -15,6 +15,9 @@
*/
package com.android.server.autofill.ui;
+import static android.view.autofill.AutoFillManager.AutofillCallback.EVENT_INPUT_HIDDEN;
+import static android.view.autofill.AutoFillManager.AutofillCallback.EVENT_INPUT_SHOWN;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -26,8 +29,8 @@ import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.service.autofill.SaveInfo;
import android.text.TextUtils;
-import android.util.Slog;
import android.text.format.DateUtils;
+import android.util.Slog;
import android.view.autofill.AutoFillId;
import android.widget.Toast;
@@ -62,6 +65,7 @@ public final class AutoFillUI {
void authenticate(@NonNull IntentSender intent);
void fill(@NonNull Dataset dataset);
void save();
+ void onEvent(AutoFillId id, int event);
}
public AutoFillUI(@NonNull Context context) {
@@ -97,8 +101,13 @@ public final class AutoFillUI {
/**
* Hides the fill UI.
*/
- public void hideFillUi() {
- mHandler.post(this::hideFillUiUiThread);
+ public void hideFillUi(AutoFillId id) {
+ mHandler.post(() -> {
+ hideFillUiUiThread();
+ if (mCallback != null) {
+ mCallback.onEvent(id, EVENT_INPUT_HIDDEN);
+ }
+ });
}
/**
@@ -175,6 +184,7 @@ public final class AutoFillUI {
// TODO(b/33197203): add MetricsLogger call
}
});
+ mCallback.onEvent(focusedId, EVENT_INPUT_SHOWN);
});
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 81f137edaa32..c9dd116c1b0e 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -678,7 +678,7 @@ public final class BatteryService extends SystemService {
pw.println("Battery service (battery) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" set [-f] [ac|usb|wireless|status|level|invalid] <value>");
+ pw.println(" set [-f] [ac|usb|wireless|status|level|present|invalid] <value>");
pw.println(" Force a battery property value, freezing battery state.");
pw.println(" -f: force a battery change broadcast be sent, prints new sequence.");
pw.println(" unplug [-f]");
@@ -748,6 +748,9 @@ public final class BatteryService extends SystemService {
}
boolean update = true;
switch (key) {
+ case "present":
+ mBatteryProps.batteryPresent = Integer.parseInt(value) != 0;
+ break;
case "ac":
mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
break;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 82d86ffd95cb..df6148e653d7 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -641,6 +641,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (appCount == 0 && mEnable) {
disableBleScanMode();
}
+ if (appCount == 0 && !mEnableExternal) {
+ sendBrEdrDownCallback();
+ }
return appCount;
}
@@ -696,7 +699,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return;
}
- if (isBleAppPresent() == false) {
+ if (isBleAppPresent()) {
+ // Need to stay at BLE ON. Disconnect all Gatt connections
+ try {
+ mBluetoothGatt.unregAll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to disconnect all apps.", e);
+ }
+ } else {
try {
mBluetoothLock.readLock().lock();
if (mBluetooth != null) mBluetooth.onBrEdrDown();
@@ -705,14 +715,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
} finally {
mBluetoothLock.readLock().unlock();
}
- } else {
- // Need to stay at BLE ON. Disconnect all Gatt connections
- try {
- mBluetoothGatt.unregAll();
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to disconnect all apps.", e);
- }
}
+
}
public boolean enableNoAutoConnect(String packageName)
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0bc9d19ab1ec..16ed2768de89 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -395,16 +395,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31;
- /**
- * Indicates a caller has requested to have its callback invoked with
- * the latest LinkProperties or NetworkCapabilities.
- *
- * arg1 = UID of caller
- * obj = NetworkRequest
- */
- private static final int EVENT_REQUEST_LINKPROPERTIES = 32;
- private static final int EVENT_REQUEST_NETCAPABILITIES = 33;
-
/** Handler thread used for both of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
@@ -2565,34 +2555,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
return nri;
}
- private void handleRequestCallbackUpdate(NetworkRequest request, int callingUid,
- String description, int callbackType) {
- final NetworkRequestInfo nri = getNriForAppRequest(request, callingUid, description);
- if (nri == null) return;
-
- final NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
- // The network that is satisfying this request may have changed since
- // the application requested the update.
- //
- // - If the request is no longer satisfied, don't send any updates.
- // - If the request is satisfied by a different network, it is the
- // caller's responsibility to check that the Network object in the
- // callback matches the network that was returned in the last
- // onAvailable() callback for this request.
- if (nai == null) return;
- callCallbackForRequest(nri, nai, callbackType, 0);
- }
-
- private void handleRequestLinkProperties(NetworkRequest request, int callingUid) {
- handleRequestCallbackUpdate(request, callingUid,
- "request LinkProperties", ConnectivityManager.CALLBACK_IP_CHANGED);
- }
-
- private void handleRequestNetworkCapabilities(NetworkRequest request, int callingUid) {
- handleRequestCallbackUpdate(request, callingUid,
- "request NetworkCapabilities", ConnectivityManager.CALLBACK_CAP_CHANGED);
- }
-
private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) {
if (mNetworkRequests.get(nri.request) != null && mNetworkForRequestId.get(
nri.request.requestId) == null) {
@@ -2986,12 +2948,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
handleMobileDataAlwaysOn();
break;
}
- case EVENT_REQUEST_LINKPROPERTIES:
- handleRequestLinkProperties((NetworkRequest) msg.obj, msg.arg1);
- break;
- case EVENT_REQUEST_NETCAPABILITIES:
- handleRequestNetworkCapabilities((NetworkRequest) msg.obj, msg.arg1);
- break;
// Sent by KeepaliveTracker to process an app request on the state machine thread.
case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
mKeepaliveTracker.handleStartKeepalive(msg);
@@ -4352,22 +4308,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
- public void requestLinkProperties(NetworkRequest networkRequest) {
- ensureNetworkRequestHasType(networkRequest);
- if (networkRequest.type == NetworkRequest.Type.LISTEN) return;
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_REQUEST_LINKPROPERTIES, getCallingUid(), 0, networkRequest));
- }
-
- @Override
- public void requestNetworkCapabilities(NetworkRequest networkRequest) {
- ensureNetworkRequestHasType(networkRequest);
- if (networkRequest.type == NetworkRequest.Type.LISTEN) return;
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_REQUEST_NETCAPABILITIES, getCallingUid(), 0, networkRequest));
- }
-
- @Override
public void releaseNetworkRequest(NetworkRequest networkRequest) {
ensureNetworkRequestHasType(networkRequest);
mHandler.sendMessage(mHandler.obtainMessage(
@@ -4879,7 +4819,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (!nr.isListen()) continue;
if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) {
nai.addRequest(nr);
- notifyNetworkCallback(nai, nri);
+ notifyNetworkAvailable(nai, nri);
}
}
}
@@ -5061,7 +5001,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// do this after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
- for (NetworkRequestInfo nri : addedRequests) notifyNetworkCallback(newNetwork, nri);
+ for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
// Linger any networks that are no longer needed. This should be done after sending the
// available callback for newNetwork.
@@ -5224,7 +5164,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
- NetworkInfo.State state = newInfo.getState();
+ final NetworkInfo.State state = newInfo.getState();
NetworkInfo oldInfo = null;
final int oldScore = networkAgent.getCurrentScore();
synchronized (networkAgent) {
@@ -5351,15 +5291,27 @@ public class ConnectivityService extends IConnectivityManager.Stub
sendUpdatedScoreToFactories(nai);
}
- // notify only this one new request of the current state
- protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
- int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
+ // Notify only this one new request of the current state. Transfer all the
+ // current state by calling NetworkCapabilities and LinkProperties callbacks
+ // so that callers can be guaranteed to have as close to atomicity in state
+ // transfer as can be supported by this current API.
+ protected void notifyNetworkAvailable(NetworkAgentInfo nai, NetworkRequestInfo nri) {
mHandler.removeMessages(EVENT_TIMEOUT_NETWORK_REQUEST, nri);
- if (nri.mPendingIntent == null) {
- callCallbackForRequest(nri, nai, notifyType, 0);
- } else {
- sendPendingIntentForRequest(nri, nai, notifyType);
+ if (nri.mPendingIntent != null) {
+ sendPendingIntentForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE);
+ // Attempt no subsequent state pushes where intents are involved.
+ return;
+ }
+
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
+ // Whether a network is currently suspended is also an important
+ // element of state to be transferred (it would not otherwise be
+ // delivered by any currently available mechanism).
+ if (nai.networkInfo.getState() == NetworkInfo.State.SUSPENDED) {
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_SUSPENDED, 0);
}
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_CAP_CHANGED, 0);
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_IP_CHANGED, 0);
}
private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 0a9610f36abb..bcee2c1bd9ea 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -2198,20 +2198,34 @@ public class LockSettingsService extends ILockSettings.Stub {
try {
// Managed profile should have escrow enabled
if (mUserManager.getUserInfo(userId).isManagedProfile()) {
+ Slog.i(TAG, "Managed profile can have escrow token");
return;
}
DevicePolicyManager dpm = (DevicePolicyManager)
mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
// Devices with Device Owner should have escrow enabled on all users.
if (dpm.getDeviceOwnerComponentOnAnyUser() != null) {
+ Slog.i(TAG, "Corp-owned device can have escrow token");
+ return;
+ }
+ // We could also have a profile owner on the given (non-managed) user for unicorn cases
+ if (dpm.getProfileOwnerAsUser(userId) != null) {
+ Slog.i(TAG, "User with profile owner can have escrow token");
return;
}
// If the device is yet to be provisioned (still in SUW), there is still
// a chance that Device Owner will be set on the device later, so postpone
// disabling escrow token for now.
if (!dpm.isDeviceProvisioned()) {
+ Slog.i(TAG, "Postpone disabling escrow tokens until device is provisioned");
+ return;
+ }
+
+ // Escrow tokens are enabled on automotive builds.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
return;
}
+
// Disable escrow token permanently on all other device/user types.
Slog.i(TAG, "Disabling escrow token on user " + userId);
if (isSyntheticPasswordBasedCredentialLocked(userId)) {
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index b83dbd686ae5..46c9f25dbccf 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -277,8 +277,8 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
private void refreshBinding() {
if (DBG) Log.d(TAG, "refreshBinding()");
- // Apply the default package name if the Setting isn't set.
- mNetworkScorerAppManager.revertToDefaultIfNoActive();
+ // Make sure the scorer is up-to-date
+ mNetworkScorerAppManager.updateState();
registerPackageMonitorIfNeeded();
bindToScoringServiceIfNeeded();
}
@@ -291,6 +291,10 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
final Uri timeoutUri = Global.getUriFor(Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS);
mContentObserver.observe(timeoutUri,
ServiceHandler.MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED);
+
+ final Uri settingUri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
+ mContentObserver.observe(settingUri,
+ ServiceHandler.MSG_RECOMMENDATION_ENABLED_SETTING_CHANGED);
}
/**
@@ -1171,6 +1175,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
public static final int MSG_RECOMMENDATION_REQUEST_TIMEOUT = 1;
public static final int MSG_RECOMMENDATIONS_PACKAGE_CHANGED = 2;
public static final int MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED = 3;
+ public static final int MSG_RECOMMENDATION_ENABLED_SETTING_CHANGED = 4;
public ServiceHandler(Looper looper) {
super(looper);
@@ -1192,6 +1197,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
break;
case MSG_RECOMMENDATIONS_PACKAGE_CHANGED:
+ case MSG_RECOMMENDATION_ENABLED_SETTING_CHANGED:
refreshBinding();
break;
diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java
index 90a33a4a217a..5b627d934209 100644
--- a/services/core/java/com/android/server/NetworkScorerAppManager.java
+++ b/services/core/java/com/android/server/NetworkScorerAppManager.java
@@ -155,6 +155,11 @@ public class NetworkScorerAppManager {
@Nullable
@VisibleForTesting
public NetworkScorerAppData getActiveScorer() {
+ final int enabledSetting = getNetworkRecommendationsEnabledSetting();
+ if (enabledSetting == NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF) {
+ return null;
+ }
+
return getScorer(getNetworkRecommendationsPackage());
}
@@ -194,39 +199,73 @@ public class NetworkScorerAppManager {
*/
@VisibleForTesting
public boolean setActiveScorer(String packageName) {
- String oldPackageName = getNetworkRecommendationsPackage();
+ final String oldPackageName = getNetworkRecommendationsPackage();
+
if (TextUtils.equals(oldPackageName, packageName)) {
// No change.
return true;
}
- Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
-
if (packageName == null) {
// revert to the default setting.
- setNetworkRecommendationsPackage(getDefaultPackageSetting());
+ packageName = getDefaultPackageSetting();
+ }
+
+ Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
+
+ // We only make the change if the new package is valid.
+ if (getScorer(packageName) != null) {
+ setNetworkRecommendationsPackage(packageName);
return true;
} else {
- // We only make the change if the new package is valid.
- if (getScorer(packageName) != null) {
- setNetworkRecommendationsPackage(packageName);
- return true;
- } else {
- Log.w(TAG, "Requested network scorer is not valid: " + packageName);
- return false;
- }
+ Log.w(TAG, "Requested network scorer is not valid: " + packageName);
+ return false;
}
}
/**
- * If the active scorer is null then revert to the default scorer.
+ * Ensures the {@link Settings.Global#NETWORK_RECOMMENDATIONS_PACKAGE} setting points to a valid
+ * package and {@link Settings.Global#NETWORK_RECOMMENDATIONS_ENABLED} is consistent.
+ *
+ * If {@link Settings.Global#NETWORK_RECOMMENDATIONS_PACKAGE} doesn't point to a valid package
+ * then it will be reverted to the default package specified by
+ * {@link R.string#config_defaultNetworkRecommendationProviderPackage}. If the default package
+ * is no longer valid then {@link Settings.Global#NETWORK_RECOMMENDATIONS_ENABLED} will be set
+ * to <code>0</code> (disabled).
*/
@VisibleForTesting
- public void revertToDefaultIfNoActive() {
- if (getActiveScorer() == null) {
- final String defaultPackage = getDefaultPackageSetting();
- setNetworkRecommendationsPackage(defaultPackage);
- Log.i(TAG, "Defaulted the network recommendations app to: " + defaultPackage);
+ public void updateState() {
+ final int enabledSetting = getNetworkRecommendationsEnabledSetting();
+ if (enabledSetting == NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF) {
+ // Don't change anything if it's forced off.
+ if (DEBUG) Log.d(TAG, "Recommendations forced off.");
+ return;
+ }
+
+ // First, see if the current package is still valid. If so, then we can exit early.
+ final String currentPackageName = getNetworkRecommendationsPackage();
+ if (getScorer(currentPackageName) != null) {
+ if (VERBOSE) Log.v(TAG, currentPackageName + " is the active scorer.");
+ setNetworkRecommendationsEnabledSetting(NetworkScoreManager.RECOMMENDATIONS_ENABLED_ON);
+ return;
+ }
+
+ // the active scorer isn't valid, revert to the default if it's different
+ final String defaultPackageName = getDefaultPackageSetting();
+ if (!TextUtils.equals(currentPackageName, defaultPackageName)) {
+ setNetworkRecommendationsPackage(defaultPackageName);
+ if (DEBUG) {
+ Log.d(TAG, "Defaulted the network recommendations app to: " + defaultPackageName);
+ }
+ if (getScorer(defaultPackageName) != null) { // the default is valid
+ if (DEBUG) Log.d(TAG, defaultPackageName + " is now the active scorer.");
+ setNetworkRecommendationsEnabledSetting(
+ NetworkScoreManager.RECOMMENDATIONS_ENABLED_ON);
+ } else { // the default isn't valid either, we're disabled at this point
+ if (DEBUG) Log.d(TAG, defaultPackageName + " is not an active scorer.");
+ setNetworkRecommendationsEnabledSetting(
+ NetworkScoreManager.RECOMMENDATIONS_ENABLED_OFF);
+ }
}
}
@@ -244,6 +283,15 @@ public class NetworkScorerAppManager {
Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, packageName);
}
+ private int getNetworkRecommendationsEnabledSetting() {
+ return mSettingsFacade.getInt(mContext, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0);
+ }
+
+ private void setNetworkRecommendationsEnabledSetting(int value) {
+ mSettingsFacade.putInt(mContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, value);
+ }
+
/**
* Wrapper around Settings to make testing easier.
*/
@@ -255,5 +303,13 @@ public class NetworkScorerAppManager {
public String getString(Context context, String name) {
return Settings.Global.getString(context.getContentResolver(), name);
}
+
+ public boolean putInt(Context context, String name, int value) {
+ return Settings.Global.putInt(context.getContentResolver(), name, value);
+ }
+
+ public int getInt(Context context, String name, int defaultValue) {
+ return Settings.Global.getInt(context.getContentResolver(), name, defaultValue);
+ }
}
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index dc73b6385897..ea9b65198e4b 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -503,10 +503,10 @@ public class AccountManagerService
UserHandle.getUserId(callingUid));
} catch (NameNotFoundException e) {
Log.d(TAG, "Package not found " + e.getMessage());
- return new HashMap<>();
+ return new LinkedHashMap<>();
}
- Map<Account, Integer> result = new HashMap<>();
+ Map<Account, Integer> result = new LinkedHashMap<>();
for (String accountType : accountTypes) {
synchronized (accounts.cacheLock) {
final Account[] accountsOfType = accounts.accountCache.get(accountType);
@@ -1040,7 +1040,7 @@ public class AccountManagerService
private static HashMap<String, Integer> getAuthenticatorTypeAndUIDForUser(
IAccountAuthenticatorCache authCache,
int userId) {
- HashMap<String, Integer> knownAuth = new HashMap<>();
+ HashMap<String, Integer> knownAuth = new LinkedHashMap<>();
for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : authCache
.getAllServices(userId)) {
knownAuth.put(service.type.type, service.uid);
@@ -3927,7 +3927,7 @@ public class AccountManagerService
List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId,
opPackageName);
if (visibleAccountTypes.isEmpty()) {
- return new Account[0];
+ return EMPTY_ACCOUNT_ARRAY;
}
long identityToken = clearCallingIdentity();
try {
@@ -4047,7 +4047,7 @@ public class AccountManagerService
opPackageName);
if (visibleAccountTypes.isEmpty()
|| (type != null && !visibleAccountTypes.contains(type))) {
- return new Account[]{};
+ return EMPTY_ACCOUNT_ARRAY;
} else if (visibleAccountTypes.contains(type)) {
// Prune the list down to just the requested type.
visibleAccountTypes = new ArrayList<>();
@@ -4194,11 +4194,11 @@ public class AccountManagerService
packageUid = mPackageManager.getPackageUidAsUser(packageName, userId);
} catch (NameNotFoundException re) {
Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
- return new Account[0];
+ return EMPTY_ACCOUNT_ARRAY;
}
if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
&& !isAccountManagedByCaller(type, callingUid, userId)) {
- return new Account[0];
+ return EMPTY_ACCOUNT_ARRAY;
}
return getAccountsAsUser(type, userId,
@@ -4229,7 +4229,7 @@ public class AccountManagerService
if (!visibleAccountTypes.contains(type)) {
Bundle result = new Bundle();
// Need to return just the accounts that are from matching signatures.
- result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
+ result.putParcelableArray(AccountManager.KEY_ACCOUNTS, EMPTY_ACCOUNT_ARRAY);
try {
response.onResult(result);
} catch (RemoteException e) {
@@ -5368,10 +5368,11 @@ public class AccountManagerService
return newAccountsForType[oldLength];
}
+ @NonNull
private Account[] filterAccounts(UserAccounts accounts, Account[] unfiltered, int callingUid,
String callingPackage, boolean includeManagedNotVisible) {
// filter based on visibility.
- Map<Account, Integer> firstPass = new HashMap<>();
+ Map<Account, Integer> firstPass = new LinkedHashMap<>();
for (Account account : unfiltered) {
int visibility = resolveAccountVisibility(account, callingPackage, accounts);
if ((visibility == AccountManager.VISIBILITY_VISIBLE
@@ -5390,8 +5391,10 @@ public class AccountManagerService
return filtered;
}
+ @NonNull
private Map<Account, Integer> filterSharedAccounts(UserAccounts userAccounts,
- Map<Account, Integer> unfiltered, int callingUid, String callingPackage) {
+ @NonNull Map<Account, Integer> unfiltered, int callingUid,
+ String callingPackage) {
// first part is to filter shared accounts.
// unfiltered type check is not necessary.
if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
@@ -5441,7 +5444,7 @@ public class AccountManagerService
} catch (NameNotFoundException e) {
Log.d(TAG, "Package not found " + e.getMessage());
}
- Map<Account, Integer> filtered = new HashMap<>();
+ Map<Account, Integer> filtered = new LinkedHashMap<>();
for (Map.Entry<Account, Integer> entry : unfiltered.entrySet()) {
Account account = entry.getKey();
if (account.type.equals(requiredAccountType)) {
@@ -5469,6 +5472,7 @@ public class AccountManagerService
* packageName can be null. If not null, it should be used to filter out restricted accounts
* that the package is not allowed to access.
*/
+ @NonNull
protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
int callingUid, String callingPackage, boolean includeManagedNotVisible) {
if (callingPackage == null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 54ee5dc43034..10b1f2bc1a3b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10088,8 +10088,18 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
- * Moves an activity, and all of the other activities within the same task, to the bottom
- * of the history stack. The activity's order within the task is unchanged.
+ * Attempts to move a task backwards in z-order (the order of activities within the task is
+ * unchanged).
+ *
+ * There are several possible results of this call:
+ * - if the task is locked, then we will show the lock toast
+ * - if there is a task behind the provided task, then that task is made visible and resumed as
+ * this task is moved to the back
+ * - otherwise, if there are no other tasks in the stack:
+ * - if this task is in the pinned stack, then we remove the stack completely, which will
+ * have the effect of moving the task to the top or bottom of the fullscreen stack
+ * (depending on whether it is visible)
+ * - otherwise, we simply return home and hide this task
*
* @param token A reference to the activity we wish to move
* @param nonRoot If false then this only works if the activity is the root
@@ -10105,10 +10115,6 @@ public class ActivityManagerService extends IActivityManager.Stub
int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task != null) {
- if (mStackSupervisor.isLockedTask(task)) {
- mStackSupervisor.showLockTaskToast();
- return false;
- }
return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
}
} finally {
@@ -10367,7 +10373,7 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized (this) {
if (!mSupportsPictureInPicture) {
throw new IllegalStateException("moveTopActivityToPinnedStack:"
- + "Device doesn't support picture-in-pciture mode");
+ + "Device doesn't support picture-in-picture mode");
}
long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5d4bff97b1b1..a679a312899d 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4351,9 +4351,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
Slog.i(TAG, "moveTaskToBack: bad taskId=" + taskId);
return false;
}
-
Slog.i(TAG, "moveTaskToBack: " + tr);
- mStackSupervisor.removeLockedTaskLocked(tr);
+
+ // If the task is locked, then show the lock task toast
+ if (mStackSupervisor.isLockedTask(tr)) {
+ mStackSupervisor.showLockTaskToast();
+ return false;
+ }
// If we have a watcher, preflight the move before committing to it. First check
// for *other* available tasks, but if none are available, then try again allowing the
@@ -4416,12 +4420,24 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
prevIsHome = true;
}
}
- mTaskHistory.remove(tr);
- mTaskHistory.add(0, tr);
- updateTaskMovement(tr, false);
- // There is an assumption that moving a task to the back moves it behind the home activity.
- // We make sure here that some activity in the stack will launch home.
+ boolean requiresMove = mTaskHistory.indexOf(tr) != 0;
+ if (requiresMove) {
+ mTaskHistory.remove(tr);
+ mTaskHistory.add(0, tr);
+ updateTaskMovement(tr, false);
+
+ mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
+ mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController());
+ }
+
+ if (mStackId == PINNED_STACK_ID) {
+ mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
+ return true;
+ }
+
+ // Otherwise, there is an assumption that moving a task to the back moves it behind the
+ // home activity. We make sure here that some activity in the stack will launch home.
int numTasks = mTaskHistory.size();
for (int taskNdx = numTasks - 1; taskNdx >= 1; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -4434,9 +4450,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
- mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
- mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController());
-
final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null;
if (prevIsHome || (task == tr && canGoHome) || (numTasks <= 1 && isOnHomeDisplay())) {
if (!mService.mBooting && !mService.mBooted) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6e138a161e1c..bd7086165a07 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2770,7 +2770,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// while pausing because that changes the focused stack and may prevent the new
// starting activity from resuming.
if (moveHomeStackToFront && task.getTaskToReturnTo() == HOME_ACTIVITY_TYPE
- && !r.supportsPictureInPictureWhilePausing) {
+ && (r.state == RESUMED || !r.supportsPictureInPictureWhilePausing)) {
// Move the home stack forward if the task we just moved to the pinned stack
// was launched from home so home should be visible behind it.
moveHomeStackToFront(reason);
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index abdcfe78a516..b3f1548d4d92 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -46,9 +46,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
/**
* set to true so the framework enforces ducking itself, without communicating to apps
- * that they lost focus.
+ * that they lost focus for most use cases.
*/
- static final boolean ENFORCE_DUCKING = false;
+ static final boolean ENFORCE_DUCKING = true;
/**
* set to true so the framework enforces muting media/game itself when the device is ringing
* or in a call.
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 6932427ba969..82a0ff690b62 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -309,7 +309,7 @@ public final class PlaybackActivityMonitor
return false;
} else {
try {
- if (DEBUG) { Log.v(TAG, "ducking player " + piid); }
+ Log.v(TAG, "ducking player " + piid);
apc.getPlayerProxy().applyVolumeShaper(
DUCK_VSHAPE,
PLAY_CREATE_IF_NEEDED);
@@ -339,7 +339,7 @@ public final class PlaybackActivityMonitor
if (apc != null
&& winner.hasSameUid(apc.getClientUid())) {
try {
- if (DEBUG) { Log.v(TAG, "unducking player" + piid); }
+ Log.v(TAG, "unducking player" + piid);
mDuckedPlayers.remove(new Integer(piid));
apc.getPlayerProxy().applyVolumeShaper(
DUCK_ID,
@@ -381,11 +381,11 @@ public final class PlaybackActivityMonitor
}
if (mute) {
try {
- if (DEBUG) { Log.v(TAG, "muting player" + piid); }
+ Log.v(TAG, "call: muting player" + piid);
apc.getPlayerProxy().setVolume(0.0f);
mMutedPlayers.add(piid);
} catch (Exception e) {
- Log.e(TAG, "Error muting player " + piid, e);
+ Log.e(TAG, "call: error muting player " + piid, e);
}
}
}
@@ -405,10 +405,10 @@ public final class PlaybackActivityMonitor
final AudioPlaybackConfiguration apc = mPlayers.get(piid);
if (apc != null) {
try {
- if (DEBUG) { Log.v(TAG, "unmuting player" + piid); }
+ Log.v(TAG, "call: unmuting player" + piid);
apc.getPlayerProxy().setVolume(1.0f);
} catch (Exception e) {
- Log.e(TAG, "Error unmuting player " + piid, e);
+ Log.e(TAG, "call: error unmuting player " + piid, e);
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 39e3758393df..60357c2befb9 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -77,6 +77,7 @@ import com.android.internal.util.StateMachine;
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.IPv6TetheringInterfaceServices;
+import com.android.server.connectivity.tethering.OffloadController;
import com.android.server.connectivity.tethering.TetheringConfiguration;
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
@@ -146,6 +147,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
.getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
private final StateMachine mTetherMasterSM;
+ private final OffloadController mOffloadController;
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
private String mCurrentUpstreamIface;
@@ -176,6 +178,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
mTetherMasterSM.start();
+ mOffloadController = new OffloadController(mTetherMasterSM.getHandler());
mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
@@ -1205,6 +1208,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
protected void handleNewUpstreamNetworkState(NetworkState ns) {
mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
+ mOffloadController.setUpstreamLinkProperties(
+ (ns != null) ? ns.linkProperties : null);
}
}
@@ -1361,12 +1366,14 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
class TetherModeAliveState extends TetherMasterUtilState {
final SimChangeListener simChange = new SimChangeListener(mContext);
boolean mTryCell = true;
+
@Override
public void enter() {
// TODO: examine if we should check the return value.
turnOnMasterTetherSettings(); // may transition us out
simChange.startListening();
mUpstreamNetworkMonitor.start();
+ mOffloadController.start();
// Better try something first pass or crazy tests cases will fail.
chooseUpstreamType(true);
@@ -1375,6 +1382,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
@Override
public void exit() {
+ mOffloadController.stop();
unrequestUpstreamMobileConnection();
mUpstreamNetworkMonitor.stop();
simChange.stopListening();
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
new file mode 100644
index 000000000000..220e7514facc
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -0,0 +1,53 @@
+/*
+ * 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.server.connectivity.tethering;
+
+import android.net.LinkProperties;
+import android.os.Handler;
+import android.util.Log;
+
+/**
+ * A wrapper around hardware offload interface.
+ *
+ * @hide
+ */
+public class OffloadController {
+ private static final String TAG = OffloadController.class.getSimpleName();
+
+ private final Handler mHandler;
+ private LinkProperties mUpstreamLinkProperties;
+
+ public OffloadController(Handler h) {
+ mHandler = h;
+ }
+
+ public void start() {
+ // TODO: initOffload() and configure callbacks to be handled on our
+ // preferred Handler.
+ Log.d(TAG, "tethering offload not supported");
+ }
+
+ public void stop() {
+ // TODO: stopOffload().
+ mUpstreamLinkProperties = null;
+ }
+
+ public void setUpstreamLinkProperties(LinkProperties lp) {
+ // TODO: setUpstreamParameters().
+ mUpstreamLinkProperties = lp;
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 6106093e4008..62099293b31c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -186,6 +186,7 @@ public class UpstreamNetworkMonitor {
switch (callbackType) {
case CALLBACK_LISTEN_ALL:
break;
+
case CALLBACK_TRACK_DEFAULT:
if (mDefaultNetworkCallback == null) {
// The callback was unregistered in the interval between
@@ -198,11 +199,9 @@ public class UpstreamNetworkMonitor {
// These request*() calls can be deleted post oag/339444.
return;
}
-
- cm().requestNetworkCapabilities(mDefaultNetworkCallback);
- cm().requestLinkProperties(mDefaultNetworkCallback);
mCurrentDefault = network;
break;
+
case CALLBACK_MOBILE_REQUEST:
if (mMobileNetworkCallback == null) {
// The callback was unregistered in the interval between
@@ -211,13 +210,8 @@ public class UpstreamNetworkMonitor {
//
// Clean-up of this network entry is deferred to the
// handling of onLost() by other callbacks.
- //
- // These request*() calls can be deleted post oag/339444.
return;
}
-
- cm().requestNetworkCapabilities(mMobileNetworkCallback);
- cm().requestLinkProperties(mMobileNetworkCallback);
break;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 4c6b8328e213..fc86d68cea7b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1659,6 +1659,17 @@ public final class HdmiControlService extends SystemService {
}
@Override
+ public void setStandbyMode(final boolean isStandbyModeOn) {
+ enforceAccessPermission();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.setStandbyMode(isStandbyModeOn);
+ }
+ });
+ }
+
+ @Override
protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -2203,6 +2214,29 @@ public final class HdmiControlService extends SystemService {
}
}
+ void setStandbyMode(boolean isStandbyModeOn) {
+ assertRunOnServiceThread();
+ if (isPowerOnOrTransient() && isStandbyModeOn) {
+ mPowerManager.goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_HDMI, 0);
+ if (playback() != null) {
+ playback().sendStandby(0 /* unused */);
+ }
+ } else if (isPowerStandbyOrTransient() && !isStandbyModeOn) {
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.hdmi:WAKE");
+ if (playback() != null) {
+ oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ if (result != HdmiControlManager.RESULT_SUCCESS) {
+ Slog.w(TAG, "Failed to complete 'one touch play'. result=" + result);
+ }
+ }
+ });
+ }
+ }
+ }
+
boolean isProhibitMode() {
synchronized (mLock) {
return mProhibitMode;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0e767daf76c8..be8aaf02db41 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2792,6 +2792,7 @@ public class NotificationManagerService extends SystemService {
dump.put("bans", mRankingHelper.dumpBansJson(filter));
dump.put("ranking", mRankingHelper.dumpJson(filter));
dump.put("stats", mUsageStats.dumpJson(filter));
+ dump.put("channels", mRankingHelper.dumpChannelsJson(filter));
} catch (JSONException e) {
e.printStackTrace();
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 3016b17db7b9..d751a2258dc6 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -778,9 +778,9 @@ public final class NotificationRecord {
.addTaggedData(MetricsEvent.NOTIFICATION_TAG, sbn.getTag());
}
return mLogMaker
- .setCategory(MetricsEvent.VIEW_UNKNOWN)
- .setType(MetricsEvent.TYPE_UNKNOWN)
- .setSubtype(0)
+ .clearCategory()
+ .clearType()
+ .clearSubtype()
.clearTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX)
.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, getLifespanMs(now))
.addTaggedData(MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS, getFreshnessMs(now))
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index bb0742adf465..60970712394a 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -19,6 +19,8 @@ import static android.app.NotificationManager.IMPORTANCE_NONE;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.Preconditions;
import android.app.Notification;
@@ -30,6 +32,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
+import android.metrics.LogMaker;
import android.os.Build;
import android.os.UserHandle;
import android.provider.Settings;
@@ -485,6 +488,12 @@ public class RankingHelper implements RankingConfig {
if (r == null) {
throw new IllegalArgumentException("Invalid package");
}
+ LogMaker lm = new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL_GROUP)
+ .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
+ .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_GROUP_ID,
+ group.getId())
+ .setPackageName(pkg);
+ MetricsLogger.action(lm);
r.groups.put(group.getId(), group);
updateConfig();
}
@@ -507,14 +516,21 @@ public class RankingHelper implements RankingConfig {
if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
}
+ if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
+ throw new IllegalArgumentException("Reserved id");
+ }
NotificationChannel existing = r.channels.get(channel.getId());
- // Keep existing settings
- if (existing != null) {
+ // Keep existing settings, except deleted status and name
+ if (existing != null && fromTargetApp) {
if (existing.isDeleted()) {
existing.setDeleted(false);
- updateConfig();
}
+
+ existing.setNameResId(channel.getNameResId());
+
+ MetricsLogger.action(getChannelLog(channel, pkg));
+ updateConfig();
return;
}
if (channel.getImportance() < NotificationManager.IMPORTANCE_NONE
@@ -538,6 +554,8 @@ public class RankingHelper implements RankingConfig {
Notification.AUDIO_ATTRIBUTES_DEFAULT);
}
r.channels.put(channel.getId(), channel);
+ MetricsLogger.action(getChannelLog(channel, pkg).setType(
+ MetricsProto.MetricsEvent.TYPE_OPEN));
updateConfig();
}
@@ -565,6 +583,8 @@ public class RankingHelper implements RankingConfig {
updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
}
r.channels.put(updatedChannel.getId(), updatedChannel);
+
+ MetricsLogger.action(getChannelLog(updatedChannel, pkg));
updateConfig();
}
@@ -612,6 +632,7 @@ public class RankingHelper implements RankingConfig {
}
// Assistant cannot change the group
+ MetricsLogger.action(getChannelLog(channel, pkg));
r.channels.put(channel.getId(), channel);
updateConfig();
}
@@ -661,6 +682,9 @@ public class RankingHelper implements RankingConfig {
if (channel != null) {
channel.setDeleted(true);
}
+ LogMaker lm = getChannelLog(channel, pkg);
+ lm.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
+ MetricsLogger.action(lm);
}
@Override
@@ -932,6 +956,49 @@ public class RankingHelper implements RankingConfig {
return packageBans;
}
+ /**
+ * Dump only the channel information as structured JSON for the stats collector.
+ *
+ * This is intentionally redundant with {#link dumpJson} because the old
+ * scraper will expect this format.
+ *
+ * @param filter
+ * @return
+ */
+ public JSONArray dumpChannelsJson(NotificationManagerService.DumpFilter filter) {
+ JSONArray channels = new JSONArray();
+ Map<String, Integer> packageChannels = getPackageChannels();
+ for(Entry<String, Integer> channelCount : packageChannels.entrySet()) {
+ final String packageName = channelCount.getKey();
+ if (filter == null || filter.matches(packageName)) {
+ JSONObject channelCountJson = new JSONObject();
+ try {
+ channelCountJson.put("packageName", packageName);
+ channelCountJson.put("channelCount", channelCount.getValue());
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ channels.put(channelCountJson);
+ }
+ }
+ return channels;
+ }
+
+ private Map<String, Integer> getPackageChannels() {
+ ArrayMap<String, Integer> packageChannels = new ArrayMap<>();
+ for (int i = 0; i < mRecords.size(); i++) {
+ final Record r = mRecords.valueAt(i);
+ int channelCount = 0;
+ for (int j = 0; j < r.channels.size();j++) {
+ if (!r.channels.valueAt(j).isDeleted()) {
+ channelCount++;
+ }
+ }
+ packageChannels.put(r.pkg, channelCount);
+ }
+ return packageChannels;
+ }
+
public void onPackagesChanged(boolean removingPackage, int changeUserId, String[] pkgList,
int[] uidList) {
if (pkgList == null || pkgList.length == 0) {
@@ -979,6 +1046,16 @@ public class RankingHelper implements RankingConfig {
}
}
+ private LogMaker getChannelLog(NotificationChannel channel, String pkg) {
+ return new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTIFICATION_CHANNEL)
+ .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
+ .setPackageName(pkg)
+ .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_ID,
+ channel.getId())
+ .addTaggedData(MetricsProto.MetricsEvent.FIELD_NOTIFICATION_CHANNEL_IMPORTANCE,
+ channel.getImportance());
+ }
+
private static class Record {
static int UNKNOWN_UID = UserHandle.USER_NULL;
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index a692559129ad..2026c1b176ba 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -53,9 +53,11 @@ import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.util.ConcurrentUtils;
import com.android.server.FgThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
+import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.pm.Installer;
import com.android.server.pm.UserManagerService;
@@ -74,6 +76,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -219,6 +222,8 @@ public final class OverlayManagerService extends SystemService {
private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
+ private Future<?> mInitCompleteSignal;
+
public OverlayManagerService(@NonNull final Context context,
@NonNull final Installer installer) {
super(context);
@@ -230,28 +235,29 @@ public final class OverlayManagerService extends SystemService {
mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
getDefaultOverlayPackages());
+ mInitCompleteSignal = SystemServerInitThreadPool.get().submit(() -> {
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+ getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
+ packageFilter, null, null);
+
+ final IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(ACTION_USER_REMOVED);
+ getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
+ userFilter, null, null);
+
+ restoreSettings();
+ onSwitchUser(UserHandle.USER_SYSTEM);
+ schedulePersistSettings();
- final IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(ACTION_PACKAGE_ADDED);
- packageFilter.addAction(ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
- getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
- packageFilter, null, null);
-
- final IntentFilter userFilter = new IntentFilter();
- userFilter.addAction(ACTION_USER_REMOVED);
- getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL,
- userFilter, null, null);
-
- restoreSettings();
- onSwitchUser(UserHandle.USER_SYSTEM);
- schedulePersistSettings();
-
- mSettings.addChangeListener(new OverlayChangeListener());
+ mSettings.addChangeListener(new OverlayChangeListener());
- publishBinderService(Context.OVERLAY_SERVICE, mService);
- publishLocalService(OverlayManagerService.class, this);
+ publishBinderService(Context.OVERLAY_SERVICE, mService);
+ publishLocalService(OverlayManagerService.class, this);
+ }, "Init OverlayManagerService");
}
@Override
@@ -260,6 +266,15 @@ public final class OverlayManagerService extends SystemService {
}
@Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
+ "Wait for OverlayManagerService init");
+ mInitCompleteSignal = null;
+ }
+ }
+
+ @Override
public void onSwitchUser(final int newUserId) {
// ensure overlays in the settings are up-to-date, and propagate
// any asset changes to the rest of the system
@@ -489,6 +504,25 @@ public final class OverlayManagerService extends SystemService {
}
@Override
+ public boolean setEnabledExclusive(@Nullable final String packageName, final boolean enable,
+ int userId) throws RemoteException {
+ enforceChangeOverlayPackagesPermission("setEnabled");
+ userId = handleIncomingUser(userId, "setEnabled");
+ if (packageName == null) {
+ return false;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ return mImpl.setEnabledExclusive(packageName, enable, userId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public boolean setPriority(@Nullable final String packageName,
@Nullable final String parentPackageName, int userId) throws RemoteException {
enforceChangeOverlayPackagesPermission("setPriority");
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index ed493833139a..b085179c7fc5 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -301,6 +301,38 @@ final class OverlayManagerServiceImpl {
}
}
+ boolean setEnabledExclusive(@NonNull final String packageName, final boolean enable,
+ final int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
+ packageName, enable, userId));
+ }
+
+ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
+ if (overlayPackage == null) {
+ return false;
+ }
+
+ try {
+ final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
+ List<OverlayInfo> allOverlays = getOverlayInfosForTarget(oi.targetPackageName, userId);
+
+ // Disable all other overlays.
+ allOverlays.remove(oi);
+ for (int i = 0; i < allOverlays.size(); i++) {
+ mSettings.setEnabled(allOverlays.get(i).packageName, userId, false);
+ }
+
+ final PackageInfo targetPackage =
+ mPackageManager.getPackageInfo(oi.targetPackageName, userId);
+ mSettings.setEnabled(packageName, userId, enable);
+ updateState(targetPackage, overlayPackage, userId);
+ return true;
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ return false;
+ }
+ }
+
boolean setPriority(@NonNull final String packageName,
@NonNull final String newParentPackageName, final int userId) {
return mSettings.setPriority(packageName, newParentPackageName, userId);
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index ff5c594c3dde..2262a2e0c208 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -360,7 +360,7 @@ final class OverlayManagerSettings {
private static final String ATTR_USER_ID = "userId";
private static final String ATTR_VERSION = "version";
- private static final int CURRENT_VERSION = 1;
+ private static final int CURRENT_VERSION = 2;
public static void restore(@NonNull final ArrayList<SettingsItem> table,
@NonNull final InputStream is) throws IOException, XmlPullParserException {
@@ -372,7 +372,7 @@ final class OverlayManagerSettings {
XmlUtils.beginDocument(parser, TAG_OVERLAYS);
int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION);
if (version != CURRENT_VERSION) {
- throw new XmlPullParserException("unrecognized version " + version);
+ upgrade(version);
}
int depth = parser.getDepth();
@@ -387,6 +387,18 @@ final class OverlayManagerSettings {
}
}
+ private static void upgrade(int oldVersion) throws XmlPullParserException {
+ switch (oldVersion) {
+ case 0:
+ case 1:
+ // Throw an exception which will cause the overlay file to be ignored
+ // and overwritten.
+ throw new XmlPullParserException("old version " + oldVersion + "; ignoring");
+ default:
+ throw new XmlPullParserException("unrecognized version " + oldVersion);
+ }
+ }
+
private static SettingsItem restoreRow(@NonNull final XmlPullParser parser, final int depth)
throws IOException {
final String packageName = XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6450a46415f9..e2358c2d4eee 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8721,60 +8721,9 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
destroyAppProfilesLeafLIF(pkg);
- destroyAppReferenceProfileLeafLIF(pkg, userId, true /* removeBaseMarker */);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
destroyAppProfilesLeafLIF(pkg.childPackages.get(i));
- destroyAppReferenceProfileLeafLIF(pkg.childPackages.get(i), userId,
- true /* removeBaseMarker */);
- }
- }
-
- private void destroyAppReferenceProfileLeafLIF(PackageParser.Package pkg, int userId,
- boolean removeBaseMarker) {
- if (pkg.isForwardLocked()) {
- return;
- }
-
- for (String path : pkg.getAllCodePathsExcludingResourceOnly()) {
- try {
- path = PackageManagerServiceUtils.realpath(new File(path));
- } catch (IOException e) {
- // TODO: Should we return early here ?
- Slog.w(TAG, "Failed to get canonical path", e);
- continue;
- }
-
- final String useMarker = path.replace('/', '@');
- for (int realUserId : resolveUserIds(userId)) {
- File profileDir = Environment.getDataProfilesDeForeignDexDirectory(realUserId);
- if (removeBaseMarker) {
- File foreignUseMark = new File(profileDir, useMarker);
- if (foreignUseMark.exists()) {
- if (!foreignUseMark.delete()) {
- Slog.w(TAG, "Unable to delete foreign user mark for package: "
- + pkg.packageName);
- }
- }
- }
-
- File[] markers = profileDir.listFiles();
- if (markers != null) {
- final String searchString = "@" + pkg.packageName + "@";
- // We also delete all markers that contain the package name we're
- // uninstalling. These are associated with secondary dex-files belonging
- // to the package. Reconstructing the path of these dex files is messy
- // in general.
- for (File marker : markers) {
- if (marker.getName().indexOf(searchString) > 0) {
- if (!marker.delete()) {
- Slog.w(TAG, "Unable to delete foreign user mark for package: "
- + pkg.packageName);
- }
- }
- }
- }
- }
}
}
@@ -8792,10 +8741,6 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
clearAppProfilesLeafLIF(pkg);
- // We don't remove the base foreign use marker when clearing profiles because
- // we will rename it when the app is updated. Unlike the actual profile contents,
- // the foreign use marker is good across installs.
- destroyAppReferenceProfileLeafLIF(pkg, userId, false /* removeBaseMarker */);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
clearAppProfilesLeafLIF(pkg.childPackages.get(i));
@@ -10056,15 +10001,6 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
// We don't expect installation to fail beyond this point
- if (pkgSetting.pkg != null) {
- // Note that |user| might be null during the initial boot scan. If a codePath
- // for an app has changed during a boot scan, it's due to an app update that's
- // part of the system partition and marker changes must be applied to all users.
- final int userId = ((user != null) ? user : UserHandle.ALL).getIdentifier();
- final int[] userIds = resolveUserIds(userId);
- maybeRenameForeignDexMarkers(pkgSetting.pkg, pkg, userIds);
- }
-
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
@@ -10398,74 +10334,6 @@ public class PackageManagerService extends IPackageManager.Stub {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- private static void maybeRenameForeignDexMarkers(PackageParser.Package existing,
- PackageParser.Package update, int[] userIds) {
- if (existing.applicationInfo == null || update.applicationInfo == null) {
- // This isn't due to an app installation.
- return;
- }
-
- final File oldCodePath = new File(existing.applicationInfo.getCodePath());
- final File newCodePath = new File(update.applicationInfo.getCodePath());
-
- // The codePath hasn't changed, so there's nothing for us to do.
- if (Objects.equals(oldCodePath, newCodePath)) {
- return;
- }
-
- File canonicalNewCodePath;
- try {
- canonicalNewCodePath = new File(PackageManagerServiceUtils.realpath(newCodePath));
- } catch (IOException e) {
- Slog.w(TAG, "Failed to get canonical path.", e);
- return;
- }
-
- // This is a bit of a hack. The oldCodePath doesn't exist at this point (because
- // we've already renamed / deleted it) so we cannot call realpath on it. Here we assume
- // that the last component of the path (i.e, the name) doesn't need canonicalization
- // (i.e, that it isn't ".", ".." or a symbolic link). This is a valid assumption for now
- // but may change in the future. Hopefully this function won't exist at that point.
- final File canonicalOldCodePath = new File(canonicalNewCodePath.getParentFile(),
- oldCodePath.getName());
-
- // Calculate the prefixes of the markers. These are just the paths with "/" replaced
- // with "@".
- String oldMarkerPrefix = canonicalOldCodePath.getAbsolutePath().replace('/', '@');
- if (!oldMarkerPrefix.endsWith("@")) {
- oldMarkerPrefix += "@";
- }
- String newMarkerPrefix = canonicalNewCodePath.getAbsolutePath().replace('/', '@');
- if (!newMarkerPrefix.endsWith("@")) {
- newMarkerPrefix += "@";
- }
-
- List<String> updatedPaths = update.getAllCodePathsExcludingResourceOnly();
- List<String> markerSuffixes = new ArrayList<String>(updatedPaths.size());
- for (String updatedPath : updatedPaths) {
- String updatedPathName = new File(updatedPath).getName();
- markerSuffixes.add(updatedPathName.replace('/', '@'));
- }
-
- for (int userId : userIds) {
- File profileDir = Environment.getDataProfilesDeForeignDexDirectory(userId);
-
- for (String markerSuffix : markerSuffixes) {
- File oldForeignUseMark = new File(profileDir, oldMarkerPrefix + markerSuffix);
- File newForeignUseMark = new File(profileDir, newMarkerPrefix + markerSuffix);
- if (oldForeignUseMark.exists()) {
- try {
- Os.rename(oldForeignUseMark.getAbsolutePath(),
- newForeignUseMark.getAbsolutePath());
- } catch (ErrnoException e) {
- Slog.w(TAG, "Failed to rename foreign use marker", e);
- oldForeignUseMark.delete();
- }
- }
- }
- }
- }
-
/**
* Derive the ABI of a non-system package located at {@code scanFile}. This information
* is derived purely on the basis of the contents of {@code scanFile} and
@@ -17541,6 +17409,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
PackageSetting uninstalledPs = null;
+ PackageParser.Package pkg = null;
// for the uninstall-updates case and restricted profiles, remember the per-
// user handle installed state
@@ -17561,7 +17430,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Static shared libs can be declared by any package, so let us not
// allow removing a package if it provides a lib others depend on.
- PackageParser.Package pkg = mPackages.get(packageName);
+ pkg = mPackages.get(packageName);
if (pkg != null && pkg.staticSharedLibName != null) {
SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(pkg.staticSharedLibName,
pkg.staticSharedLibVersion);
@@ -17600,8 +17469,9 @@ public class PackageManagerService extends IPackageManager.Stub {
}
synchronized (mPackages) {
if (res) {
- mInstantAppRegistry.onPackageUninstalledLPw(uninstalledPs.pkg,
- info.removedUsers);
+ if (pkg != null) {
+ mInstantAppRegistry.onPackageUninstalledLPw(pkg, info.removedUsers);
+ }
updateSequenceNumberLP(packageName, info.removedUsers);
}
}
@@ -18448,8 +18318,6 @@ public class PackageManagerService extends IPackageManager.Stub {
try (PackageFreezer freezer = freezePackage(packageName, "clearApplicationProfileData")) {
synchronized (mInstallLock) {
clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
- destroyAppReferenceProfileLeafLIF(pkg, UserHandle.USER_ALL,
- true /* removeBaseMarker */);
}
}
}
@@ -18757,11 +18625,8 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public void getPackageSizeInfo(final String packageName, int userHandle,
final IPackageStatsObserver observer) {
- Slog.w(TAG, "Shame on you for calling a hidden API. Shame!");
- try {
- observer.onGetStatsCompleted(null, false);
- } catch (Throwable ignored) {
- }
+ throw new UnsupportedOperationException(
+ "Shame on you for calling a hidden API. Shame!");
}
private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index fb8429d6b461..36eba8e1b6bb 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -414,13 +414,6 @@ public class UserRestrictionsUtils {
final long id = Binder.clearCallingIdentity();
try {
switch (key) {
- case UserManager.DISALLOW_CONFIG_WIFI:
- if (newValue) {
- android.provider.Settings.Secure.putIntForUser(cr,
- android.provider.Settings.Global
- .WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userId);
- }
- break;
case UserManager.DISALLOW_DATA_ROAMING:
if (newValue) {
// DISALLOW_DATA_ROAMING user restriction is set.
diff --git a/services/core/java/com/android/server/storage/AppCollector.java b/services/core/java/com/android/server/storage/AppCollector.java
index ee9c5bf2775d..25880fb90496 100644
--- a/services/core/java/com/android/server/storage/AppCollector.java
+++ b/services/core/java/com/android/server/storage/AppCollector.java
@@ -17,6 +17,8 @@
package com.android.server.storage;
import android.annotation.NonNull;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
@@ -64,7 +66,8 @@ public class AppCollector {
mBackgroundHandler = new BackgroundHandler(BackgroundThread.get().getLooper(),
volume,
context.getPackageManager(),
- (UserManager) context.getSystemService(Context.USER_SERVICE));
+ (UserManager) context.getSystemService(Context.USER_SERVICE),
+ (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE));
}
/**
@@ -93,39 +96,20 @@ public class AppCollector {
return value;
}
- private class StatsObserver extends IPackageStatsObserver.Stub {
- private AtomicInteger mCount;
- private final ArrayList<PackageStats> mPackageStats;
-
- public StatsObserver(int count) {
- mCount = new AtomicInteger(count);
- mPackageStats = new ArrayList<>(count);
- }
-
- @Override
- public void onGetStatsCompleted(PackageStats packageStats, boolean succeeded)
- throws RemoteException {
- if (succeeded) {
- mPackageStats.add(packageStats);
- }
-
- if (mCount.decrementAndGet() == 0) {
- mStats.complete(mPackageStats);
- }
- }
- }
-
private class BackgroundHandler extends Handler {
static final int MSG_START_LOADING_SIZES = 0;
private final VolumeInfo mVolume;
private final PackageManager mPm;
private final UserManager mUm;
+ private final StorageStatsManager mStorageStatsManager;
- BackgroundHandler(Looper looper, @NonNull VolumeInfo volume, PackageManager pm, UserManager um) {
+ BackgroundHandler(Looper looper, @NonNull VolumeInfo volume,
+ PackageManager pm, UserManager um, StorageStatsManager storageStatsManager) {
super(looper);
mVolume = volume;
mPm = pm;
mUm = um;
+ mStorageStatsManager = storageStatsManager;
}
@Override
@@ -149,14 +133,20 @@ public class AppCollector {
mStats.complete(new ArrayList<>());
}
- // Kick off the async package size query for all apps.
- final StatsObserver observer = new StatsObserver(count);
+ List<PackageStats> stats = new ArrayList<>();
for (UserInfo user : users) {
for (ApplicationInfo app : volumeApps) {
- mPm.getPackageSizeInfoAsUser(app.packageName, user.id,
- observer);
+ PackageStats packageStats = new PackageStats(app.packageName, user.id);
+ StorageStats storageStats = mStorageStatsManager.queryStatsForPackage(
+ app.volumeUuid, app.packageName, user.getUserHandle());
+ packageStats.cacheSize = storageStats.getCacheBytes();
+ packageStats.codeSize = storageStats.getCodeBytes();
+ packageStats.dataSize = storageStats.getDataBytes();
+ stats.add(packageStats);
}
}
+
+ mStats.complete(stats);
}
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 4aa013ae90c1..647adbf960a5 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -785,16 +785,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (canFreezeBounds()) {
freezeBounds();
}
-
- // In the process of tearing down before relaunching, the app will
- // try and clean up it's child surfaces. We need to prevent this from
- // happening, so we sever the children, transfering their ownership
- // from the client it-self to the parent surface (owned by us).
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final WindowState w = mChildren.get(i);
- w.mWinAnimator.detachChildren();
- }
-
mPendingRelaunchCount++;
}
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 837b69e5f7f3..cd0e6ccb2eb4 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -23,12 +23,15 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.animation.Animator;
import android.animation.ValueAnimator;
+import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
import android.os.Debug;
import android.util.ArrayMap;
import android.util.Slog;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.WindowManagerInternal;
@@ -51,6 +54,8 @@ public class BoundsAnimationController {
? "BoundsAnimationController" : TAG_WM;
private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1;
+ private static final int DEFAULT_TRANSITION_DURATION = 425;
+
// Only accessed on UI thread.
private ArrayMap<AnimateBoundsUser, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
@@ -85,12 +90,15 @@ public class BoundsAnimationController {
private final Handler mHandler;
private final AppTransition mAppTransition;
private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier();
+ private final Interpolator mFastOutSlowInInterpolator;
private boolean mFinishAnimationAfterTransition = false;
- BoundsAnimationController(AppTransition transition, Handler handler) {
+ BoundsAnimationController(Context context, AppTransition transition, Handler handler) {
mHandler = handler;
mAppTransition = transition;
mAppTransition.registerListenerLocked(mAppTransitionNotifier);
+ mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
}
private final class BoundsAnimator extends ValueAnimator
@@ -297,8 +305,8 @@ public class BoundsAnimationController {
mRunningAnimations.put(target, animator);
animator.setFloatValues(0f, 1f);
animator.setDuration((animationDuration != -1 ? animationDuration
- : DEFAULT_APP_TRANSITION_DURATION) * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
- animator.setInterpolator(new LinearInterpolator());
+ : DEFAULT_TRANSITION_DURATION) * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
+ animator.setInterpolator(mFastOutSlowInInterpolator);
animator.start();
}
}
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 2a20a70b6ee9..1d50d0d3826f 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -94,13 +94,16 @@ class PinnedStackController {
// The size and position information that describes where the pinned stack will go by default.
private int mDefaultStackGravity;
- private Size mDefaultStackSize;
+ private float mDefaultAspectRatio;
private Point mScreenEdgeInsets;
// The aspect ratio bounds of the PIP.
private float mMinAspectRatio;
private float mMaxAspectRatio;
+ // The minimum edge size of the normal PiP bounds.
+ private int mMinSize;
+
// Temp vars for calculation
private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
private final Rect mTmpInsets = new Rect();
@@ -151,15 +154,15 @@ class PinnedStackController {
*/
void reloadResources() {
final Resources res = mService.mContext.getResources();
- final Size defaultSizeDp = Size.parseSize(res.getString(
- com.android.internal.R.string.config_defaultPictureInPictureSize));
+ mMinSize = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
+ mDefaultAspectRatio = res.getFloat(
+ com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
final Size screenEdgeInsetsDp = Size.parseSize(res.getString(
com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets));
mDefaultStackGravity = res.getInteger(
com.android.internal.R.integer.config_defaultPictureInPictureGravity);
mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics);
- mDefaultStackSize = new Size(dpToPx(defaultSizeDp.getWidth(), mTmpMetrics),
- dpToPx(defaultSizeDp.getHeight(), mTmpMetrics));
mScreenEdgeInsets = new Point(dpToPx(screenEdgeInsetsDp.getWidth(), mTmpMetrics),
dpToPx(screenEdgeInsetsDp.getHeight(), mTmpMetrics));
mMinAspectRatio = res.getFloat(
@@ -199,16 +202,13 @@ class PinnedStackController {
* specified aspect ratio.
*/
Rect transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio) {
- // Save the snap fraction, calculate the aspect ratio based on the current bounds
+ // Save the snap fraction, calculate the aspect ratio based on screen size
final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
getMovementBounds(stackBounds));
- final float radius = PointF.length(stackBounds.width(), stackBounds.height());
- final int height = (int) Math.round(Math.sqrt((radius * radius) /
- (aspectRatio * aspectRatio + 1)));
- final int width = Math.round(height * aspectRatio);
- final int left = (int) (stackBounds.centerX() - width / 2f);
- final int top = (int) (stackBounds.centerY() - height / 2f);
- stackBounds.set(left, top, left + width, top + height);
+ final Size size = getSize(aspectRatio);
+ final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
+ final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
+ stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
if (mIsMinimized) {
applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds));
@@ -217,6 +217,14 @@ class PinnedStackController {
}
/**
+ * @return the size of the PIP based on the given {@param aspectRatio}.
+ */
+ Size getSize(float aspectRatio) {
+ return mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mMinSize,
+ mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+ }
+
+ /**
* @return the default bounds to show the PIP when there is no active PIP.
*/
Rect getDefaultBounds() {
@@ -224,8 +232,9 @@ class PinnedStackController {
getInsetBounds(insetBounds);
final Rect defaultBounds = new Rect();
- Gravity.apply(mDefaultStackGravity, mDefaultStackSize.getWidth(),
- mDefaultStackSize.getHeight(), insetBounds, 0, 0, defaultBounds);
+ final Size size = getSize(mDefaultAspectRatio);
+ Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
+ 0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
return defaultBounds;
}
@@ -344,14 +353,21 @@ class PinnedStackController {
private void notifyMovementBoundsChanged(boolean fromImeAdjustement) {
if (mPinnedStackListener != null) {
try {
- Rect insetBounds = new Rect();
+ final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
- Rect normalBounds = getDefaultBounds();
+ final Rect normalBounds = getDefaultBounds();
if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
transformBoundsToAspectRatio(normalBounds, mAspectRatio);
}
+ final Rect animatingBounds = mTmpRect;
+ final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
+ if (pinnedStack != null) {
+ pinnedStack.getAnimatingBounds(animatingBounds);
+ } else {
+ animatingBounds.set(normalBounds);
+ }
mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
- fromImeAdjustement);
+ animatingBounds, fromImeAdjustement);
} catch (RemoteException e) {
Slog.e(TAG_WM, "Error delivering actions changed event.", e);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4a7b8d4d165e..8b859d12be4f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1039,8 +1039,8 @@ public class WindowManagerService extends IWindowManager.Stub
mAppTransition = new AppTransition(context, this);
mAppTransition.registerListenerLocked(mActivityManagerAppTransitionNotifier);
- mBoundsAnimationController =
- new BoundsAnimationController(mAppTransition, UiThread.getHandler());
+ mBoundsAnimationController = new BoundsAnimationController(context, mAppTransition,
+ UiThread.getHandler());
mActivityManager = ActivityManager.getService();
mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -2212,15 +2212,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (mAccessibilityController != null && win.getDisplayId() == DEFAULT_DISPLAY) {
mAccessibilityController.onWindowTransitionLocked(win, transit);
}
-
- // When we start the exit animation we take the Surface from the client
- // so it will stop perturbing it. We need to likewise takeaway the SurfaceFlinger
- // side child surfaces, so they will remain preserved in their current state
- // (rather than be cleaned up immediately by the app code).
- SurfaceControl.openTransaction();
- winAnimator.detachChildren();
- SurfaceControl.closeTransaction();
-
return focusMayChange;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 48060686a1de..6d572d78b9d7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1532,13 +1532,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return changed;
}
- // Next up we will notify the client that it's visibility has changed.
- // We need to prevent it from destroying child surfaces until
- // the animation has finished.
- if (!visible && isVisibleNow()) {
- mWinAnimator.detachChildren();
- }
-
if (visible != isVisibleNow()) {
if (!runningAppAnimation) {
final AccessibilityController accessibilityController =
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 0b1f9068dac2..98598e1654dc 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -566,20 +566,6 @@ class WindowStateAnimator {
if (!mDestroyPreservedSurfaceUponRedraw) {
return;
}
- if (mSurfaceController != null) {
- if (mPendingDestroySurface != null) {
- // If we are preserving a surface but we aren't relaunching that means
- // we are just doing an in-place switch. In that case any SurfaceFlinger side
- // child layers need to be reparented to the new surface to make this
- // transparent to the app.
- if (mWin.mAppToken == null || mWin.mAppToken.isRelaunching() == false) {
- SurfaceControl.openTransaction();
- mPendingDestroySurface.reparentChildrenInTransaction(mSurfaceController);
- SurfaceControl.closeTransaction();
- }
- }
- }
-
destroyDeferredSurfaceLocked();
mDestroyPreservedSurfaceUponRedraw = false;
}
@@ -1979,11 +1965,4 @@ class WindowStateAnimator {
}
return mForceScaleUntilResize;
}
-
- void detachChildren() {
- Slog.i(TAG, "detaching children: " + this);
- if (mSurfaceController != null) {
- mSurfaceController.detachChildren();
- }
- }
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index f7d3343831bf..f8e74284fafd 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -135,20 +135,6 @@ class WindowSurfaceController {
}
}
- void reparentChildrenInTransaction(WindowSurfaceController other) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "REPARENT from: " + this + " to: " + other);
- if ((mSurfaceControl != null) && (other.mSurfaceControl != null)) {
- mSurfaceControl.reparentChildren(other.getHandle());
- }
- }
-
- void detachChildren() {
- if (SHOW_TRANSACTIONS) Slog.i(TAG, "SEVER CHILDREN");
- if (mSurfaceControl != null) {
- mSurfaceControl.detachChildren();
- }
- }
-
void hideInTransaction(String reason) {
if (SHOW_TRANSACTIONS) logSurface("HIDE ( " + reason + " )", null);
mHiddenForOtherReasons = true;
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 36ae94b88b23..78d8b53bee75 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1175,7 +1175,7 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject
}
sp<IGnssNiCallback> gnssNiCbIface = new GnssNiCallback();
- if (gnssNiCbIface != nullptr) {
+ if (gnssNiIface != nullptr) {
gnssNiIface->setCallback(gnssNiCbIface);
} else {
ALOGE("Unable to initialize GNSS NI interface\n");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index 18f06be063cd..241356162506 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -69,6 +69,10 @@ class SecurityLogMonitor implements Runnable {
*/
private static final long RATE_LIMIT_INTERVAL_MILLISECONDS = TimeUnit.HOURS.toMillis(2);
/**
+ * How often to retry the notification about available logs if it is ignored or missed by DO.
+ */
+ private static final long BROADCAST_RETRY_INTERVAL_MILLISECONDS = TimeUnit.MINUTES.toMillis(30);
+ /**
* Internally how often should the monitor poll the security logs from logd.
*/
private static final long POLLING_INTERVAL_MILLISECONDS = TimeUnit.MINUTES.toMillis(1);
@@ -82,7 +86,7 @@ class SecurityLogMonitor implements Runnable {
/**
* When DO will be allowed to retrieve the log, in milliseconds since boot (as per
- * {@link SystemClock#elapsedRealtime()})
+ * {@link SystemClock#elapsedRealtime()}). After that it will mark the time to retry broadcast.
*/
@GuardedBy("mLock")
private long mNextAllowedRetrievalTimeMillis = -1;
@@ -256,36 +260,35 @@ class SecurityLogMonitor implements Runnable {
}
private void notifyDeviceOwnerIfNeeded() throws InterruptedException {
- boolean shouldNotifyDO = false;
- boolean allowToRetrieveNow = false;
+ boolean allowRetrievalAndNotifyDO = false;
mLock.lockInterruptibly();
try {
if (mPaused) {
return;
}
-
- // STOPSHIP(b/34186771): If the previous notification didn't reach the DO and logs were
- // not retrieved (e.g. the broadcast was sent before the user was unlocked), no more
- // subsequent callbacks will be sent. We should make sure that the DO gets notified
- // before logs are lost.
- int logSize = mPendingLogs.size();
+ final int logSize = mPendingLogs.size();
if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL) {
// Allow DO to retrieve logs if too many pending logs
- allowToRetrieveNow = true;
- if (DEBUG) Slog.d(TAG, "Number of log entries over threshold: " + logSize);
- } else if (logSize > 0) {
- if (SystemClock.elapsedRealtime() >= mNextAllowedRetrievalTimeMillis) {
- // Rate limit reset
- allowToRetrieveNow = true;
- if (DEBUG) Slog.d(TAG, "Timeout reached");
+ if (!mAllowedToRetrieve) {
+ allowRetrievalAndNotifyDO = true;
}
+ if (DEBUG) Slog.d(TAG, "Number of log entries over threshold: " + logSize);
+ }
+ if (logSize > 0 && SystemClock.elapsedRealtime() >= mNextAllowedRetrievalTimeMillis) {
+ // Rate limit reset
+ allowRetrievalAndNotifyDO = true;
+ if (DEBUG) Slog.d(TAG, "Timeout reached");
+ }
+ if (allowRetrievalAndNotifyDO) {
+ mAllowedToRetrieve = true;
+ // Set the timeout to retry the notification if the DO misses it.
+ mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime()
+ + BROADCAST_RETRY_INTERVAL_MILLISECONDS;
}
- shouldNotifyDO = (!mAllowedToRetrieve) && allowToRetrieveNow;
- mAllowedToRetrieve = allowToRetrieveNow;
} finally {
mLock.unlock();
}
- if (shouldNotifyDO) {
+ if (allowRetrievalAndNotifyDO) {
Slog.i(TAG, "notify DO");
mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_SECURITY_LOGS_AVAILABLE,
null);
diff --git a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
index 9ac8295e816d..ad64e4e6e64d 100644
--- a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
+++ b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
@@ -20,6 +20,7 @@ package com.android.server.print;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.Manifest;
+import android.annotation.Nullable;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.companion.ICompanionDeviceDiscoveryService;
@@ -34,16 +35,39 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.NetworkPolicyManager;
import android.os.Binder;
+import android.os.Environment;
import android.os.IBinder;
import android.os.IDeviceIdleController;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.AtomicFile;
+import android.util.ExceptionUtils;
import android.util.Slog;
+import android.util.Xml;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemService;
+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.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Function;
+
//TODO move to own package!
+//TODO un/linkToDeath & onBinderDied - unbind
+//TODO onStop schedule unbind in 5 seconds
+//TODO Prune association on app uninstall
/** @hide */
public class CompanionDeviceManagerService extends SystemService {
@@ -54,7 +78,14 @@ public class CompanionDeviceManagerService extends SystemService {
private static final boolean DEBUG = false;
private static final String LOG_TAG = "CompanionDeviceManagerService";
+ private static final String XML_TAG_ASSOCIATIONS = "associations";
+ private static final String XML_TAG_ASSOCIATION = "association";
+ private static final String XML_ATTR_PACKAGE = "package";
+ private static final String XML_ATTR_DEVICE = "device";
+ private static final String XML_FILE_NAME = "companion_device_manager_associations.xml";
+
private final CompanionDeviceManagerImpl mImpl;
+ private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
public CompanionDeviceManagerService(Context context) {
super(context);
@@ -89,6 +120,24 @@ public class CompanionDeviceManagerService extends SystemService {
Binder.restoreCallingIdentity(callingIdentity);
}
}
+
+
+ @Override
+ public List<String> getAssociations(String callingPackage) {
+ return ArrayUtils.map(
+ readAllAssociations(getUserId(), callingPackage),
+ (a) -> a.deviceAddress);
+ }
+
+ @Override
+ public void disassociate(String deviceMacAddress, String callingPackage) {
+ updateAssociations((associations) -> ArrayUtils.remove(associations,
+ new Association(getUserId(), checkNotNull(deviceMacAddress), callingPackage)));
+ }
+ }
+
+ private int getUserId() {
+ return UserHandle.getUserId(Binder.getCallingUid());
}
private ServiceConnection getServiceConnection(
@@ -125,9 +174,12 @@ public class CompanionDeviceManagerService extends SystemService {
private ICompanionDeviceDiscoveryServiceCallback.Stub getServiceCallback() {
return new ICompanionDeviceDiscoveryServiceCallback.Stub() {
+
@Override
- public void onDeviceSelected(String packageName, int userId) {
+ public void onDeviceSelected(String packageName, int userId, String deviceAddress) {
+ //TODO unbind
grantSpecialAccessPermissionsIfNeeded(packageName, userId);
+ recordAssociation(packageName, deviceAddress);
}
};
}
@@ -136,14 +188,14 @@ public class CompanionDeviceManagerService extends SystemService {
final long identity = Binder.clearCallingIdentity();
final PackageInfo packageInfo;
try {
- packageInfo = getContext().getPackageManager().getPackageInfoAsUser(
- packageName, PackageManager.GET_PERMISSIONS, userId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(LOG_TAG, "Error granting special access permissions to package:"
- + packageName, e);
- return;
- }
- try {
+ try {
+ packageInfo = getContext().getPackageManager().getPackageInfoAsUser(
+ packageName, PackageManager.GET_PERMISSIONS, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(LOG_TAG, "Error granting special access permissions to package:"
+ + packageName, e);
+ return;
+ }
if (ArrayUtils.contains(packageInfo.requestedPermissions,
Manifest.permission.RUN_IN_BACKGROUND)) {
IDeviceIdleController idleController = IDeviceIdleController.Stub.asInterface(
@@ -164,4 +216,134 @@ public class CompanionDeviceManagerService extends SystemService {
Binder.restoreCallingIdentity(identity);
}
}
+
+ private void recordAssociation(String priviledgedPackage, String deviceAddress) {
+ updateAssociations((associations) -> ArrayUtils.add(associations,
+ new Association(getUserId(), deviceAddress, priviledgedPackage)));
+ }
+
+ private void updateAssociations(
+ Function<ArrayList<Association>, ArrayList<Association>> update) {
+ final int userId = getUserId();
+ final AtomicFile file = getStorageFileForUser(userId);
+ synchronized (file) {
+ final ArrayList<Association> old = readAllAssociations(userId);
+ final ArrayList<Association> associations = update.apply(old);
+ if (Objects.equals(old, associations)) return;
+
+ file.write((out) -> {
+ XmlSerializer xml = Xml.newSerializer();
+ try {
+ xml.setOutput(out, StandardCharsets.UTF_8.name());
+ xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ xml.startDocument(null, true);
+ xml.startTag(null, XML_TAG_ASSOCIATIONS);
+
+ for (int i = 0; i < ArrayUtils.size(associations); i++) {
+ Association association = associations.get(i);
+ xml.startTag(null, XML_TAG_ASSOCIATION)
+ .attribute(null, XML_ATTR_PACKAGE, association.companionAppPackage)
+ .attribute(null, XML_ATTR_DEVICE, association.deviceAddress)
+ .endTag(null, XML_TAG_ASSOCIATION);
+ }
+
+ xml.endTag(null, XML_TAG_ASSOCIATIONS);
+ xml.endDocument();
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Error while writing associations file", e);
+ throw ExceptionUtils.propagate(e);
+ }
+
+ });
+ }
+
+
+ //TODO Show dialog before recording notification access
+// final SettingStringHelper setting =
+// new SettingStringHelper(
+// getContext().getContentResolver(),
+// Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+// getUserId());
+// setting.write(ColonDelimitedSet.OfStrings.add(setting.read(), priviledgedPackage));
+ }
+
+ private AtomicFile getStorageFileForUser(int uid) {
+ return mUidToStorage.computeIfAbsent(uid, (u) ->
+ new AtomicFile(new File(
+ //TODO deprecated method - what's the right replacement?
+ Environment.getUserSystemDirectory(u),
+ XML_FILE_NAME)));
+ }
+
+ @Nullable
+ private ArrayList<Association> readAllAssociations(int uid) {
+ return readAllAssociations(uid, null);
+ }
+
+ @Nullable
+ private ArrayList<Association> readAllAssociations(int userId, @Nullable String packageFilter) {
+ final AtomicFile file = getStorageFileForUser(userId);
+
+ if (!file.getBaseFile().exists()) return null;
+
+ ArrayList<Association> result = null;
+ final XmlPullParser parser = Xml.newPullParser();
+ synchronized (file) {
+ try (FileInputStream in = file.openRead()) {
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG
+ && !XML_TAG_ASSOCIATIONS.equals(parser.getName())) continue;
+
+ final String appPackage = parser.getAttributeValue(null, XML_ATTR_PACKAGE);
+ final String deviceAddress = parser.getAttributeValue(null, XML_ATTR_DEVICE);
+
+ if (appPackage == null || deviceAddress == null) continue;
+ if (packageFilter != null && !packageFilter.equals(appPackage)) continue;
+
+ result = ArrayUtils.add(result,
+ new Association(userId, deviceAddress, appPackage));
+ }
+ return result;
+ } catch (XmlPullParserException | IOException e) {
+ Slog.e(LOG_TAG, "Error while reading associations file", e);
+ return null;
+ }
+ }
+ }
+
+ private class Association {
+ public final int uid;
+ public final String deviceAddress;
+ public final String companionAppPackage;
+
+ private Association(int uid, String deviceAddress, String companionAppPackage) {
+ this.uid = uid;
+ this.deviceAddress = checkNotNull(deviceAddress);
+ this.companionAppPackage = checkNotNull(companionAppPackage);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Association that = (Association) o;
+
+ if (uid != that.uid) return false;
+ if (!deviceAddress.equals(that.deviceAddress)) return false;
+ return companionAppPackage.equals(that.companionAppPackage);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = uid;
+ result = 31 * result + deviceAddress.hashCode();
+ result = 31 * result + companionAppPackage.hashCode();
+ return result;
+ }
+ }
+
}
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 25c29ee9566f..450f9b638c2e 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -15,11 +15,15 @@
*/
package com.android.server.notification;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.fail;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,6 +51,7 @@ import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArrayMap;
import android.util.Xml;
import java.io.BufferedInputStream;
@@ -59,12 +64,14 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.ThreadLocalRandom;
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.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
@@ -771,6 +778,17 @@ public class RankingHelperTest {
}
@Test
+ public void testCreateChannel_defaultChannelId() throws Exception {
+ try {
+ mHelper.createNotificationChannel(pkg2, uid2, new NotificationChannel(
+ NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true);
+ fail("Allowed to create default channel");
+ } catch (IllegalArgumentException e) {
+ // pass
+ }
+ }
+
+ @Test
public void testCreateChannel_alreadyExists() throws Exception {
long[] vibration = new long[]{100, 67, 145, 156};
NotificationChannel channel =
@@ -974,9 +992,58 @@ public class RankingHelperTest {
assertEquals(2, actual.size());
for (NotificationChannelGroup group : actual) {
- if (Objects.equals(group.getId(),ncg.getId())) {
+ if (Objects.equals(group.getId(), ncg.getId())) {
assertEquals(1, group.getChannels().size());
}
}
}
+
+ @Test
+ public void testCreateChannel_updateNameResId() throws Exception {
+ NotificationChannel nc = new NotificationChannel("id", 1, IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(pkg, uid, nc, true);
+
+ nc = new NotificationChannel("id", 2, IMPORTANCE_DEFAULT);
+ mHelper.createNotificationChannel(pkg, uid, nc, true);
+
+ assertEquals(2, mHelper.getNotificationChannel(pkg, uid, "id", false).getNameResId());
+ }
+
+ @Test
+ public void testDumpChannelsJson() throws Exception {
+ final ApplicationInfo upgrade = new ApplicationInfo();
+ upgrade.targetSdkVersion = Build.VERSION_CODES.O;
+ try {
+ when(mPm.getApplicationInfoAsUser(
+ anyString(), anyInt(), anyInt())).thenReturn(upgrade);
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ ArrayMap<String, Integer> expectedChannels = new ArrayMap<>();
+ int numPackages = ThreadLocalRandom.current().nextInt(1, 5);
+ for (int i = 0; i < numPackages; i++) {
+ String pkgName = "pkg" + i;
+ int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
+ for (int j = 0; j < numChannels; j++) {
+ mHelper.createNotificationChannel(pkgName, uid,
+ new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true);
+ }
+ expectedChannels.put(pkgName, numChannels);
+ }
+
+ // delete the first channel of the first package
+ String pkg = expectedChannels.keyAt(0);
+ mHelper.deleteNotificationChannel("pkg" + 0, uid, "0");
+ // dump should not include deleted channels
+ int count = expectedChannels.get(pkg);
+ expectedChannels.put(pkg, count - 1);
+
+ JSONArray actual = mHelper.dumpChannelsJson(new NotificationManagerService.DumpFilter());
+ assertEquals(numPackages, actual.length());
+ for (int i = 0; i < numPackages; i++) {
+ JSONObject object = actual.getJSONObject(i);
+ assertTrue(expectedChannels.containsKey(object.get("packageName")));
+ assertEquals(expectedChannels.get(object.get("packageName")).intValue() + 1,
+ object.getInt("channelCount"));
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
index 502bf06dd6d6..64f176a41ba8 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
@@ -23,6 +23,7 @@ import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -206,11 +207,14 @@ public class NetworkScorerAppManagerTest {
}
@Test
- public void testSetActiveScorer_nullPackage() throws Exception {
+ public void testSetActiveScorer_nullPackage_validDefault() throws Exception {
String packageName = "package";
String defaultPackage = "defaultPackage";
setNetworkRecoPackageSetting(packageName);
setDefaultNetworkRecommendationPackage(defaultPackage);
+ final ComponentName recoComponent = new ComponentName(defaultPackage, "class1");
+ mockScoreNetworksGranted(recoComponent.getPackageName());
+ mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, null);
assertTrue(mNetworkScorerAppManager.setActiveScorer(null));
verify(mSettingsFacade).putString(mMockContext,
@@ -218,6 +222,18 @@ public class NetworkScorerAppManagerTest {
}
@Test
+ public void testSetActiveScorer_nullPackage_invalidDefault() throws Exception {
+ String packageName = "package";
+ String defaultPackage = "defaultPackage";
+ setNetworkRecoPackageSetting(packageName);
+ setDefaultNetworkRecommendationPackage(defaultPackage);
+
+ assertFalse(mNetworkScorerAppManager.setActiveScorer(null));
+ verify(mSettingsFacade, never()).putString(any(),
+ eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), any());
+ }
+
+ @Test
public void testSetActiveScorer_validPackage() throws Exception {
String packageName = "package";
String newPackage = "newPackage";
@@ -242,29 +258,82 @@ public class NetworkScorerAppManagerTest {
verify(mSettingsFacade, never()).putString(any(), any(), any());
}
-
@Test
- public void testRevertToDefaultIfNoActive_notActive() throws Exception {
- String defaultPackage = "defaultPackage";
- setDefaultNetworkRecommendationPackage(defaultPackage);
+ public void testUpdateState_recommendationsForcedOff() throws Exception {
+ setRecommendationsEnabledSetting(NetworkScoreManager.RECOMMENDATIONS_ENABLED_FORCED_OFF);
- mNetworkScorerAppManager.revertToDefaultIfNoActive();
+ mNetworkScorerAppManager.updateState();
- verify(mSettingsFacade).putString(mMockContext,
- Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, defaultPackage);
+ verify(mSettingsFacade, never()).getString(any(),
+ eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE));
+ verify(mSettingsFacade, never()).putInt(any(),
+ eq(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED), anyInt());
}
@Test
- public void testRevertToDefaultIfNoActive_active() throws Exception {
+ public void testUpdateState_currentPackageValid() throws Exception {
String packageName = "package";
setNetworkRecoPackageSetting(packageName);
final ComponentName recoComponent = new ComponentName(packageName, "class1");
mockScoreNetworksGranted(recoComponent.getPackageName());
mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, null);
- mNetworkScorerAppManager.revertToDefaultIfNoActive();
+ mNetworkScorerAppManager.updateState();
- verify(mSettingsFacade, never()).putString(any(), any(), any());
+ verify(mSettingsFacade, never()).putString(any(),
+ eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), any());
+ verify(mSettingsFacade).putInt(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+ NetworkScoreManager.RECOMMENDATIONS_ENABLED_ON);
+ }
+
+ @Test
+ public void testUpdateState_currentPackageNotValid_validDefault() throws Exception {
+ String defaultPackage = "defaultPackage";
+ setDefaultNetworkRecommendationPackage(defaultPackage);
+ final ComponentName recoComponent = new ComponentName(defaultPackage, "class1");
+ mockScoreNetworksGranted(recoComponent.getPackageName());
+ mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, null);
+
+ mNetworkScorerAppManager.updateState();
+
+ verify(mSettingsFacade).putString(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, defaultPackage);
+ verify(mSettingsFacade).putInt(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+ NetworkScoreManager.RECOMMENDATIONS_ENABLED_ON);
+ }
+
+ @Test
+ public void testUpdateState_currentPackageNotValid_invalidDefault() throws Exception {
+ String defaultPackage = "defaultPackage";
+ setDefaultNetworkRecommendationPackage(defaultPackage);
+ setNetworkRecoPackageSetting("currentPackage");
+
+ mNetworkScorerAppManager.updateState();
+
+ verify(mSettingsFacade).putString(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, defaultPackage);
+ verify(mSettingsFacade).putInt(mMockContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+ NetworkScoreManager.RECOMMENDATIONS_ENABLED_OFF);
+ }
+
+ @Test
+ public void testUpdateState_currentPackageNotValid_sameAsDefault() throws Exception {
+ String defaultPackage = "defaultPackage";
+ setDefaultNetworkRecommendationPackage(defaultPackage);
+ setNetworkRecoPackageSetting(defaultPackage);
+
+ mNetworkScorerAppManager.updateState();
+
+ verify(mSettingsFacade, never()).putString(any(),
+ eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), any());
+ }
+
+ private void setRecommendationsEnabledSetting(int value) {
+ when(mSettingsFacade.getInt(eq(mMockContext),
+ eq(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED), anyInt())).thenReturn(value);
}
private void setNetworkRecoPackageSetting(String packageName) {
diff --git a/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java b/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
index 29185e92d5ed..8cf7c8a62039 100644
--- a/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/AppCollectorTest.java
@@ -16,12 +16,15 @@
package com.android.server.storage;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.content.pm.UserInfo;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
+import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.VolumeInfo;
import android.test.AndroidTestCase;
@@ -53,6 +56,7 @@ public class AppCollectorTest extends AndroidTestCase {
@Mock private Context mContext;
@Mock private PackageManager mPm;
@Mock private UserManager mUm;
+ @Mock private StorageStatsManager mSsm;
private List<ApplicationInfo> mApps;
private List<UserInfo> mUsers;
@@ -63,6 +67,7 @@ public class AppCollectorTest extends AndroidTestCase {
mApps = new ArrayList<>();
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUm);
+ when(mContext.getSystemService(Context.STORAGE_STATS_SERVICE)).thenReturn(mSsm);
// Set up the app list.
when(mPm.getInstalledApplications(anyInt())).thenReturn(mApps);
@@ -100,39 +105,9 @@ public class AppCollectorTest extends AndroidTestCase {
AppCollector collector = new AppCollector(mContext, volume);
PackageStats stats = new PackageStats("com.test.app");
- // Set up this to handle the asynchronous call to the PackageManager. This returns the
- // package info for the specified package.
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- try {
- ((IPackageStatsObserver.Stub) invocation.getArguments()[2])
- .onGetStatsCompleted(stats, true);
- } catch (Exception e) {
- // We fail instead of just letting the exception fly because throwing
- // out of the callback like this on the background thread causes the test
- // runner to crash, rather than reporting the failure.
- fail();
- }
- return null;
- }
- }).when(mPm).getPackageSizeInfoAsUser(eq("com.test.app"), eq(0), any());
-
-
- // Because getPackageStats is a blocking call, we block execution of the test until the
- // call finishes. In order to finish the call, we need the above answer to execute.
- List<PackageStats> myStats = new ArrayList<>();
- CountDownLatch latch = new CountDownLatch(1);
- new Thread(new Runnable() {
- @Override
- public void run() {
- myStats.addAll(collector.getPackageStats(TIMEOUT));
- latch.countDown();
- }
- }).start();
- latch.await();
-
- assertThat(myStats).containsExactly(stats);
+ when(mSsm.queryStatsForPackage(eq("testuuid"),
+ eq("com.test.app"), eq(UserHandle.of(0)))).thenReturn(new StorageStats());
+ assertThat(collector.getPackageStats(TIMEOUT)).containsExactly(stats);
}
@Test
@@ -151,43 +126,11 @@ public class AppCollectorTest extends AndroidTestCase {
PackageStats otherStats = new PackageStats("com.test.app");
otherStats.userHandle = 1;
- // Set up this to handle the asynchronous call to the PackageManager. This returns the
- // package info for our packages.
- doAnswer(new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) {
- try {
- ((IPackageStatsObserver.Stub) invocation.getArguments()[2])
- .onGetStatsCompleted(stats, true);
-
- // Now callback for the other uid.
- ((IPackageStatsObserver.Stub) invocation.getArguments()[2])
- .onGetStatsCompleted(otherStats, true);
- } catch (Exception e) {
- // We fail instead of just letting the exception fly because throwing
- // out of the callback like this on the background thread causes the test
- // runner to crash, rather than reporting the failure.
- fail();
- }
- return null;
- }
- }).when(mPm).getPackageSizeInfoAsUser(eq("com.test.app"), eq(0), any());
-
-
- // Because getPackageStats is a blocking call, we block execution of the test until the
- // call finishes. In order to finish the call, we need the above answer to execute.
- List<PackageStats> myStats = new ArrayList<>();
- CountDownLatch latch = new CountDownLatch(1);
- new Thread(new Runnable() {
- @Override
- public void run() {
- myStats.addAll(collector.getPackageStats(TIMEOUT));
- latch.countDown();
- }
- }).start();
- latch.await();
-
- assertThat(myStats).containsAllOf(stats, otherStats);
+ when(mSsm.queryStatsForPackage(eq("testuuid"),
+ eq("com.test.app"), eq(UserHandle.of(0)))).thenReturn(new StorageStats());
+ when(mSsm.queryStatsForPackage(eq("testuuid"),
+ eq("com.test.app"), eq(UserHandle.of(1)))).thenReturn(new StorageStats());
+ assertThat(collector.getPackageStats(TIMEOUT)).containsExactly(stats, otherStats);
}
@Test(expected=NullPointerException.class)
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index caf9ec6865e5..7b8ebd44e629 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -602,7 +602,8 @@ class UsbProfileGroupSettingsManager {
new MtpNotificationManager.OnOpenInAppListener() {
@Override
public void onOpenInApp(UsbDevice device) {
- resolveActivity(createDeviceAttachedIntent(device), device);
+ resolveActivity(createDeviceAttachedIntent(device),
+ device, false /* showMtpNotification */);
}
});
}
@@ -958,31 +959,27 @@ class UsbProfileGroupSettingsManager {
// Send broadcast to running activities with registered intent
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- if (MtpNotificationManager.shouldShowNotification(mPackageManager, device)) {
- // Show notification if the device is MTP storage.
- mMtpNotificationManager.showNotification(device);
- } else {
- resolveActivity(intent, device);
- }
+ resolveActivity(intent, device, true /* showMtpNotification */);
}
- private void resolveActivity(Intent intent, UsbDevice device) {
- ArrayList<ResolveInfo> matches;
- String defaultPackage = null;
- UserHandle user = null;
+ private void resolveActivity(Intent intent, UsbDevice device, boolean showMtpNotification) {
+ final ArrayList<ResolveInfo> matches;
+ final ActivityInfo defaultActivity;
synchronized (mLock) {
matches = getDeviceMatchesLocked(device, intent);
- // Launch our default activity directly, if we have one.
- // Otherwise we will start the UsbResolverActivity to allow the user to choose.
- UserPackage userPackage = mDevicePreferenceMap.get(new DeviceFilter(device));
- if (userPackage != null) {
- defaultPackage = userPackage.packageName;
- user = userPackage.user;
- }
+ defaultActivity = getDefaultActivityLocked(
+ matches, mDevicePreferenceMap.get(new DeviceFilter(device)));
+ }
+
+ if (showMtpNotification && MtpNotificationManager.shouldShowNotification(
+ mPackageManager, device) && defaultActivity == null) {
+ // Show notification if the device is MTP storage.
+ mMtpNotificationManager.showNotification(device);
+ return;
}
// Start activity with registered intent
- resolveActivity(intent, matches, defaultPackage, user, device, null);
+ resolveActivity(intent, matches, defaultActivity, device, null);
}
public void deviceAttachedForFixedHandler(UsbDevice device, ComponentName component) {
@@ -1027,21 +1024,15 @@ class UsbProfileGroupSettingsManager {
Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- ArrayList<ResolveInfo> matches;
- String defaultPackage = null;
- UserHandle user = null;
+ final ArrayList<ResolveInfo> matches;
+ final ActivityInfo defaultActivity;
synchronized (mLock) {
matches = getAccessoryMatchesLocked(accessory, intent);
- // Launch our default activity directly, if we have one.
- // Otherwise we will start the UsbResolverActivity to allow the user to choose.
- UserPackage userPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
- if (userPackage != null) {
- defaultPackage = userPackage.packageName;
- user = userPackage.user;
- }
+ defaultActivity = getDefaultActivityLocked(
+ matches, mAccessoryPreferenceMap.get(new AccessoryFilter(accessory)));
}
- resolveActivity(intent, matches, defaultPackage, user, null, accessory);
+ resolveActivity(intent, matches, defaultActivity, null, accessory);
}
/**
@@ -1049,14 +1040,13 @@ class UsbProfileGroupSettingsManager {
*
* @param intent The intent to start the package
* @param matches The available resolutions of the intent
- * @param defaultPackage The default package for the device (if set)
- * @param defaultUser The user of the default package (if package is set)
+ * @param defaultActivity The default activity for the device (if set)
* @param device The device if a device was attached
* @param accessory The accessory if a device was attached
*/
private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches,
- @Nullable String defaultPackage, @Nullable UserHandle defaultUser,
- @Nullable UsbDevice device, @Nullable UsbAccessory accessory) {
+ @Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device,
+ @Nullable UsbAccessory accessory) {
int count = matches.size();
// don't show the resolver activity if there are no choices available
@@ -1083,62 +1073,25 @@ class UsbProfileGroupSettingsManager {
return;
}
- ResolveInfo defaultRI = null;
- if (count == 1 && defaultPackage == null) {
- // Check to see if our single choice is on the system partition.
- // If so, treat it as our default without calling UsbResolverActivity
- ResolveInfo rInfo = matches.get(0);
- if (rInfo.activityInfo != null &&
- rInfo.activityInfo.applicationInfo != null &&
- (rInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- defaultRI = rInfo;
- }
-
- if (mDisablePermissionDialogs) {
- // bypass dialog and launch the only matching activity
- rInfo = matches.get(0);
- if (rInfo.activityInfo != null) {
- defaultPackage = rInfo.activityInfo.packageName;
- defaultUser = UserHandle.getUserHandleForUid(
- rInfo.activityInfo.applicationInfo.uid);
- }
- }
- }
-
- if (defaultRI == null && defaultPackage != null) {
- // look for default activity
- for (int i = 0; i < count; i++) {
- ResolveInfo rInfo = matches.get(i);
- if (rInfo.activityInfo != null &&
- defaultPackage.equals(rInfo.activityInfo.packageName) &&
- defaultUser.getIdentifier() ==
- UserHandle.getUserId(rInfo.activityInfo.applicationInfo.uid)) {
- defaultRI = rInfo;
- break;
- }
- }
- }
-
- if (defaultRI != null) {
+ if (defaultActivity != null) {
UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser(
- UserHandle.getUserId(defaultRI.activityInfo.applicationInfo.uid));
+ UserHandle.getUserId(defaultActivity.applicationInfo.uid));
// grant permission for default activity
if (device != null) {
defaultRIUserSettings.
- grantDevicePermission(device, defaultRI.activityInfo.applicationInfo.uid);
+ grantDevicePermission(device, defaultActivity.applicationInfo.uid);
} else if (accessory != null) {
defaultRIUserSettings.grantAccessoryPermission(accessory,
- defaultRI.activityInfo.applicationInfo.uid);
+ defaultActivity.applicationInfo.uid);
}
// start default activity directly
try {
intent.setComponent(
- new ComponentName(defaultRI.activityInfo.packageName,
- defaultRI.activityInfo.name));
+ new ComponentName(defaultActivity.packageName, defaultActivity.name));
UserHandle user = UserHandle.getUserHandleForUid(
- defaultRI.activityInfo.applicationInfo.uid);
+ defaultActivity.applicationInfo.uid);
mContext.startActivityAsUser(intent, user);
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "startActivity failed", e);
@@ -1179,6 +1132,46 @@ class UsbProfileGroupSettingsManager {
}
}
+ /**
+ * Returns a default activity for matched ResolveInfo.
+ * @param matches Resolved activities matched with connected device/accesary.
+ * @param userPackage Default activity choosed by a user before. Should be null if no activity
+ * is choosed by a user.
+ * @return Default activity
+ */
+ private @Nullable ActivityInfo getDefaultActivityLocked(
+ @NonNull ArrayList<ResolveInfo> matches,
+ @Nullable UserPackage userPackage) {
+ if (userPackage != null) {
+ // look for default activity
+ for (final ResolveInfo info : matches) {
+ if (info.activityInfo != null
+ && userPackage.packageName.equals(info.activityInfo.packageName)
+ && userPackage.user.getIdentifier()
+ == UserHandle.getUserId(info.activityInfo.applicationInfo.uid)) {
+ return info.activityInfo;
+ }
+ }
+ }
+
+ if (matches.size() == 1) {
+ final ActivityInfo activityInfo = matches.get(0).activityInfo;
+ if (activityInfo != null) {
+ // bypass dialog and launch the only matching activity
+ if (mDisablePermissionDialogs) {
+ return activityInfo;
+ }
+ if (activityInfo.applicationInfo != null
+ && (activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
+ != 0) {
+ return activityInfo;
+ }
+ }
+ }
+
+ return null;
+ }
+
private boolean clearCompatibleMatchesLocked(String packageName, DeviceFilter filter) {
boolean changed = false;
for (DeviceFilter test : mDevicePreferenceMap.keySet()) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 5bb479fdeea5..c79090203c0b 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -878,6 +878,16 @@ public final class Call {
* @param id The ID of the request.
*/
public void onRttRequest(Call call, int id) {}
+
+ /**
+ * Invoked when the RTT session failed to initiate for some reason, including rejection
+ * by the remote party.
+ * @param call The call which the RTT initiation failure occurred on.
+ * @param reason One of the status codes defined in
+ * {@link android.telecom.Connection.RttModifyStatus}, with the exception of
+ * {@link android.telecom.Connection.RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
+ */
+ public void onRttInitiationFailure(Call call, int reason) {}
}
/**
@@ -920,13 +930,15 @@ public final class Call {
private OutputStreamWriter mTransmitStream;
private int mRttMode;
private final InCallAdapter mInCallAdapter;
+ private final String mTelecomCallId;
private char[] mReadBuffer = new char[READ_BUFFER_SIZE];
/**
* @hide
*/
- public RttCall(InputStreamReader receiveStream, OutputStreamWriter transmitStream,
- int mode, InCallAdapter inCallAdapter) {
+ public RttCall(String telecomCallId, InputStreamReader receiveStream,
+ OutputStreamWriter transmitStream, int mode, InCallAdapter inCallAdapter) {
+ mTelecomCallId = telecomCallId;
mReceiveStream = receiveStream;
mTransmitStream = transmitStream;
mRttMode = mode;
@@ -949,7 +961,7 @@ public final class Call {
* {@link #RTT_MODE_VCO}, or {@link #RTT_MODE_HCO}.
*/
public void setRttMode(@RttAudioMode int mode) {
- mInCallAdapter.setRttMode(mode);
+ mInCallAdapter.setRttMode(mTelecomCallId, mode);
}
/**
@@ -1014,6 +1026,7 @@ public final class Call {
private int mState;
private List<String> mCannedTextResponses = null;
private String mCallingPackage;
+ private int mTargetSdkVersion;
private String mRemainingPostDialSequence;
private VideoCallImpl mVideoCallImpl;
private RttCall mRttCall;
@@ -1220,7 +1233,7 @@ public final class Call {
* {@link Callback#onRttStatusChanged(Call, boolean, RttCall)} callback.
*/
public void sendRttRequest() {
- mInCallAdapter.sendRttRequest();
+ mInCallAdapter.sendRttRequest(mTelecomCallId);
}
/**
@@ -1231,7 +1244,7 @@ public final class Call {
* @param accept {@code true} if the RTT request should be accepted, {@code false} otherwise.
*/
public void respondToRttRequest(int id, boolean accept) {
- mInCallAdapter.respondToRttRequest(id, accept);
+ mInCallAdapter.respondToRttRequest(mTelecomCallId, id, accept);
}
/**
@@ -1239,7 +1252,7 @@ public final class Call {
* the {@link Callback#onRttStatusChanged(Call, boolean, RttCall)} callback.
*/
public void stopRtt() {
- mInCallAdapter.stopRtt();
+ mInCallAdapter.stopRtt(mTelecomCallId);
}
/**
@@ -1547,22 +1560,25 @@ public final class Call {
}
/** {@hide} */
- Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, String callingPackage) {
+ Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, String callingPackage,
+ int targetSdkVersion) {
mPhone = phone;
mTelecomCallId = telecomCallId;
mInCallAdapter = inCallAdapter;
mState = STATE_NEW;
mCallingPackage = callingPackage;
+ mTargetSdkVersion = targetSdkVersion;
}
/** {@hide} */
Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state,
- String callingPackage) {
+ String callingPackage, int targetSdkVersion) {
mPhone = phone;
mTelecomCallId = telecomCallId;
mInCallAdapter = inCallAdapter;
mState = state;
mCallingPackage = callingPackage;
+ mTargetSdkVersion = targetSdkVersion;
}
/** {@hide} */
@@ -1588,7 +1604,8 @@ public final class Call {
cannedTextResponsesChanged = true;
}
- VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(mCallingPackage);
+ VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(mCallingPackage,
+ mTargetSdkVersion);
boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
!Objects.equals(mVideoCallImpl, newVideoCallImpl);
if (videoCallChanged) {
@@ -1644,7 +1661,7 @@ public final class Call {
new ParcelFileDescriptor.AutoCloseOutputStream(
parcelableRttCall.getTransmitStream()),
StandardCharsets.UTF_8);
- RttCall newRttCall = new Call.RttCall(
+ RttCall newRttCall = new Call.RttCall(mTelecomCallId,
receiveStream, transmitStream, parcelableRttCall.getRttMode(), mInCallAdapter);
if (mRttCall == null) {
isRttChanged = true;
@@ -1724,6 +1741,15 @@ public final class Call {
}
}
+ /** @hide */
+ final void internalOnRttInitiationFailure(int reason) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(() -> callback.onRttInitiationFailure(call, reason));
+ }
+ }
+
private void fireStateChanged(final int newState) {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final Call call = this;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 3e690b997b29..833affa379f3 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -20,9 +20,12 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.IVideoCallback;
import com.android.internal.telecom.IVideoProvider;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.Notification;
+import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
import android.os.Binder;
@@ -39,6 +42,8 @@ import android.view.Surface;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -764,6 +769,10 @@ public abstract class Connection extends Conferenceable {
/** @hide */
public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
public void onAudioRouteChanged(Connection c, int audioRoute) {}
+ public void onRttInitiationSuccess(Connection c) {}
+ public void onRttInitiationFailure(Connection c, int reason) {}
+ public void onRttSessionRemotelyTerminated(Connection c) {}
+ public void onRemoteRttRequest(Connection c) {}
}
/**
@@ -774,12 +783,16 @@ public abstract class Connection extends Conferenceable {
private static final int READ_BUFFER_SIZE = 1000;
private final InputStreamReader mPipeFromInCall;
private final OutputStreamWriter mPipeToInCall;
+ private final ParcelFileDescriptor mFdFromInCall;
+ private final ParcelFileDescriptor mFdToInCall;
private char[] mReadBuffer = new char[READ_BUFFER_SIZE];
/**
* @hide
*/
public RttTextStream(ParcelFileDescriptor toInCall, ParcelFileDescriptor fromInCall) {
+ mFdFromInCall = fromInCall;
+ mFdToInCall = toInCall;
mPipeFromInCall = new InputStreamReader(
new ParcelFileDescriptor.AutoCloseInputStream(fromInCall));
mPipeToInCall = new OutputStreamWriter(
@@ -823,6 +836,47 @@ public abstract class Connection extends Conferenceable {
return null;
}
}
+
+ /** @hide */
+ public ParcelFileDescriptor getFdFromInCall() {
+ return mFdFromInCall;
+ }
+
+ /** @hide */
+ public ParcelFileDescriptor getFdToInCall() {
+ return mFdToInCall;
+ }
+ }
+
+ /**
+ * Provides constants to represent the results of responses to session modify requests sent via
+ * {@link Call#sendRttRequest()}
+ */
+ public static final class RttModifyStatus {
+ /**
+ * Session modify request was successful.
+ */
+ public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
+
+ /**
+ * Session modify request failed.
+ */
+ public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
+
+ /**
+ * Session modify request ignored due to invalid parameters.
+ */
+ public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
+
+ /**
+ * Session modify request timed out.
+ */
+ public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
+
+ /**
+ * Session modify request rejected by remote user.
+ */
+ public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
}
/**
@@ -1005,7 +1059,7 @@ public abstract class Connection extends Conferenceable {
try {
onSetCamera((String) args.arg1);
onSetCamera((String) args.arg1, (String) args.arg2, args.argi1,
- args.argi2);
+ args.argi2, args.argi3);
} finally {
args.recycle();
}
@@ -1065,7 +1119,9 @@ public abstract class Connection extends Conferenceable {
MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
}
- public void setCamera(String cameraId, String callingPackageName) {
+ public void setCamera(String cameraId, String callingPackageName,
+ int targetSdkVersion) {
+
SomeArgs args = SomeArgs.obtain();
args.arg1 = cameraId;
// Propagate the calling package; originally determined in
@@ -1077,6 +1133,9 @@ public abstract class Connection extends Conferenceable {
// check to see if the calling app is able to use the camera.
args.argi1 = Binder.getCallingUid();
args.argi2 = Binder.getCallingPid();
+ // Pass along the target SDK version of the calling InCallService. This is used to
+ // maintain backwards compatibility of the API for older callers.
+ args.argi3 = targetSdkVersion;
mMessageHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
}
@@ -1179,10 +1238,11 @@ public abstract class Connection extends Conferenceable {
* @param callingPackageName The AppOpps package name of the caller.
* @param callingUid The UID of the caller.
* @param callingPid The PID of the caller.
+ * @param targetSdkVersion The target SDK version of the caller.
* @hide
*/
public void onSetCamera(String cameraId, String callingPackageName, int callingUid,
- int callingPid) {}
+ int callingPid, int targetSdkVersion) {}
/**
* Sets the surface to be used for displaying a preview of what the user's camera is
@@ -2426,6 +2486,47 @@ public abstract class Connection extends Conferenceable {
}
/**
+ * Informs listeners that a previously requested RTT session via
+ * {@link ConnectionRequest#isRequestingRtt()} or
+ * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)} has succeeded.
+ * @hide
+ */
+ public final void sendRttInitiationSuccess() {
+ mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this));
+ }
+
+ /**
+ * Informs listeners that a previously requested RTT session via
+ * {@link ConnectionRequest#isRequestingRtt()} or
+ * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)}
+ * has failed.
+ * @param reason One of the reason codes defined in {@link RttModifyStatus}, with the
+ * exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
+ * @hide
+ */
+ public final void sendRttInitiationFailure(int reason) {
+ mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason));
+ }
+
+ /**
+ * Informs listeners that a currently active RTT session has been terminated by the remote
+ * side of the coll.
+ * @hide
+ */
+ public final void sendRttSessionRemotelyTerminated() {
+ mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this));
+ }
+
+ /**
+ * Informs listeners that the remote side of the call has requested an upgrade to include an
+ * RTT session in the call.
+ * @hide
+ */
+ public final void sendRemoteRttRequest() {
+ mListeners.forEach((l) -> l.onRemoteRttRequest(Connection.this));
+ }
+
+ /**
* Notifies this Connection that the {@link #getAudioState()} property has a new value.
*
* @param state The new connection audio state.
@@ -2592,9 +2693,73 @@ public abstract class Connection extends Conferenceable {
* regular {@link ConnectionService}, the Telecom framework will display its own incoming call
* user interface to allow the user to choose whether to answer the new incoming call and
* disconnect other ongoing calls, or to reject the new incoming call.
+ * <p>
+ * You should trigger the display of the incoming call user interface for your application by
+ * showing a {@link Notification} with a full-screen {@link Intent} specified.
+ * For example:
+ * <pre><code>
+ * // Create an intent which triggers your fullscreen incoming call user interface.
+ * Intent intent = new Intent(Intent.ACTION_MAIN, null);
+ * intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
+ * intent.setClass(context, YourIncomingCallActivity.class);
+ * PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, 0);
+ *
+ * // Build the notification as an ongoing high priority item; this ensures it will show as
+ * // a heads up notification which slides down over top of the current content.
+ * final Notification.Builder builder = new Notification.Builder(context);
+ * builder.setOngoing(true);
+ * builder.setPriority(Notification.PRIORITY_HIGH);
+ *
+ * // Set notification content intent to take user to fullscreen UI if user taps on the
+ * // notification body.
+ * builder.setContentIntent(pendingIntent);
+ * // Set full screen intent to trigger display of the fullscreen UI when the notification
+ * // manager deems it appropriate.
+ * builder.setFullScreenIntent(pendingIntent, true);
+ *
+ * // Setup notification content.
+ * builder.setSmallIcon( yourIconResourceId );
+ * builder.setContentTitle("Your notification title");
+ * builder.setContentText("Your notification content.");
+ *
+ * // Use builder.addAction(..) to add buttons to answer or reject the call.
+ *
+ * NotificationManager notificationManager = mContext.getSystemService(
+ * NotificationManager.class);
+ * notificationManager.notify(YOUR_TAG, YOUR_ID, builder.build());
+ * </code></pre>
*/
public void onShowIncomingCallUi() {}
+ /**
+ * Notifies this {@link Connection} that the user has requested an RTT session.
+ * The connection service should call {@link #sendRttInitiationSuccess} or
+ * {@link #sendRttInitiationFailure} to inform Telecom of the success or failure of the
+ * request, respectively.
+ * @param rttTextStream The object that should be used to send text to or receive text from
+ * the in-call app.
+ * @hide
+ */
+ public void onStartRtt(@NonNull RttTextStream rttTextStream) {}
+
+ /**
+ * Notifies this {@link Connection} that it should terminate any existing RTT communication
+ * channel. No response to Telecom is needed for this method.
+ * @hide
+ */
+ public void onStopRtt() {}
+
+ /**
+ * Notifies this connection of a response to a previous remotely-initiated RTT upgrade
+ * request sent via {@link #sendRemoteRttRequest}. Acceptance of the request is
+ * indicated by the supplied {@link RttTextStream} being non-null, and rejection is
+ * indicated by {@code rttTextStream} being {@code null}
+ * @hide
+ * @param rttTextStream The object that should be used to send text to or receive text from
+ * the in-call app.
+ */
+ public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
+
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
if (number == null) {
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 6e100298da35..bf8f8e4e723e 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -26,6 +26,8 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.telecom.Logging.Session;
import com.android.internal.os.SomeArgs;
@@ -119,6 +121,9 @@ public abstract class ConnectionService extends Service {
private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
+ private static final String SESSION_START_RTT = "CS.+RTT";
+ private static final String SESSION_STOP_RTT = "CS.-RTT";
+ private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR";
private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
private static final int MSG_CREATE_CONNECTION = 2;
@@ -144,6 +149,9 @@ public abstract class ConnectionService extends Service {
private static final int MSG_SEND_CALL_EVENT = 23;
private static final int MSG_ON_EXTRAS_CHANGED = 24;
private static final int MSG_CREATE_CONNECTION_FAILED = 25;
+ private static final int MSG_ON_START_RTT = 26;
+ private static final int MSG_ON_STOP_RTT = 27;
+ private static final int MSG_RTT_UPGRADE_RESPONSE = 28;
private static Connection sNullConnection;
@@ -214,6 +222,7 @@ public abstract class ConnectionService extends Service {
@Override
public void createConnectionFailed(
+ PhoneAccountHandle connectionManagerPhoneAccount,
String callId,
ConnectionRequest request,
boolean isIncoming,
@@ -224,6 +233,7 @@ public abstract class ConnectionService extends Service {
args.arg1 = callId;
args.arg2 = request;
args.arg3 = Log.createSubsession();
+ args.arg4 = connectionManagerPhoneAccount;
args.argi1 = isIncoming ? 1 : 0;
mHandler.obtainMessage(MSG_CREATE_CONNECTION_FAILED, args).sendToTarget();
} finally {
@@ -501,6 +511,53 @@ public abstract class ConnectionService extends Service {
Log.endSession();
}
}
+
+ @Override
+ public void startRtt(String callId, ParcelFileDescriptor fromInCall,
+ ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException {
+ Log.startSession(sessionInfo, SESSION_START_RTT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = new Connection.RttTextStream(toInCall, fromInCall);
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_START_RTT, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void stopRtt(String callId, Session.Info sessionInfo) throws RemoteException {
+ Log.startSession(sessionInfo, SESSION_STOP_RTT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_STOP_RTT, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void respondToRttUpgradeRequest(String callId, ParcelFileDescriptor fromInCall,
+ ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException {
+ Log.startSession(sessionInfo, SESSION_RTT_UPGRADE_RESPONSE);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ if (toInCall == null || fromInCall == null) {
+ args.arg2 = null;
+ } else {
+ args.arg2 = new Connection.RttTextStream(toInCall, fromInCall);
+ }
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_RTT_UPGRADE_RESPONSE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
};
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -581,6 +638,8 @@ public abstract class ConnectionService extends Service {
final String id = (String) args.arg1;
final ConnectionRequest request = (ConnectionRequest) args.arg2;
final boolean isIncoming = args.argi1 == 1;
+ final PhoneAccountHandle connectionMgrPhoneAccount =
+ (PhoneAccountHandle) args.arg4;
if (!mAreAccountsInitialized) {
Log.d(this, "Enqueueing pre-init request %s", id);
mPreInitializationConnectionRequests.add(
@@ -589,12 +648,14 @@ public abstract class ConnectionService extends Service {
null /*lock*/) {
@Override
public void loggedRun() {
- createConnectionFailed(id, request, isIncoming);
+ createConnectionFailed(connectionMgrPhoneAccount, id,
+ request, isIncoming);
}
}.prepare());
} else {
Log.i(this, "createConnectionFailed %s", id);
- createConnectionFailed(id, request, isIncoming);
+ createConnectionFailed(connectionMgrPhoneAccount, id, request,
+ isIncoming);
}
} finally {
args.recycle();
@@ -848,6 +909,49 @@ public abstract class ConnectionService extends Service {
}
break;
}
+ case MSG_ON_START_RTT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_START_RTT);
+ String callId = (String) args.arg1;
+ Connection.RttTextStream rttTextStream =
+ (Connection.RttTextStream) args.arg2;
+ startRtt(callId, rttTextStream);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_ON_STOP_RTT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg2,
+ SESSION_HANDLER + SESSION_STOP_RTT);
+ String callId = (String) args.arg1;
+ stopRtt(callId);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_RTT_UPGRADE_RESPONSE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_RTT_UPGRADE_RESPONSE);
+ String callId = (String) args.arg1;
+ Connection.RttTextStream rttTextStream =
+ (Connection.RttTextStream) args.arg2;
+ handleRttUpgradeResponse(callId, rttTextStream);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
default:
break;
}
@@ -1136,6 +1240,38 @@ public abstract class ConnectionService extends Service {
mAdapter.setAudioRoute(id, audioRoute);
}
}
+
+ @Override
+ public void onRttInitiationSuccess(Connection c) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.onRttInitiationSuccess(id);
+ }
+ }
+
+ @Override
+ public void onRttInitiationFailure(Connection c, int reason) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.onRttInitiationFailure(id, reason);
+ }
+ }
+
+ @Override
+ public void onRttSessionRemotelyTerminated(Connection c) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.onRttSessionRemotelyTerminated(id);
+ }
+ }
+
+ @Override
+ public void onRemoteRttRequest(Connection c) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.onRemoteRttRequest(id);
+ }
+ }
};
/** {@inheritDoc} */
@@ -1225,14 +1361,15 @@ public abstract class ConnectionService extends Service {
}
}
- private void createConnectionFailed(final String callId, final ConnectionRequest request,
- boolean isIncoming) {
+ private void createConnectionFailed(final PhoneAccountHandle callManagerAccount,
+ final String callId, final ConnectionRequest request,
+ boolean isIncoming) {
Log.i(this, "createConnectionFailed %s", callId);
if (isIncoming) {
- onCreateIncomingConnectionFailed(request);
+ onCreateIncomingConnectionFailed(callManagerAccount, request);
} else {
- onCreateOutgoingConnectionFailed(request);
+ onCreateOutgoingConnectionFailed(callManagerAccount, request);
}
}
@@ -1430,7 +1567,6 @@ public abstract class ConnectionService extends Service {
if (connection != null) {
connection.onCallEvent(event, extras);
}
-
}
/**
@@ -1454,6 +1590,34 @@ public abstract class ConnectionService extends Service {
}
}
+ private void startRtt(String callId, Connection.RttTextStream rttTextStream) {
+ Log.d(this, "startRtt(%s)", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "startRtt").onStartRtt(rttTextStream);
+ } else if (mConferenceById.containsKey(callId)) {
+ Log.w(this, "startRtt called on a conference.");
+ }
+ }
+
+ private void stopRtt(String callId) {
+ Log.d(this, "stopRtt(%s)", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "stopRtt").onStopRtt();
+ } else if (mConferenceById.containsKey(callId)) {
+ Log.w(this, "stopRtt called on a conference.");
+ }
+ }
+
+ private void handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream) {
+ Log.d(this, "handleRttUpgradeResponse(%s, %s)", callId, rttTextStream == null);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "handleRttUpgradeResponse")
+ .handleRttUpgradeResponse(rttTextStream);
+ } else if (mConferenceById.containsKey(callId)) {
+ Log.w(this, "handleRttUpgradeResponse called on a conference.");
+ }
+ }
+
private void onPostDialContinue(String callId, boolean proceed) {
Log.d(this, "onPostDialContinue(%s)", callId);
findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
@@ -1682,9 +1846,12 @@ public abstract class ConnectionService extends Service {
* <p>
* See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
*
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
* @param request The incoming connection request.
*/
- public void onCreateIncomingConnectionFailed(ConnectionRequest request) {
+ public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
}
/**
@@ -1698,9 +1865,12 @@ public abstract class ConnectionService extends Service {
* <p>
* See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
*
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
* @param request The outgoing connection request.
*/
- public void onCreateOutgoingConnectionFailed(ConnectionRequest request) {
+ public void onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
}
/**
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 9542b73c68fb..63bdf74d383c 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -547,4 +547,66 @@ final class ConnectionServiceAdapter implements DeathRecipient {
}
}
}
+
+ /**
+ * Notifies Telecom that an RTT session was successfully established.
+ *
+ * @param callId The unique ID of the call.
+ */
+ void onRttInitiationSuccess(String callId) {
+ Log.v(this, "onRttInitiationSuccess: %s", callId);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.onRttInitiationSuccess(callId, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Notifies Telecom that a requested RTT session failed to be established.
+ *
+ * @param callId The unique ID of the call.
+ */
+ void onRttInitiationFailure(String callId, int reason) {
+ Log.v(this, "onRttInitiationFailure: %s", callId);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.onRttInitiationFailure(callId, reason, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Notifies Telecom that an established RTT session was terminated by the remote user on
+ * the call.
+ *
+ * @param callId The unique ID of the call.
+ */
+ void onRttSessionRemotelyTerminated(String callId) {
+ Log.v(this, "onRttSessionRemotelyTerminated: %s", callId);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.onRttSessionRemotelyTerminated(callId, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Notifies Telecom that the remote user on the call has requested an upgrade to an RTT
+ * session for this call.
+ *
+ * @param callId The unique ID of the call.
+ */
+ void onRemoteRttRequest(String callId) {
+ Log.v(this, "onRemoteRttRequest: %s", callId);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.onRemoteRttRequest(callId, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index cc437f9c7cd0..80e3c33a443d 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -68,6 +68,10 @@ final class ConnectionServiceAdapterServant {
private static final int MSG_SET_CONNECTION_PROPERTIES = 27;
private static final int MSG_SET_PULLING = 28;
private static final int MSG_SET_AUDIO_ROUTE = 29;
+ private static final int MSG_ON_RTT_INITIATION_SUCCESS = 30;
+ private static final int MSG_ON_RTT_INITIATION_FAILURE = 31;
+ private static final int MSG_ON_RTT_REMOTELY_TERMINATED = 32;
+ private static final int MSG_ON_RTT_UPGRADE_REQUEST = 33;
private final IConnectionServiceAdapter mDelegate;
@@ -300,6 +304,20 @@ final class ConnectionServiceAdapterServant {
}
break;
}
+ case MSG_ON_RTT_INITIATION_SUCCESS:
+ mDelegate.onRttInitiationSuccess((String) msg.obj, null /*Session.Info*/);
+ break;
+ case MSG_ON_RTT_INITIATION_FAILURE:
+ mDelegate.onRttInitiationFailure((String) msg.obj, msg.arg1,
+ null /*Session.Info*/);
+ break;
+ case MSG_ON_RTT_REMOTELY_TERMINATED:
+ mDelegate.onRttSessionRemotelyTerminated((String) msg.obj,
+ null /*Session.Info*/);
+ break;
+ case MSG_ON_RTT_UPGRADE_REQUEST:
+ mDelegate.onRemoteRttRequest((String) msg.obj, null /*Session.Info*/);
+ break;
}
}
};
@@ -537,6 +555,32 @@ final class ConnectionServiceAdapterServant {
args.arg3 = extras;
mHandler.obtainMessage(MSG_ON_CONNECTION_EVENT, args).sendToTarget();
}
+
+ @Override
+ public void onRttInitiationSuccess(String connectionId, Session.Info sessionInfo)
+ throws RemoteException {
+ mHandler.obtainMessage(MSG_ON_RTT_INITIATION_SUCCESS, connectionId).sendToTarget();
+ }
+
+ @Override
+ public void onRttInitiationFailure(String connectionId, int reason,
+ Session.Info sessionInfo)
+ throws RemoteException {
+ mHandler.obtainMessage(MSG_ON_RTT_INITIATION_FAILURE, reason, 0, connectionId)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onRttSessionRemotelyTerminated(String connectionId, Session.Info sessionInfo)
+ throws RemoteException {
+ mHandler.obtainMessage(MSG_ON_RTT_REMOTELY_TERMINATED, connectionId).sendToTarget();
+ }
+
+ @Override
+ public void onRemoteRttRequest(String connectionId, Session.Info sessionInfo)
+ throws RemoteException {
+ mHandler.obtainMessage(MSG_ON_RTT_UPGRADE_REQUEST, connectionId).sendToTarget();
+ }
};
public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index d640b1dd6022..9559a28c0bef 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -379,9 +379,9 @@ public final class InCallAdapter {
/**
* Sends an RTT upgrade request to the remote end of the connection.
*/
- public void sendRttRequest() {
+ public void sendRttRequest(String callId) {
try {
- mAdapter.sendRttRequest();
+ mAdapter.sendRttRequest(callId);
} catch (RemoteException ignored) {
}
}
@@ -392,9 +392,9 @@ public final class InCallAdapter {
* @param id the ID of the request as specified by Telecom
* @param accept Whether the request should be accepted.
*/
- public void respondToRttRequest(int id, boolean accept) {
+ public void respondToRttRequest(String callId, int id, boolean accept) {
try {
- mAdapter.respondToRttRequest(id, accept);
+ mAdapter.respondToRttRequest(callId, id, accept);
} catch (RemoteException ignored) {
}
}
@@ -402,9 +402,9 @@ public final class InCallAdapter {
/**
* Instructs Telecom to shut down the RTT communication channel.
*/
- public void stopRtt() {
+ public void stopRtt(String callId) {
try {
- mAdapter.stopRtt();
+ mAdapter.stopRtt(callId);
} catch (RemoteException ignored) {
}
}
@@ -413,9 +413,9 @@ public final class InCallAdapter {
* Sets the RTT audio mode.
* @param mode the desired RTT audio mode
*/
- public void setRttMode(int mode) {
+ public void setRttMode(String callId, int mode) {
try {
- mAdapter.setRttMode(mode);
+ mAdapter.setRttMode(callId, mode);
} catch (RemoteException ignored) {
}
}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 4bc64c05bfee..e384d4696ab0 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -77,6 +77,7 @@ public abstract class InCallService extends Service {
private static final int MSG_SILENCE_RINGER = 8;
private static final int MSG_ON_CONNECTION_EVENT = 9;
private static final int MSG_ON_RTT_UPGRADE_REQUEST = 10;
+ private static final int MSG_ON_RTT_INITIATION_FAILURE = 11;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -89,7 +90,8 @@ public abstract class InCallService extends Service {
switch (msg.what) {
case MSG_SET_IN_CALL_ADAPTER:
String callingPackage = getApplicationContext().getOpPackageName();
- mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage);
+ mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage,
+ getApplicationContext().getApplicationInfo().targetSdkVersion);
mPhone.addListener(mPhoneListener);
onPhoneCreated(mPhone);
break;
@@ -140,6 +142,12 @@ public abstract class InCallService extends Service {
mPhone.internalOnRttUpgradeRequest(callId, requestId);
break;
}
+ case MSG_ON_RTT_INITIATION_FAILURE: {
+ String callId = (String) msg.obj;
+ int reason = msg.arg1;
+ mPhone.internalOnRttInitiationFailure(callId, reason);
+ break;
+ }
default:
break;
}
@@ -210,6 +218,11 @@ public abstract class InCallService extends Service {
public void onRttUpgradeRequest(String callId, int id) {
mHandler.obtainMessage(MSG_ON_RTT_UPGRADE_REQUEST, id, 0, callId).sendToTarget();
}
+
+ @Override
+ public void onRttInitiationFailure(String callId, int reason) {
+ mHandler.obtainMessage(MSG_ON_RTT_INITIATION_FAILURE, reason, 0, callId).sendToTarget();
+ }
}
private Phone.Listener mPhoneListener = new Phone.Listener() {
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 975aa5a332ca..85a92d1a135a 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -193,13 +193,16 @@ public final class ParcelableCall implements Parcelable {
/**
* Returns an object for remotely communicating through the video call provider's binder.
-
+ *
+ * @param callingPackageName the package name of the calling InCallService.
+ * @param targetSdkVersion the target SDK version of the calling InCallService.
* @return The video call.
*/
- public VideoCallImpl getVideoCallImpl(String callingPackageName) {
+ public VideoCallImpl getVideoCallImpl(String callingPackageName, int targetSdkVersion) {
if (mVideoCall == null && mVideoCallProvider != null) {
try {
- mVideoCall = new VideoCallImpl(mVideoCallProvider, callingPackageName);
+ mVideoCall = new VideoCallImpl(mVideoCallProvider, callingPackageName,
+ targetSdkVersion);
} catch (RemoteException ignored) {
// Ignore RemoteException.
}
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index ebd04c7cd666..066f6c26dd52 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -127,14 +127,20 @@ public final class Phone {
private final String mCallingPackage;
- Phone(InCallAdapter adapter, String callingPackage) {
+ /**
+ * The Target SDK version of the InCallService implementation.
+ */
+ private final int mTargetSdkVersion;
+
+ Phone(InCallAdapter adapter, String callingPackage, int targetSdkVersion) {
mInCallAdapter = adapter;
mCallingPackage = callingPackage;
+ mTargetSdkVersion = targetSdkVersion;
}
final void internalAddCall(ParcelableCall parcelableCall) {
Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
- parcelableCall.getState(), mCallingPackage);
+ parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
mCallByTelecomCallId.put(parcelableCall.getId(), call);
mCalls.add(call);
checkCallTree(parcelableCall);
@@ -208,6 +214,13 @@ public final class Phone {
}
}
+ final void internalOnRttInitiationFailure(String callId, int reason) {
+ Call call = mCallByTelecomCallId.get(callId);
+ if (call != null) {
+ call.internalOnRttInitiationFailure(reason);
+ }
+ }
+
/**
* Called to destroy the phone and cleanup any lingering calls.
*/
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 77e0e5486127..57fc9ced91ae 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -20,6 +20,7 @@ import com.android.internal.telecom.IConnectionService;
import com.android.internal.telecom.IVideoCallback;
import com.android.internal.telecom.IVideoProvider;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.camera2.CameraManager;
@@ -231,6 +232,41 @@ public final class RemoteConnection {
* @param extras Extras associated with the event.
*/
public void onConnectionEvent(RemoteConnection connection, String event, Bundle extras) {}
+
+ /**
+ * Indicates that a RTT session was successfully established on this
+ * {@link RemoteConnection}. See {@link Connection#sendRttInitiationSuccess()}.
+ * @hide
+ * @param connection The {@code RemoteConnection} invoking this method.
+ */
+ public void onRttInitiationSuccess(RemoteConnection connection) {}
+
+ /**
+ * Indicates that a RTT session failed to be established on this
+ * {@link RemoteConnection}. See {@link Connection#sendRttInitiationFailure()}.
+ * @hide
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param reason One of the reason codes defined in {@link Connection.RttModifyStatus},
+ * with the exception of
+ * {@link Connection.RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
+ */
+ public void onRttInitiationFailure(RemoteConnection connection, int reason) {}
+
+ /**
+ * Indicates that an established RTT session was terminated remotely on this
+ * {@link RemoteConnection}. See {@link Connection#sendRttSessionRemotelyTerminated()}
+ * @hide
+ * @param connection The {@code RemoteConnection} invoking this method.
+ */
+ public void onRttSessionRemotelyTerminated(RemoteConnection connection) {}
+
+ /**
+ * Indicates that the remote user on this {@link RemoteConnection} has requested an upgrade
+ * to an RTT session. See {@link Connection#sendRemoteRttRequest()}
+ * @hide
+ * @param connection The {@code RemoteConnection} invoking this method.
+ */
+ public void onRemoteRttRequest(RemoteConnection connection) {}
}
/**
@@ -410,6 +446,8 @@ public final class RemoteConnection {
private final String mCallingPackage;
+ private final int mTargetSdkVersion;
+
/**
* ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
* load factor before resizing, 1 means we only expect a single thread to
@@ -418,9 +456,12 @@ public final class RemoteConnection {
private final Set<Callback> mCallbacks = Collections.newSetFromMap(
new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
- VideoProvider(IVideoProvider videoProviderBinder, String callingPackage) {
+ VideoProvider(IVideoProvider videoProviderBinder, String callingPackage,
+ int targetSdkVersion) {
+
mVideoProviderBinder = videoProviderBinder;
mCallingPackage = callingPackage;
+ mTargetSdkVersion = targetSdkVersion;
try {
mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder());
} catch (RemoteException e) {
@@ -455,7 +496,7 @@ public final class RemoteConnection {
*/
public void setCamera(String cameraId) {
try {
- mVideoProviderBinder.setCamera(cameraId, mCallingPackage);
+ mVideoProviderBinder.setCamera(cameraId, mCallingPackage, mTargetSdkVersion);
} catch (RemoteException e) {
}
}
@@ -631,7 +672,7 @@ public final class RemoteConnection {
* @hide
*/
RemoteConnection(String callId, IConnectionService connectionService,
- ParcelableConnection connection, String callingPackage) {
+ ParcelableConnection connection, String callingPackage, int targetSdkVersion) {
mConnectionId = callId;
mConnectionService = connectionService;
mConnected = true;
@@ -643,7 +684,8 @@ public final class RemoteConnection {
mVideoState = connection.getVideoState();
IVideoProvider videoProvider = connection.getVideoProvider();
if (videoProvider != null) {
- mVideoProvider = new RemoteConnection.VideoProvider(videoProvider, callingPackage);
+ mVideoProvider = new RemoteConnection.VideoProvider(videoProvider, callingPackage,
+ targetSdkVersion);
} else {
mVideoProvider = null;
}
@@ -1046,6 +1088,61 @@ public final class RemoteConnection {
}
/**
+ * Notifies this {@link RemoteConnection} that the user has requested an RTT session.
+ * @param rttTextStream The object that should be used to send text to or receive text from
+ * the in-call app.
+ * @hide
+ */
+ public void startRtt(@NonNull Connection.RttTextStream rttTextStream) {
+ try {
+ if (mConnected) {
+ mConnectionService.startRtt(mConnectionId, rttTextStream.getFdFromInCall(),
+ rttTextStream.getFdToInCall(), null /*Session.Info*/);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Notifies this {@link RemoteConnection} that it should terminate any existing RTT
+ * session. No response to Telecom is needed for this method.
+ * @hide
+ */
+ public void stopRtt() {
+ try {
+ if (mConnected) {
+ mConnectionService.stopRtt(mConnectionId, null /*Session.Info*/);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Notifies this {@link RemoteConnection} of a response to a previous remotely-initiated RTT
+ * upgrade request sent via {@link Connection#sendRemoteRttRequest}.
+ * Acceptance of the request is indicated by the supplied {@link RttTextStream} being non-null,
+ * and rejection is indicated by {@code rttTextStream} being {@code null}
+ * @hide
+ * @param rttTextStream The object that should be used to send text to or receive text from
+ * the in-call app.
+ */
+ public void sendRttUpgradeResponse(@Nullable Connection.RttTextStream rttTextStream) {
+ try {
+ if (mConnected) {
+ if (rttTextStream == null) {
+ mConnectionService.respondToRttUpgradeRequest(mConnectionId,
+ null, null, null /*Session.Info*/);
+ } else {
+ mConnectionService.respondToRttUpgradeRequest(mConnectionId,
+ rttTextStream.getFdFromInCall(), rttTextStream.getFdToInCall(),
+ null /*Session.Info*/);
+ }
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
* Obtain the {@code RemoteConnection}s with which this {@code RemoteConnection} may be
* successfully asked to create a conference with.
*
@@ -1411,6 +1508,47 @@ public final class RemoteConnection {
}
}
+ /** @hide */
+ void onRttInitiationSuccess() {
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(
+ () -> callback.onRttInitiationSuccess(connection));
+ }
+ }
+
+ /** @hide */
+ void onRttInitiationFailure(int reason) {
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(
+ () -> callback.onRttInitiationFailure(connection, reason));
+ }
+ }
+
+ /** @hide */
+ void onRttSessionRemotelyTerminated() {
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(
+ () -> callback.onRttSessionRemotelyTerminated(connection));
+ }
+ }
+
+ /** @hide */
+ void onRemoteRttRequest() {
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(
+ () -> callback.onRemoteRttRequest(connection));
+ }
+ }
+
+ /**
/**
* Create a RemoteConnection represents a failure, and which will be in
* {@link Connection#STATE_DISCONNECTED}. Attempting to use it for anything will almost
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 60a40f5261dd..06cdd1aa7c3c 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -286,10 +286,11 @@ final class RemoteConnectionService {
String callingPackage = mOurConnectionServiceImpl.getApplicationContext()
.getOpPackageName();
+ int targetSdkVersion = mOurConnectionServiceImpl.getApplicationInfo().targetSdkVersion;
RemoteConnection.VideoProvider remoteVideoProvider = null;
if (videoProvider != null) {
remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider,
- callingPackage);
+ callingPackage, targetSdkVersion);
}
findConnectionForAction(callId, "setVideoProvider")
.setVideoProvider(remoteVideoProvider);
@@ -357,8 +358,11 @@ final class RemoteConnectionService {
Session.Info sessionInfo) {
String callingPackage = mOurConnectionServiceImpl.getApplicationContext().
getOpPackageName();
+ int callingTargetSdkVersion = mOurConnectionServiceImpl.getApplicationInfo()
+ .targetSdkVersion;
RemoteConnection remoteConnection = new RemoteConnection(callId,
- mOutgoingConnectionServiceRpc, connection, callingPackage);
+ mOutgoingConnectionServiceRpc, connection, callingPackage,
+ callingTargetSdkVersion);
mConnectionById.put(callId, remoteConnection);
remoteConnection.registerCallback(new RemoteConnection.Callback() {
@Override
@@ -405,6 +409,50 @@ final class RemoteConnectionService {
extras);
}
}
+
+ @Override
+ public void onRttInitiationSuccess(String callId, Session.Info sessionInfo)
+ throws RemoteException {
+ if (hasConnection(callId)) {
+ findConnectionForAction(callId, "onRttInitiationSuccess")
+ .onRttInitiationSuccess();
+ } else {
+ Log.w(this, "onRttInitiationSuccess called on a remote conference");
+ }
+ }
+
+ @Override
+ public void onRttInitiationFailure(String callId, int reason, Session.Info sessionInfo)
+ throws RemoteException {
+ if (hasConnection(callId)) {
+ findConnectionForAction(callId, "onRttInitiationFailure")
+ .onRttInitiationFailure(reason);
+ } else {
+ Log.w(this, "onRttInitiationFailure called on a remote conference");
+ }
+ }
+
+ @Override
+ public void onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo)
+ throws RemoteException {
+ if (hasConnection(callId)) {
+ findConnectionForAction(callId, "onRttSessionRemotelyTerminated")
+ .onRttSessionRemotelyTerminated();
+ } else {
+ Log.w(this, "onRttSessionRemotelyTerminated called on a remote conference");
+ }
+ }
+
+ @Override
+ public void onRemoteRttRequest(String callId, Session.Info sessionInfo)
+ throws RemoteException {
+ if (hasConnection(callId)) {
+ findConnectionForAction(callId, "onRemoteRttRequest")
+ .onRemoteRttRequest();
+ } else {
+ Log.w(this, "onRemoteRttRequest called on a remote conference");
+ }
+ }
};
private final ConnectionServiceAdapterServant mServant =
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index d8ede5c21316..429a434992c5 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -44,6 +44,7 @@ public class VideoCallImpl extends VideoCall {
private int mVideoQuality = VideoProfile.QUALITY_UNKNOWN;
private int mVideoState = VideoProfile.STATE_AUDIO_ONLY;
private final String mCallingPackageName;
+ private final int mTargetSdkVersion;
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
@@ -198,13 +199,15 @@ public class VideoCallImpl extends VideoCall {
private Handler mHandler;
- VideoCallImpl(IVideoProvider videoProvider, String callingPackageName) throws RemoteException {
+ VideoCallImpl(IVideoProvider videoProvider, String callingPackageName, int targetSdkVersion)
+ throws RemoteException {
mVideoProvider = videoProvider;
mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
mBinder = new VideoCallListenerBinder();
mVideoProvider.addVideoCallback(mBinder);
mCallingPackageName = callingPackageName;
+ mTargetSdkVersion = targetSdkVersion;
}
public void destroy() {
@@ -243,7 +246,7 @@ public class VideoCallImpl extends VideoCall {
public void setCamera(String cameraId) {
try {
Log.w(this, "setCamera: cameraId=%s, calling=%s", cameraId, mCallingPackageName);
- mVideoProvider.setCamera(cameraId, mCallingPackageName);
+ mVideoProvider.setCamera(cameraId, mCallingPackageName, mTargetSdkVersion);
} catch (RemoteException e) {
}
}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 20feba78f5c9..c631d085f6e7 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.telecom;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
import android.telecom.ConnectionRequest;
import android.telecom.Logging.Session;
@@ -46,8 +47,8 @@ oneway interface IConnectionService {
boolean isUnknown,
in Session.Info sessionInfo);
- void createConnectionFailed(String callId, in ConnectionRequest request, boolean isIncoming,
- in Session.Info sessionInfo);
+ void createConnectionFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId,
+ in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo);
void abort(String callId, in Session.Info sessionInfo);
@@ -89,4 +90,12 @@ oneway interface IConnectionService {
void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo);
void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo);
+
+ void startRtt(String callId, in ParcelFileDescriptor fromInCall,
+ in ParcelFileDescriptor toInCall, in Session.Info sessionInfo);
+
+ void stopRtt(String callId, in Session.Info sessionInfo);
+
+ void respondToRttUpgradeRequest(String callId, in ParcelFileDescriptor fromInCall,
+ in ParcelFileDescriptor toInCall, in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index b58f8bc7a0aa..ac9da2ef9df4 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -106,4 +106,12 @@ oneway interface IConnectionServiceAdapter {
void onConnectionEvent(String callId, String event, in Bundle extras,
in Session.Info sessionInfo);
+
+ void onRttInitiationSuccess(String callId, in Session.Info sessionInfo);
+
+ void onRttInitiationFailure(String callId, int reason, in Session.Info sessionInfo);
+
+ void onRttSessionRemotelyTerminated(String callId, in Session.Info sessionInfo);
+
+ void onRemoteRttRequest(String callId, in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 47c3e6cfc3d5..73fa29af2ca4 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -70,11 +70,11 @@ oneway interface IInCallAdapter {
void removeExtras(String callId, in List<String> keys);
- void sendRttRequest();
+ void sendRttRequest(String callId);
- void respondToRttRequest(int id, boolean accept);
+ void respondToRttRequest(String callId, int id, boolean accept);
- void stopRtt();
+ void stopRtt(String callId);
- void setRttMode(int mode);
+ void setRttMode(String callId, int mode);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index 1f92e0c42443..e8cf8e975444 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -52,4 +52,6 @@ oneway interface IInCallService {
void onConnectionEvent(String callId, String event, in Bundle extras);
void onRttUpgradeRequest(String callId, int id);
+
+ void onRttInitiationFailure(String callId, int reason);
}
diff --git a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
index a109e90243fe..272b884bd200 100644
--- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
+++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
@@ -30,7 +30,7 @@ oneway interface IVideoProvider {
void removeVideoCallback(IBinder videoCallbackBinder);
- void setCamera(String cameraId, in String mCallingPackageName);
+ void setCamera(String cameraId, in String mCallingPackageName, int targetSdkVersion);
void setPreviewSurface(in Surface surface);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b461fe9e51ed..92197d6dd00c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -21,9 +21,9 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.app.PendingIntent;
import android.content.ContentResolver;
@@ -32,9 +32,6 @@ import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.BatteryStats;
-import android.os.Binder;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
import android.os.Bundle;
import android.os.Handler;
import android.os.PersistableBundle;
@@ -42,11 +39,11 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.service.carrier.CarrierIdentifier;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
-import android.telephony.ClientRequestStats;
-import android.telephony.TelephonyHistogram;
import android.telephony.ims.feature.ImsFeature;
import android.util.Log;
@@ -64,7 +61,6 @@ import com.android.internal.telephony.TelephonyProperties;
import java.io.FileInputStream;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -747,6 +743,8 @@ public class TelephonyManager {
* notification.
*
* <p>
+ * The {@link #EXTRA_PHONE_ACCOUNT_HANDLE} extra indicates which {@link PhoneAccountHandle} the
+ * voicemail is received on.
* The {@link #EXTRA_NOTIFICATION_COUNT} extra indicates the total numbers of unheard
* voicemails.
* The {@link #EXTRA_VOICEMAIL_NUMBER} extra indicates the voicemail number if available.
@@ -757,6 +755,7 @@ public class TelephonyManager {
* {@link android.app.PendingIntent} that will launch the voicemail settings. This extra is only
* available when the voicemail number is not set.
*
+ * @see #EXTRA_PHONE_ACCOUNT_HANDLE
* @see #EXTRA_NOTIFICATION_COUNT
* @see #EXTRA_VOICEMAIL_NUMBER
* @see #EXTRA_CALL_VOICEMAIL_INTENT
@@ -766,6 +765,15 @@ public class TelephonyManager {
"android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
/**
+ * The extra used with an {@link #ACTION_SHOW_VOICEMAIL_NOTIFICATION} {@code Intent} to specify
+ * the {@link PhoneAccountHandle} the notification is for.
+ * <p class="note">
+ * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
+ "android.telephony.extra.PHONE_ACCOUNT_HANDLE";
+
+ /**
* The number of voice messages associated with the notification.
*/
public static final String EXTRA_NOTIFICATION_COUNT =
diff --git a/telephony/java/android/telephony/VisualVoicemailService.java b/telephony/java/android/telephony/VisualVoicemailService.java
index 84833e38232f..e211f76beccf 100644
--- a/telephony/java/android/telephony/VisualVoicemailService.java
+++ b/telephony/java/android/telephony/VisualVoicemailService.java
@@ -41,6 +41,10 @@ import android.util.Log;
* the SMS filtering chain and may intercept the visual voicemail SMS before it reaches this
* service.
* <p>
+ * To extend this class, The service must be declared in the manifest file with
+ * the {@link android.Manifest.permission#BIND_VISUAL_VOICEMAIL_SERVICE} permission and include an
+ * intent filter with the {@link #SERVICE_INTERFACE} action.
+ * <p>
* Below is an example manifest registration for a {@code VisualVoicemailService}.
* <pre>
* {@code
@@ -260,6 +264,9 @@ public abstract class VisualVoicemailService extends Service {
* @param port The destination port for data SMS, or 0 for text SMS.
* @param text The message content. For data sms, it will be encoded as a UTF-8 byte stream.
* @param sentIntent The sent intent passed to the {@link SmsManager}
+ *
+ * @throws SecurityException if the caller is not the current default dialer
+ *
* @see SmsManager#sendDataMessage(String, String, short, byte[], PendingIntent, PendingIntent)
* @see SmsManager#sendTextMessage(String, String, String, PendingIntent, PendingIntent)
*/
diff --git a/telephony/java/android/telephony/ims/ImsServiceBase.java b/telephony/java/android/telephony/ims/ImsServiceBase.java
index 0878db845347..bb36862ef25f 100644
--- a/telephony/java/android/telephony/ims/ImsServiceBase.java
+++ b/telephony/java/android/telephony/ims/ImsServiceBase.java
@@ -19,6 +19,7 @@ package android.telephony.ims;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.os.Binder;
import android.os.IBinder;
/**
@@ -30,8 +31,15 @@ import android.os.IBinder;
@SystemApi
public class ImsServiceBase extends Service {
+ /**
+ * Binder connection that does nothing but keep the connection between this Service and the
+ * framework active. If this service crashes, the framework will be notified.
+ */
+ private IBinder mConnection = new Binder();
+
@Override
public IBinder onBind(Intent intent) {
- return null;
+ return mConnection;
}
+
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e3816b6d1a40..220ea14222fd 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -527,7 +527,7 @@ interface ITelephony {
* Send a visual voicemail SMS. Internal use only.
* Requires caller to be the default dialer and have SEND_SMS permission
*/
- oneway void sendVisualVoicemailSmsForSubscriber(in String callingPackage, in int subId,
+ void sendVisualVoicemailSmsForSubscriber(in String callingPackage, in int subId,
in String number, in int port, in String text, in PendingIntent sentIntent);
// Send the special dialer code. The IPC caller must be the current default dialer.
diff --git a/tests/FeatureSplit/base/Android.mk b/tests/FeatureSplit/base/Android.mk
index 7c0fc04a445a..93f6d7a5f52b 100644
--- a/tests/FeatureSplit/base/Android.mk
+++ b/tests/FeatureSplit/base/Android.mk
@@ -19,6 +19,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := FeatureSplitBase
+LOCAL_EXPORT_PACKAGE_RESOURCES := true
LOCAL_MODULE_TAGS := tests
diff --git a/tests/FeatureSplit/feature1/Android.mk b/tests/FeatureSplit/feature1/Android.mk
index aa222dd6aa66..e6ba5c2d04c9 100644
--- a/tests/FeatureSplit/feature1/Android.mk
+++ b/tests/FeatureSplit/feature1/Android.mk
@@ -17,17 +17,15 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := FeatureSplit1
LOCAL_MODULE_TAGS := tests
-featureOf := FeatureSplitBase
+LOCAL_APK_LIBRARIES := FeatureSplitBase
+LOCAL_RES_LIBRARIES := FeatureSplitBase
-LOCAL_APK_LIBRARIES := $(featureOf)
-featureOfApk := $(call intermediates-dir-for,APPS,$(featureOf))/package.apk
-localRStamp := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON)/src/R.stamp
-$(localRStamp): $(featureOfApk)
-
-LOCAL_AAPT_FLAGS := --feature-of $(featureOfApk) --custom-package com.android.test.split.feature.one
+LOCAL_AAPT_FLAGS += --package-id 0x80
+LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.one
include $(BUILD_PACKAGE)
diff --git a/tests/FeatureSplit/feature2/Android.mk b/tests/FeatureSplit/feature2/Android.mk
index 1a0322bf686e..c8e860942fa3 100644
--- a/tests/FeatureSplit/feature2/Android.mk
+++ b/tests/FeatureSplit/feature2/Android.mk
@@ -17,22 +17,15 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := FeatureSplit2
LOCAL_MODULE_TAGS := tests
-featureOf := FeatureSplitBase
-featureAfter := FeatureSplit1
+LOCAL_APK_LIBRARIES := FeatureSplitBase
+LOCAL_RES_LIBRARIES := FeatureSplitBase
-LOCAL_APK_LIBRARIES := $(featureOf)
-
-featureOfApk := $(call intermediates-dir-for,APPS,$(featureOf))/package.apk
-featureAfterApk := $(call intermediates-dir-for,APPS,$(featureAfter))/package.apk
-localRStamp := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),,COMMON)/src/R.stamp
-$(localRStamp): $(featureOfApk) $(featureAfterApk)
-
-LOCAL_AAPT_FLAGS := --feature-of $(featureOfApk)
-LOCAL_AAPT_FLAGS += --feature-after $(featureAfterApk)
+LOCAL_AAPT_FLAGS += --package-id 0x81
LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.two
include $(BUILD_PACKAGE)
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
index db6421e1810e..0defe924e6b3 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
@@ -42,7 +42,7 @@ public class ListActivity extends Activity {
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
index 535f86514fa8..ffb86893be73 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
@@ -42,7 +42,7 @@ public class TransparentListActivity extends Activity {
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
index 0ddd7fd692a0..7168478c083c 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
@@ -126,7 +126,7 @@ public class ViewLayersActivity extends Activity {
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java
index e795f02a224d..a037d70ef845 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java
@@ -76,7 +76,7 @@ public class ViewLayersActivity2 extends Activity {
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java
index c8ae75bfb701..e65dd6331408 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java
@@ -73,7 +73,7 @@ public class ViewLayersActivity3 extends Activity {
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java
index 6072c6e75f08..17f78af50edb 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java
@@ -80,7 +80,7 @@ public class ViewLayersActivity4 extends Activity {
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java
index cbbb7ef79961..2dd7b6a99ce1 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java
@@ -145,7 +145,7 @@ public class ViewLayersActivity5 extends Activity {
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java
index 7a4fddf7312b..292bbd22d0fc 100644
--- a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java
+++ b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java
@@ -65,7 +65,7 @@ public class AutoCompleteTextViewActivityLandscape extends Activity
"Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java
index 5bfe456a30f7..570cb6bf8a06 100644
--- a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java
+++ b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java
@@ -60,7 +60,7 @@ public class AutoCompleteTextViewActivityPortrait extends Activity
"Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/TtsTests/Android.mk b/tests/TtsTests/Android.mk
index ed63e1264d8b..3c3cd77813f7 100644
--- a/tests/TtsTests/Android.mk
+++ b/tests/TtsTests/Android.mk
@@ -20,7 +20,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_STATIC_JAVA_LIBRARIES := littlemock junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target legacy-android-test
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := TtsTests
diff --git a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
index faf6827bbc44..918873bf0bdd 100644
--- a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
+++ b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
@@ -22,9 +22,12 @@ import android.speech.tts.TextToSpeech;
import android.test.InstrumentationTestCase;
import com.android.speech.tts.MockableTextToSpeechService.IDelegate;
-import com.google.testing.littlemock.ArgumentCaptor;
-import com.google.testing.littlemock.Behaviour;
-import com.google.testing.littlemock.LittleMock;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.internal.stubbing.StubberImpl;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.mockito.stubbing.Stubber;
import junit.framework.Assert;
import java.util.Locale;
@@ -40,16 +43,16 @@ public class TextToSpeechTests extends InstrumentationTestCase {
@Override
public void setUp() throws Exception {
- IDelegate passThrough = LittleMock.mock(IDelegate.class);
+ IDelegate passThrough = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(passThrough);
// For the default voice selection
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough)
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough)
.onIsLanguageAvailable(
- LittleMock.anyString(), LittleMock.anyString(), LittleMock.anyString());
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough)
+ Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough)
.onLoadLanguage(
- LittleMock.anyString(), LittleMock.anyString(), LittleMock.anyString());
+ Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
blockingInitAndVerify(MOCK_ENGINE, TextToSpeech.SUCCESS);
assertEquals(MOCK_ENGINE, mTts.getCurrentEngine());
@@ -71,42 +74,42 @@ public class TextToSpeechTests extends InstrumentationTestCase {
}
public void testSetLanguage_delegation() {
- IDelegate delegate = LittleMock.mock(IDelegate.class);
+ IDelegate delegate = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(delegate);
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onIsLanguageAvailable(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onIsLanguageAvailable(
"eng", "USA", "variant");
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onLoadLanguage(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onLoadLanguage(
"eng", "USA", "variant");
// Test 1 :Tests that calls to onLoadLanguage( ) are delegated through to the
// service without any caching or intermediate steps.
assertEquals(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE, mTts.setLanguage(new Locale("eng", "USA", "variant")));
- LittleMock.verify(delegate, LittleMock.anyTimes()).onIsLanguageAvailable(
+ Mockito.verify(delegate, Mockito.atLeast(0)).onIsLanguageAvailable(
"eng", "USA", "variant");
- LittleMock.verify(delegate, LittleMock.anyTimes()).onLoadLanguage(
+ Mockito.verify(delegate, Mockito.atLeast(0)).onLoadLanguage(
"eng", "USA", "variant");
}
public void testSetLanguage_availableLanguage() throws Exception {
- IDelegate delegate = LittleMock.mock(IDelegate.class);
+ IDelegate delegate = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(delegate);
// ---------------------------------------------------------
// Test 2 : Tests that when the language is successfully set
// like above (returns LANG_COUNTRY_AVAILABLE). That the
// request language changes from that point on.
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable(
"eng", "USA", "variant");
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable(
"eng", "USA", "");
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onLoadLanguage(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onLoadLanguage(
"eng", "USA", "");
mTts.setLanguage(new Locale("eng", "USA", "variant"));
blockingCallSpeak("foo bar", delegate);
- ArgumentCaptor<SynthesisRequest> req = LittleMock.createCaptor();
- LittleMock.verify(delegate, LittleMock.times(1)).onSynthesizeText(req.capture(),
- LittleMock.<SynthesisCallback>anyObject());
+ ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class);
+ Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(),
+ Mockito.<SynthesisCallback>anyObject());
assertEquals("eng", req.getValue().getLanguage());
assertEquals("USA", req.getValue().getCountry());
@@ -115,21 +118,21 @@ public class TextToSpeechTests extends InstrumentationTestCase {
}
public void testSetLanguage_unavailableLanguage() throws Exception {
- IDelegate delegate = LittleMock.mock(IDelegate.class);
+ IDelegate delegate = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(delegate);
// ---------------------------------------------------------
// TEST 3 : Tests that the language that is set does not change when the
// engine reports it could not load the specified language.
- LittleMock.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when(
+ Mockito.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when(
delegate).onIsLanguageAvailable("fra", "FRA", "");
- LittleMock.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when(
+ Mockito.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when(
delegate).onLoadLanguage("fra", "FRA", "");
mTts.setLanguage(Locale.FRANCE);
blockingCallSpeak("le fou barre", delegate);
- ArgumentCaptor<SynthesisRequest> req2 = LittleMock.createCaptor();
- LittleMock.verify(delegate, LittleMock.times(1)).onSynthesizeText(req2.capture(),
- LittleMock.<SynthesisCallback>anyObject());
+ ArgumentCaptor<SynthesisRequest> req2 = ArgumentCaptor.forClass(SynthesisRequest.class);
+ Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req2.capture(),
+ Mockito.<SynthesisCallback>anyObject());
// The params are basically unchanged.
assertEquals("eng", req2.getValue().getLanguage());
@@ -139,41 +142,41 @@ public class TextToSpeechTests extends InstrumentationTestCase {
}
public void testIsLanguageAvailable() {
- IDelegate delegate = LittleMock.mock(IDelegate.class);
+ IDelegate delegate = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(delegate);
// Test1: Simple end to end test.
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(
delegate).onIsLanguageAvailable("eng", "USA", "");
assertEquals(TextToSpeech.LANG_COUNTRY_AVAILABLE, mTts.isLanguageAvailable(Locale.US));
- LittleMock.verify(delegate, LittleMock.times(1)).onIsLanguageAvailable(
+ Mockito.verify(delegate, Mockito.times(1)).onIsLanguageAvailable(
"eng", "USA", "");
}
public void testDefaultLanguage_setsVoiceName() throws Exception {
- IDelegate delegate = LittleMock.mock(IDelegate.class);
+ IDelegate delegate = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(delegate);
Locale defaultLocale = Locale.getDefault();
// ---------------------------------------------------------
// Test that default language also sets the default voice
// name
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).
when(delegate).onIsLanguageAvailable(
defaultLocale.getISO3Language(),
defaultLocale.getISO3Country().toUpperCase(),
defaultLocale.getVariant());
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).
when(delegate).onLoadLanguage(
defaultLocale.getISO3Language(),
defaultLocale.getISO3Country(),
defaultLocale.getVariant());
blockingCallSpeak("foo bar", delegate);
- ArgumentCaptor<SynthesisRequest> req = LittleMock.createCaptor();
- LittleMock.verify(delegate, LittleMock.times(1)).onSynthesizeText(req.capture(),
- LittleMock.<SynthesisCallback>anyObject());
+ ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class);
+ Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(),
+ Mockito.<SynthesisCallback>anyObject());
assertEquals(defaultLocale.getISO3Language(), req.getValue().getLanguage());
assertEquals(defaultLocale.getISO3Country(), req.getValue().getCountry());
@@ -185,8 +188,8 @@ public class TextToSpeechTests extends InstrumentationTestCase {
private void blockingCallSpeak(String speech, IDelegate mock) throws
InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- doCountDown(latch).when(mock).onSynthesizeText(LittleMock.<SynthesisRequest>anyObject(),
- LittleMock.<SynthesisCallback>anyObject());
+ doCountDown(latch).when(mock).onSynthesizeText(Mockito.<SynthesisRequest>anyObject(),
+ Mockito.<SynthesisCallback>anyObject());
mTts.speak(speech, TextToSpeech.QUEUE_ADD, null);
awaitCountDown(latch, 5, TimeUnit.SECONDS);
@@ -194,7 +197,7 @@ public class TextToSpeechTests extends InstrumentationTestCase {
private void blockingInitAndVerify(final String engine, int errorCode) throws
InterruptedException {
- TextToSpeech.OnInitListener listener = LittleMock.mock(
+ TextToSpeech.OnInitListener listener = Mockito.mock(
TextToSpeech.OnInitListener.class);
final CountDownLatch latch = new CountDownLatch(1);
@@ -206,18 +209,18 @@ public class TextToSpeechTests extends InstrumentationTestCase {
awaitCountDown(latch, 5, TimeUnit.SECONDS);
}
- public interface CountDownBehaviour extends Behaviour {
+ public static abstract class CountDownBehaviour extends StubberImpl {
/** Used to mock methods that return a result. */
- Behaviour andReturn(Object result);
+ public abstract Stubber andReturn(Object result);
}
public static CountDownBehaviour doCountDown(final CountDownLatch latch) {
return new CountDownBehaviour() {
@Override
public <T> T when(T mock) {
- return LittleMock.doAnswer(new Callable<Void>() {
+ return Mockito.doAnswer(new Answer<Void>() {
@Override
- public Void call() throws Exception {
+ public Void answer(InvocationOnMock invocation) throws Exception {
latch.countDown();
return null;
}
@@ -225,13 +228,13 @@ public class TextToSpeechTests extends InstrumentationTestCase {
}
@Override
- public Behaviour andReturn(final Object result) {
- return new Behaviour() {
+ public Stubber andReturn(final Object result) {
+ return new StubberImpl() {
@Override
public <T> T when(T mock) {
- return LittleMock.doAnswer(new Callable<Object>() {
+ return Mockito.doAnswer(new Answer<Object>() {
@Override
- public Object call() throws Exception {
+ public Object answer(InvocationOnMock invocation) throws Exception {
latch.countDown();
return result;
}
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 8aa27a9559b8..79f6e4d2f000 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -40,7 +40,19 @@ LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \
libnetdaidl \
libui \
libunwind \
- libutils
+ libutils \
+ libcrypto \
+ libhidl-gen-utils \
+ libhidlbase \
+ libhidltransport \
+ libpackagelistparser \
+ libpcre2 \
+ libselinux \
+ libtinyxml2 \
+ libvintf \
+ libhwbinder \
+ android.hidl.base@1.0 \
+ android.hidl.token@1.0
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c05045ebd18d..04443a53527c 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1176,15 +1176,11 @@ public class ConnectivityServiceTest extends AndroidTestCase {
void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
-
- final boolean HAS_DATASYNC_ON_AVAILABLE = false;
- if (HAS_DATASYNC_ON_AVAILABLE) {
- if (expectSuspended) {
- expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
- }
- expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
- expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
+ if (expectSuspended) {
+ expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
}
+ expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
+ expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
}
void expectAvailableCallbacks(MockNetworkAgent agent) {
@@ -1196,7 +1192,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
}
void expectAvailableAndValidatedCallbacks(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, true, TIMEOUT_MS);
+ expectAvailableCallbacks(agent, false, TIMEOUT_MS);
expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
}
@@ -1937,20 +1933,6 @@ public class ConnectivityServiceTest extends AndroidTestCase {
dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent);
dfltNetworkCallback.assertNoCallback();
- // Request a NetworkCapabilities update; only the requesting callback is notified.
- // TODO: Delete this together with Connectivity{Manager,Service} code.
- mCm.requestNetworkCapabilities(dfltNetworkCallback);
- dfltNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent);
- cellNetworkCallback.assertNoCallback();
- dfltNetworkCallback.assertNoCallback();
-
- // Request a LinkProperties update; only the requesting callback is notified.
- // TODO: Delete this together with Connectivity{Manager,Service} code.
- mCm.requestLinkProperties(dfltNetworkCallback);
- dfltNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
- cellNetworkCallback.assertNoCallback();
- dfltNetworkCallback.assertNoCallback();
-
mCm.unregisterNetworkCallback(dfltNetworkCallback);
mCm.unregisterNetworkCallback(cellNetworkCallback);
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index ff306ce5be6b..60f0d56e09bf 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -4764,6 +4764,7 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle,
const sp<XMLNode>& root) {
const String16 vector16("vector");
const String16 animatedVector16("animated-vector");
+ const String16 pathInterpolator16("pathInterpolator");
const int minSdk = getMinSdkVersion(bundle);
if (minSdk >= SDK_LOLLIPOP_MR1) {
@@ -4789,7 +4790,8 @@ status_t ResourceTable::modifyForCompat(const Bundle* bundle,
nodesToVisit.pop();
if (bundle->getNoVersionVectors() && (node->getElementName() == vector16 ||
- node->getElementName() == animatedVector16)) {
+ node->getElementName() == animatedVector16 ||
+ node->getElementName() == pathInterpolator16)) {
// We were told not to version vector tags, so skip the children here.
continue;
}
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 38d9ef84f44b..456f68635705 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -25,7 +25,7 @@ namespace aapt {
static const char* sMajorVersion = "2";
// Update minor version whenever a feature or flag is added.
-static const char* sMinorVersion = "9";
+static const char* sMinorVersion = "10";
int PrintVersion() {
std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "."
diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/diff/Diff.cpp
index c8774687944c..dacf8d9f1e96 100644
--- a/tools/aapt2/diff/Diff.cpp
+++ b/tools/aapt2/diff/Diff.cpp
@@ -331,7 +331,7 @@ class ZeroingReferenceVisitor : public ValueVisitor {
void Visit(Reference* ref) override {
if (ref->name && ref->id) {
- if (ref->id.value().package_id() == 0x7f) {
+ if (ref->id.value().package_id() == kAppPackageId) {
ref->id = {};
}
}
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index ec3d75e354c7..494d9d25b008 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -30,23 +30,20 @@ namespace aapt {
class XmlFlattenerTest : public ::testing::Test {
public:
void SetUp() override {
- context_ =
- test::ContextBuilder()
- .SetCompilationPackage("com.app.test")
- .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
- .AddSymbolSource(
- test::StaticSymbolSourceBuilder()
- .AddSymbol("android:attr/id", ResourceId(0x010100d0),
- test::AttributeBuilder().Build())
- .AddSymbol("com.app.test:id/id", ResourceId(0x7f020000))
- .AddSymbol("android:attr/paddingStart",
- ResourceId(0x010103b3),
- test::AttributeBuilder().Build())
- .AddSymbol("android:attr/colorAccent",
- ResourceId(0x01010435),
- test::AttributeBuilder().Build())
- .Build())
- .Build();
+ context_ = test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .AddSymbolSource(
+ test::StaticSymbolSourceBuilder()
+ .AddSymbol("android:attr/id", ResourceId(0x010100d0),
+ test::AttributeBuilder().Build())
+ .AddSymbol("com.app.test:id/id", ResourceId(0x7f020000))
+ .AddPublicSymbol("android:attr/paddingStart", ResourceId(0x010103b3),
+ test::AttributeBuilder().Build())
+ .AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
+ test::AttributeBuilder().Build())
+ .Build())
+ .Build();
}
::testing::AssertionResult Flatten(xml::XmlResource* doc,
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index dd8e14b40fa8..c8f02171bfd3 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -23,6 +23,7 @@
#include "android-base/errors.h"
#include "android-base/file.h"
+#include "android-base/stringprintf.h"
#include "androidfw/StringPiece.h"
#include "google/protobuf/io/coded_stream.h"
@@ -57,6 +58,7 @@
#include "xml/XmlDom.h"
using android::StringPiece;
+using android::base::StringPrintf;
using ::google::protobuf::io::CopyingOutputStreamAdaptor;
namespace aapt {
@@ -705,11 +707,17 @@ class LinkCommand {
}
// If we are using --no-static-lib-packages, we need to rename the
- // package of this
- // table to our compilation package.
+ // package of this table to our compilation package.
if (options_.no_static_lib_packages) {
- if (ResourceTablePackage* pkg = include_static->FindPackageById(0x7f)) {
+ // Since package names can differ, and multiple packages can exist in a ResourceTable,
+ // we place the requirement that all static libraries are built with the package
+ // ID 0x7f. So if one is not found, this is an error.
+ if (ResourceTablePackage* pkg = include_static->FindPackageById(kAppPackageId)) {
pkg->name = context_->GetCompilationPackage();
+ } else {
+ context_->GetDiagnostics()->Error(DiagMessage(path)
+ << "no package with ID 0x7f found in static library");
+ return false;
}
}
@@ -733,7 +741,7 @@ class LinkCommand {
// Capture the shared libraries so that the final resource table can be properly flattened
// with support for shared libraries.
for (auto& entry : asset_source->GetAssignedPackageIds()) {
- if (entry.first > 0x01 && entry.first < 0x7f) {
+ if (entry.first > kFrameworkPackageId && entry.first < kAppPackageId) {
final_table_.included_packages_[entry.first] = entry.second;
}
}
@@ -860,10 +868,9 @@ class LinkCommand {
for (const auto& package : final_table_.packages) {
for (const auto& type : package->types) {
if (type->id) {
- context_->GetDiagnostics()->Error(
- DiagMessage() << "type " << type->type << " has ID " << std::hex
- << (int)type->id.value() << std::dec
- << " assigned");
+ context_->GetDiagnostics()->Error(DiagMessage() << "type " << type->type << " has ID "
+ << StringPrintf("%02x", type->id.value())
+ << " assigned");
return false;
}
@@ -871,9 +878,8 @@ class LinkCommand {
if (entry->id) {
ResourceNameRef res_name(package->name, type->type, entry->name);
context_->GetDiagnostics()->Error(
- DiagMessage() << "entry " << res_name << " has ID " << std::hex
- << (int)entry->id.value() << std::dec
- << " assigned");
+ DiagMessage() << "entry " << res_name << " has ID "
+ << StringPrintf("%02x", entry->id.value()) << " assigned");
return false;
}
}
@@ -1103,7 +1109,7 @@ class LinkCommand {
return false;
}
- ResourceTablePackage* pkg = table->FindPackageById(0x7f);
+ ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId);
if (!pkg) {
context_->GetDiagnostics()->Error(DiagMessage(input) << "static library has no package");
return false;
@@ -1490,12 +1496,17 @@ class LinkCommand {
context_->SetNameManglerPolicy(
NameManglerPolicy{context_->GetCompilationPackage()});
- if (options_.package_type == PackageType::kSharedLib) {
- context_->SetPackageId(0x00);
- } else if (context_->GetCompilationPackage() == "android") {
+
+ // Override the package ID when it is "android".
+ if (context_->GetCompilationPackage() == "android") {
context_->SetPackageId(0x01);
- } else {
- context_->SetPackageId(0x7f);
+
+ // Verify we're building a regular app.
+ if (options_.package_type != PackageType::kApp) {
+ context_->GetDiagnostics()->Error(
+ DiagMessage() << "package 'android' can only be built as a regular app");
+ return 1;
+ }
}
if (!LoadSymbolsFromIncludePaths()) {
@@ -1509,10 +1520,9 @@ class LinkCommand {
if (context_->IsVerbose()) {
context_->GetDiagnostics()->Note(DiagMessage()
- << "linking package '"
- << context_->GetCompilationPackage()
- << "' with package ID " << std::hex
- << (int)context_->GetPackageId());
+ << StringPrintf("linking package '%s' using package ID %02x",
+ context_->GetCompilationPackage().data(),
+ context_->GetPackageId()));
}
for (const std::string& input : input_files) {
@@ -1889,6 +1899,7 @@ int Link(const std::vector<StringPiece>& args) {
LinkOptions options;
std::vector<std::string> overlay_arg_list;
std::vector<std::string> extra_java_packages;
+ Maybe<std::string> package_id;
Maybe<std::string> configs;
Maybe<std::string> preferred_density;
Maybe<std::string> product_list;
@@ -1909,6 +1920,10 @@ int Link(const std::vector<StringPiece>& args) {
"Compilation unit to link, using `overlay` semantics.\n"
"The last conflicting resource given takes precedence.",
&overlay_arg_list)
+ .OptionalFlag("--package-id",
+ "Specify the package ID to use for this app. Must be greater or equal to\n"
+ "0x7f and can't be used with --static-lib or --shared-lib.",
+ &package_id)
.OptionalFlag("--java", "Directory in which to generate R.java",
&options.generate_java_class_path)
.OptionalFlag("--proguard", "Output file for generated Proguard rules",
@@ -2062,6 +2077,47 @@ int Link(const std::vector<StringPiece>& args) {
context.SetVerbose(verbose);
}
+ if (shared_lib && static_lib) {
+ context.GetDiagnostics()->Error(DiagMessage()
+ << "only one of --shared-lib and --static-lib can be defined");
+ return 1;
+ }
+
+ if (shared_lib) {
+ options.package_type = PackageType::kSharedLib;
+ context.SetPackageId(0x00);
+ } else if (static_lib) {
+ options.package_type = PackageType::kStaticLib;
+ context.SetPackageId(kAppPackageId);
+ } else {
+ options.package_type = PackageType::kApp;
+ context.SetPackageId(kAppPackageId);
+ }
+
+ if (package_id) {
+ if (options.package_type != PackageType::kApp) {
+ context.GetDiagnostics()->Error(
+ DiagMessage() << "can't specify --package-id when not building a regular app");
+ return 1;
+ }
+
+ const Maybe<uint32_t> maybe_package_id_int = ResourceUtils::ParseInt(package_id.value());
+ if (!maybe_package_id_int) {
+ context.GetDiagnostics()->Error(DiagMessage() << "package ID '" << package_id.value()
+ << "' is not a valid integer");
+ return 1;
+ }
+
+ const uint32_t package_id_int = maybe_package_id_int.value();
+ if (package_id_int < kAppPackageId || package_id_int > std::numeric_limits<uint8_t>::max()) {
+ context.GetDiagnostics()->Error(
+ DiagMessage() << StringPrintf(
+ "invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int));
+ return 1;
+ }
+ context.SetPackageId(static_cast<uint8_t>(package_id_int));
+ }
+
// Populate the set of extra packages for which to generate R.java.
for (std::string& extra_package : extra_java_packages) {
// A given package can actually be a colon separated list of packages.
@@ -2128,18 +2184,6 @@ int Link(const std::vector<StringPiece>& args) {
options.table_splitter_options.preferred_densities.push_back(preferred_density_config.density);
}
- if (shared_lib && static_lib) {
- context.GetDiagnostics()->Error(DiagMessage()
- << "only one of --shared-lib and --static-lib can be defined");
- return 1;
- }
-
- if (shared_lib) {
- options.package_type = PackageType::kSharedLib;
- } else if (static_lib) {
- options.package_type = PackageType::kStaticLib;
- }
-
if (options.package_type != PackageType::kStaticLib && stable_id_file_path) {
if (!LoadStableIdMap(context.GetDiagnostics(), stable_id_file_path.value(),
&options.stable_id_map)) {
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 0331313563c8..833ae69a0317 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -51,18 +51,16 @@ class ReferenceLinkerVisitor : public ValueVisitor {
public:
using ValueVisitor::Visit;
- ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols,
- StringPool* string_pool, xml::IPackageDeclStack* decl,
- CallSite* callsite)
- : context_(context),
+ ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
+ StringPool* string_pool, xml::IPackageDeclStack* decl)
+ : callsite_(callsite),
+ context_(context),
symbols_(symbols),
package_decls_(decl),
- string_pool_(string_pool),
- callsite_(callsite) {}
+ string_pool_(string_pool) {}
void Visit(Reference* ref) override {
- if (!ReferenceLinker::LinkReference(ref, context_, symbols_, package_decls_,
- callsite_)) {
+ if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, package_decls_)) {
error_ = true;
}
}
@@ -97,7 +95,7 @@ class ReferenceLinkerVisitor : public ValueVisitor {
// Find the attribute in the symbol table and check if it is visible from
// this callsite.
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
- transformed_reference, symbols_, callsite_, &err_str);
+ transformed_reference, callsite_, symbols_, &err_str);
if (symbol) {
// Assign our style key the correct ID.
// The ID may not exist.
@@ -105,8 +103,7 @@ class ReferenceLinkerVisitor : public ValueVisitor {
// Try to convert the value to a more specific, typed value based on the
// attribute it is set to.
- entry.value = ParseValueWithAttribute(std::move(entry.value),
- symbol->attribute.get());
+ entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
// Link/resolve the final value (mostly if it's a reference).
entry.value->Accept(this);
@@ -131,8 +128,7 @@ class ReferenceLinkerVisitor : public ValueVisitor {
} else {
DiagMessage msg(entry.key.GetSource());
msg << "style attribute '";
- ReferenceLinker::WriteResourceName(&msg, entry.key,
- transformed_reference);
+ ReferenceLinker::WriteResourceName(&msg, entry.key, transformed_reference);
msg << "' " << err_str;
context_->GetDiagnostics()->Error(msg);
error_ = true;
@@ -158,28 +154,26 @@ class ReferenceLinkerVisitor : public ValueVisitor {
ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
// If we could not parse as any specific type, try a basic STRING.
- if (!transformed &&
- (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
+ if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
util::StringBuilder string_builder;
string_builder.Append(*raw_string->value);
if (string_builder) {
- transformed = util::make_unique<String>(
- string_pool_->MakeRef(string_builder.ToString()));
+ transformed = util::make_unique<String>(string_pool_->MakeRef(string_builder.ToString()));
}
}
if (transformed) {
return transformed;
}
- };
+ }
return value;
}
+ const CallSite& callsite_;
IAaptContext* context_;
SymbolTable* symbols_;
xml::IPackageDeclStack* package_decls_;
StringPool* string_pool_;
- CallSite* callsite_;
bool error_ = false;
};
@@ -234,8 +228,8 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& refer
}
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
+ const CallSite& callsite,
SymbolTable* symbols,
- CallSite* callsite,
std::string* out_error) {
const SymbolTable::Symbol* symbol = ResolveSymbol(reference, symbols);
if (!symbol) {
@@ -243,7 +237,7 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const R
return nullptr;
}
- if (!IsSymbolVisible(*symbol, reference, *callsite)) {
+ if (!IsSymbolVisible(*symbol, reference, callsite)) {
if (out_error) *out_error = "is private";
return nullptr;
}
@@ -251,9 +245,10 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const R
}
const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
- const Reference& reference, SymbolTable* symbols, CallSite* callsite, std::string* out_error) {
+ const Reference& reference, const CallSite& callsite, SymbolTable* symbols,
+ std::string* out_error) {
const SymbolTable::Symbol* symbol =
- ResolveSymbolCheckVisibility(reference, symbols, callsite, out_error);
+ ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error);
if (!symbol) {
return nullptr;
}
@@ -266,12 +261,12 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
}
Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
+ const CallSite& callsite,
SymbolTable* symbols,
- CallSite* callsite,
std::string* out_error) {
- const SymbolTable::Symbol* symbol = ResolveSymbol(reference, symbols);
+ const SymbolTable::Symbol* symbol =
+ ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error);
if (!symbol) {
- if (out_error) *out_error = "not found";
return {};
}
@@ -297,10 +292,9 @@ void ReferenceLinker::WriteResourceName(DiagMessage* out_msg,
}
}
-bool ReferenceLinker::LinkReference(Reference* reference, IAaptContext* context,
- SymbolTable* symbols,
- xml::IPackageDeclStack* decls,
- CallSite* callsite) {
+bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
+ IAaptContext* context, SymbolTable* symbols,
+ xml::IPackageDeclStack* decls) {
CHECK(reference != nullptr);
CHECK(reference->name || reference->id);
@@ -309,7 +303,7 @@ bool ReferenceLinker::LinkReference(Reference* reference, IAaptContext* context,
std::string err_str;
const SymbolTable::Symbol* s =
- ResolveSymbolCheckVisibility(transformed_reference, symbols, callsite, &err_str);
+ ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str);
if (s) {
// The ID may not exist. This is fine because of the possibility of building
// against libraries without assigned IDs.
@@ -344,11 +338,9 @@ bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
error = true;
}
- CallSite callsite = {
- ResourceNameRef(package->name, type->type, entry->name)};
- ReferenceLinkerVisitor visitor(context, context->GetExternalSymbols(),
- &table->string_pool, &decl_stack,
- &callsite);
+ CallSite callsite = {ResourceNameRef(package->name, type->type, entry->name)};
+ ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
+ &table->string_pool, &decl_stack);
for (auto& config_value : entry->values) {
config_value->value->Accept(&visitor);
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 2d18c49d3687..b3d0196d9e7c 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -59,8 +59,8 @@ class ReferenceLinker : public IResourceTableConsumer {
* returned. out_error holds the error message.
*/
static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,
+ const CallSite& callsite,
SymbolTable* symbols,
- CallSite* callsite,
std::string* out_error);
/**
@@ -70,8 +70,8 @@ class ReferenceLinker : public IResourceTableConsumer {
* ISymbolTable::Symbol::attribute.
*/
static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,
+ const CallSite& callsite,
SymbolTable* symbols,
- CallSite* callsite,
std::string* out_error);
/**
@@ -80,7 +80,8 @@ class ReferenceLinker : public IResourceTableConsumer {
* If resolution fails, outError holds the error message.
*/
static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
- SymbolTable* symbols, CallSite* callsite,
+ const CallSite& callsite,
+ SymbolTable* symbols,
std::string* out_error);
/**
@@ -99,9 +100,8 @@ class ReferenceLinker : public IResourceTableConsumer {
* Returns false on failure, and an error message is logged to the
* IDiagnostics in the context.
*/
- static bool LinkReference(Reference* reference, IAaptContext* context,
- SymbolTable* symbols, xml::IPackageDeclStack* decls,
- CallSite* callsite);
+ static bool LinkReference(const CallSite& callsite, Reference* reference, IAaptContext* context,
+ SymbolTable* symbols, xml::IPackageDeclStack* decls);
/**
* Links all references in the ResourceTable.
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 4ca36a9e2b22..d8e33a42711a 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -53,21 +53,20 @@ TEST(ReferenceLinkerTest, LinkSimpleReferences) {
ReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context.get(), table.get()));
- Reference* ref =
- test::GetValue<Reference>(table.get(), "com.app.test:string/foo");
- ASSERT_NE(ref, nullptr);
+ Reference* ref = test::GetValue<Reference>(table.get(), "com.app.test:string/foo");
+ ASSERT_NE(nullptr, ref);
AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f020001));
+ EXPECT_EQ(ResourceId(0x7f020001), ref->id.value());
ref = test::GetValue<Reference>(table.get(), "com.app.test:string/bar");
- ASSERT_NE(ref, nullptr);
+ ASSERT_NE(nullptr, ref);
AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x7f020002));
+ EXPECT_EQ(ResourceId(0x7f020002), ref->id.value());
ref = test::GetValue<Reference>(table.get(), "com.app.test:string/baz");
- ASSERT_NE(ref, nullptr);
+ ASSERT_NE(nullptr, ref);
AAPT_ASSERT_TRUE(ref->id);
- EXPECT_EQ(ref->id.value(), ResourceId(0x01040034));
+ EXPECT_EQ(ResourceId(0x01040034), ref->id.value());
}
TEST(ReferenceLinkerTest, LinkStyleAttributes) {
@@ -87,9 +86,8 @@ TEST(ReferenceLinkerTest, LinkStyleAttributes) {
// We need to fill in the value for the attribute android:attr/bar after we
// build the
// table, because we need access to the string pool.
- Style* style =
- test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
- ASSERT_NE(style, nullptr);
+ Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
+ ASSERT_NE(nullptr, style);
style->entries.back().value =
util::make_unique<RawString>(table->string_pool.MakeRef("one|two"));
}
@@ -120,20 +118,20 @@ TEST(ReferenceLinkerTest, LinkStyleAttributes) {
ASSERT_TRUE(linker.Consume(context.get(), table.get()));
Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
- ASSERT_NE(style, nullptr);
+ ASSERT_NE(nullptr, style);
AAPT_ASSERT_TRUE(style->parent);
AAPT_ASSERT_TRUE(style->parent.value().id);
- EXPECT_EQ(style->parent.value().id.value(), ResourceId(0x01060000));
+ EXPECT_EQ(ResourceId(0x01060000), style->parent.value().id.value());
ASSERT_EQ(2u, style->entries.size());
AAPT_ASSERT_TRUE(style->entries[0].key.id);
- EXPECT_EQ(style->entries[0].key.id.value(), ResourceId(0x01010001));
- ASSERT_NE(ValueCast<BinaryPrimitive>(style->entries[0].value.get()), nullptr);
+ EXPECT_EQ(ResourceId(0x01010001), style->entries[0].key.id.value());
+ ASSERT_NE(nullptr, ValueCast<BinaryPrimitive>(style->entries[0].value.get()));
AAPT_ASSERT_TRUE(style->entries[1].key.id);
- EXPECT_EQ(style->entries[1].key.id.value(), ResourceId(0x01010002));
- ASSERT_NE(ValueCast<BinaryPrimitive>(style->entries[1].value.get()), nullptr);
+ EXPECT_EQ(ResourceId(0x01010002), style->entries[1].key.id.value());
+ ASSERT_NE(nullptr, ValueCast<BinaryPrimitive>(style->entries[1].value.get()));
}
TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
@@ -167,10 +165,10 @@ TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
ASSERT_TRUE(linker.Consume(context.get(), table.get()));
Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
- ASSERT_NE(style, nullptr);
+ ASSERT_NE(nullptr, style);
ASSERT_EQ(1u, style->entries.size());
AAPT_ASSERT_TRUE(style->entries.front().key.id);
- EXPECT_EQ(style->entries.front().key.id.value(), ResourceId(0x7f010000));
+ EXPECT_EQ(ResourceId(0x7f010000), style->entries.front().key.id.value());
}
TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) {
@@ -257,4 +255,42 @@ TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) {
ASSERT_FALSE(linker.Consume(context.get(), table.get()));
}
+TEST(ReferenceLinkerTest, AppsWithSamePackageButDifferentIdAreVisibleNonPublic) {
+ NameMangler mangler(NameManglerPolicy{"com.app.test"});
+ SymbolTable table(&mangler);
+ table.AppendSource(test::StaticSymbolSourceBuilder()
+ .AddSymbol("com.app.test:string/foo", ResourceId(0x7f010000))
+ .Build());
+
+ std::string error;
+ const CallSite call_site{ResourceNameRef("com.app.test", ResourceType::kString, "foo")};
+ const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
+ *test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
+ ASSERT_NE(nullptr, symbol);
+ EXPECT_TRUE(error.empty());
+}
+
+TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute) {
+ NameMangler mangler(NameManglerPolicy{"com.app.ext"});
+ SymbolTable table(&mangler);
+ table.AppendSource(test::StaticSymbolSourceBuilder()
+ .AddSymbol("com.app.test:attr/foo", ResourceId(0x7f010000),
+ test::AttributeBuilder().Build())
+ .AddPublicSymbol("com.app.test:attr/public_foo", ResourceId(0x7f010001),
+ test::AttributeBuilder().Build())
+ .Build());
+
+ std::string error;
+ const CallSite call_site{ResourceNameRef("com.app.ext", ResourceType::kLayout, "foo")};
+
+ AAPT_EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
+ *test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
+ EXPECT_FALSE(error.empty());
+
+ error = "";
+ AAPT_ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute(
+ *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, &error));
+ EXPECT_TRUE(error.empty());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index b839862a0689..94bdccd5ab64 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -42,12 +42,12 @@ class ReferenceVisitor : public ValueVisitor {
public:
using ValueVisitor::Visit;
- ReferenceVisitor(IAaptContext* context, SymbolTable* symbols, xml::IPackageDeclStack* decls,
- CallSite* callsite)
- : context_(context), symbols_(symbols), decls_(decls), callsite_(callsite), error_(false) {}
+ ReferenceVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
+ xml::IPackageDeclStack* decls)
+ : callsite_(callsite), context_(context), symbols_(symbols), decls_(decls), error_(false) {}
void Visit(Reference* ref) override {
- if (!ReferenceLinker::LinkReference(ref, context_, symbols_, decls_, callsite_)) {
+ if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, decls_)) {
error_ = true;
}
}
@@ -57,10 +57,10 @@ class ReferenceVisitor : public ValueVisitor {
private:
DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
+ const CallSite& callsite_;
IAaptContext* context_;
SymbolTable* symbols_;
xml::IPackageDeclStack* decls_;
- CallSite* callsite_;
bool error_;
};
@@ -71,14 +71,14 @@ class XmlVisitor : public xml::PackageAwareVisitor {
public:
using xml::PackageAwareVisitor::Visit;
- XmlVisitor(IAaptContext* context, SymbolTable* symbols, const Source& source,
- std::set<int>* sdk_levels_found, CallSite* callsite)
- : context_(context),
+ XmlVisitor(const Source& source, const CallSite& callsite, IAaptContext* context,
+ SymbolTable* symbols, std::set<int>* sdk_levels_found)
+ : source_(source),
+ callsite_(callsite),
+ context_(context),
symbols_(symbols),
- source_(source),
sdk_levels_found_(sdk_levels_found),
- callsite_(callsite),
- reference_visitor_(context, symbols, this, callsite) {}
+ reference_visitor_(callsite, context, symbols, this) {}
void Visit(xml::Element* el) override {
// The default Attribute allows everything except enums or flags.
@@ -108,7 +108,7 @@ class XmlVisitor : public xml::PackageAwareVisitor {
std::string err_str;
attr.compiled_attribute =
- ReferenceLinker::CompileXmlAttribute(attr_ref, symbols_, callsite_, &err_str);
+ ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str);
if (!attr.compiled_attribute) {
context_->GetDiagnostics()->Error(DiagMessage(source) << "attribute '"
@@ -159,11 +159,12 @@ class XmlVisitor : public xml::PackageAwareVisitor {
private:
DISALLOW_COPY_AND_ASSIGN(XmlVisitor);
+ Source source_;
+ const CallSite& callsite_;
IAaptContext* context_;
SymbolTable* symbols_;
- Source source_;
+
std::set<int>* sdk_levels_found_;
- CallSite* callsite_;
ReferenceVisitor reference_visitor_;
bool error_ = false;
};
@@ -172,9 +173,9 @@ class XmlVisitor : public xml::PackageAwareVisitor {
bool XmlReferenceLinker::Consume(IAaptContext* context, xml::XmlResource* resource) {
sdk_levels_found_.clear();
- CallSite callsite = {resource->file.name};
- XmlVisitor visitor(context, context->GetExternalSymbols(), resource->file.source,
- &sdk_levels_found_, &callsite);
+ const CallSite callsite = {resource->file.name};
+ XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols(),
+ &sdk_levels_found_);
if (resource->root) {
resource->root->Accept(&visitor);
return !visitor.HasError();
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index bba316f4b484..fd8a5080278b 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -30,10 +30,8 @@ TEST(ResourceTableSymbolSourceTest, FindSymbols) {
.Build();
ResourceTableSymbolSource symbol_source(table.get());
- EXPECT_NE(nullptr,
- symbol_source.FindByName(test::ParseNameOrDie("android:id/foo")));
- EXPECT_NE(nullptr,
- symbol_source.FindByName(test::ParseNameOrDie("android:id/bar")));
+ EXPECT_NE(nullptr, symbol_source.FindByName(test::ParseNameOrDie("android:id/foo")));
+ EXPECT_NE(nullptr, symbol_source.FindByName(test::ParseNameOrDie("android:id/bar")));
std::unique_ptr<SymbolTable::Symbol> s =
symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo"));
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index fedd65c4e239..1c9a75d0547d 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -1,5 +1,13 @@
# Android Asset Packaging Tool 2.0 (AAPT2) release notes
+## Version 2.10
+### `aapt2 link ...`
+- Add ability to specify package ID to compile with for regular apps (not shared or static libs).
+ This package ID is limited to the range 0x7f-0xff inclusive. Specified with the --package-id
+ flag.
+- Fixed issue with <plurals> resources being stripped for locales and other configuration.
+- Fixed issue with escaping strings in XML resources.
+
## Version 2.9
### `aapt2 link ...`
- Added sparse resource type encoding, which encodes resource entries that are sparse with
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index 8c6740c41052..43c95f483b48 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -650,6 +650,20 @@ public final class Bitmap_Delegate {
return null;
}
+ @LayoutlibDelegate
+ /*package*/ static boolean nativeIsSRGB(long nativeBitmap) {
+ Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
+ "Color spaces are not supported", null /*data*/);
+ return false;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params) {
+ Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
+ "Color spaces are not supported", null /*data*/);
+ return false;
+ }
+
// ---- Private delegate/helper methods ----
private Bitmap_Delegate(BufferedImage image, Config config) {
diff --git a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java
index fcd63ea993c8..499e58a5d4e5 100644
--- a/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/Hyphenator_Delegate.java
@@ -40,7 +40,7 @@ public class Hyphenator_Delegate {
}
/*package*/ @SuppressWarnings("UnusedParameters") // TODO implement this.
- static long loadHyphenator(ByteBuffer buffer, int offset) {
+ static long loadHyphenator(ByteBuffer buffer, int offset, int minPrefix, int minSuffix) {
return sDelegateManager.addNewDelegate(new Hyphenator_Delegate());
}
}
diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
index 970c7d577c51..1b9901594f6e 100644
--- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
@@ -53,8 +53,9 @@ public class StaticLayout_Delegate {
}
@LayoutlibDelegate
- /*package*/ static long nLoadHyphenator(ByteBuffer buf, int offset) {
- return Hyphenator_Delegate.loadHyphenator(buf, offset);
+ /*package*/ static long nLoadHyphenator(ByteBuffer buf, int offset, int minPrefix,
+ int minSuffix) {
+ return Hyphenator_Delegate.loadHyphenator(buf, offset, minPrefix, minSuffix);
}
@LayoutlibDelegate
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
index 59fe1ee6bc80..156c3fdf336c 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java
@@ -116,6 +116,7 @@ public class DiscoverySession {
Log.w(TAG, "terminate: already terminated.");
return;
}
+
mTerminated = true;
mMgr.clear();
mCloseGuard.close();
@@ -172,15 +173,15 @@ public class DiscoverySession {
if (mTerminated) {
Log.w(TAG, "sendMessage: called on terminated session");
return;
- } else {
- WifiAwareManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "sendMessage: called post GC on WifiAwareManager");
- return;
- }
+ }
- mgr.sendMessage(mClientId, mSessionId, peerHandle, message, messageId, retryCount);
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "sendMessage: called post GC on WifiAwareManager");
+ return;
}
+
+ mgr.sendMessage(mClientId, mSessionId, peerHandle, message, messageId, retryCount);
}
/**
@@ -235,15 +236,15 @@ public class DiscoverySession {
if (mTerminated) {
Log.w(TAG, "startRanging: called on terminated session");
return;
- } else {
- WifiAwareManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "startRanging: called post GC on WifiAwareManager");
- return;
- }
+ }
- mgr.startRanging(mClientId, mSessionId, params, listener);
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "startRanging: called post GC on WifiAwareManager");
+ return;
}
+
+ mgr.startRanging(mClientId, mSessionId, params, listener);
}
/**
@@ -261,6 +262,9 @@ public class DiscoverySession {
* <p>
* Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
* and a Publisher is a RESPONDER.
+ * <p>
+ * To set up an encrypted link use the {@link #createNetworkSpecifierPmk(PeerHandle, byte[])}
+ * or {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} APIs.
*
* @param peerHandle The peer's handle obtained through
* {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}
@@ -282,19 +286,81 @@ public class DiscoverySession {
if (mTerminated) {
Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session");
return null;
- } else {
- WifiAwareManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
- return null;
- }
+ }
+
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
+ return null;
+ }
+
+ int role = this instanceof SubscribeDiscoverySession
+ ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+
+ return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null, null);
+ }
+
+ /**
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+ * encrypted WiFi Aware connection (link) to the specified peer. The
+ * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+ * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
+ * <p>
+ * This method should be used when setting up a connection with a peer discovered through Aware
+ * discovery or communication (in such scenarios the MAC address of the peer is shielded by
+ * an opaque peer ID handle). If a Aware connection is needed to a peer discovered using other
+ * OOB (out-of-band) mechanism then use the alternative
+ * {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)} method -
+ * which uses the peer's MAC address.
+ * <p>
+ * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
+ * and a Publisher is a RESPONDER.
+ *
+ * @param peerHandle The peer's handle obtained through
+ * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
+ * byte[], java.util.List)} or
+ * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
+ * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
+ * from only that peer. A RESPONDER may specify a null - indicating that
+ * it will accept connection requests from any device.
+ * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from
+ * the passphrase. Use the
+ * {@link #createNetworkSpecifierPmk(PeerHandle, byte[])} to specify the
+ * PMK directly or {@link #createNetworkSpecifierOpen(PeerHandle)} to
+ * specify an open (unencrypted) link.
+ *
+ * @return A string to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+ * android.net.ConnectivityManager.NetworkCallback)}
+ * [or other varieties of that API].
+ *
+ * * @hide
+ */
+ public String createNetworkSpecifierPassphrase(@Nullable PeerHandle peerHandle,
+ @NonNull String passphrase) {
+ if (passphrase == null || passphrase.length() == 0) {
+ throw new IllegalArgumentException("Passphrase must not be null or empty");
+ }
- int role = this instanceof SubscribeDiscoverySession
- ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
- : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+ if (mTerminated) {
+ Log.w(TAG, "createNetworkSpecifierPassphrase: called on terminated session");
+ return null;
+ }
- return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null);
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager");
+ return null;
}
+
+ int role = this instanceof SubscribeDiscoverySession
+ ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+
+ return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null,
+ passphrase);
}
/**
@@ -307,8 +373,8 @@ public class DiscoverySession {
* discovery or communication (in such scenarios the MAC address of the peer is shielded by
* an opaque peer ID handle). If a Aware connection is needed to a peer discovered using other
* OOB (out-of-band) mechanism then use the alternative
- * {@link WifiAwareSession#createNetworkSpecifierPmk(int, byte[], byte[])} method - which uses the
- * peer's MAC address.
+ * {@link WifiAwareSession#createNetworkSpecifierPmk(int, byte[], byte[])} method - which uses
+ * the peer's MAC address.
* <p>
* Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
* and a Publisher is a RESPONDER.
@@ -322,8 +388,9 @@ public class DiscoverySession {
* it will accept connection requests from any device.
* @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
* encrypting the data-path. Use the
- * {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an open (unencrypted)
- * link.
+ * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} to specify a
+ * Passphrase or {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an
+ * open (unencrypted) link.
*
* @return A string to be used to construct
* {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
@@ -342,19 +409,19 @@ public class DiscoverySession {
if (mTerminated) {
Log.w(TAG, "createNetworkSpecifierPmk: called on terminated session");
return null;
- } else {
- WifiAwareManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
- return null;
- }
-
- int role = this instanceof SubscribeDiscoverySession
- ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
- : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+ }
- return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, pmk);
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
+ return null;
}
+
+ int role = this instanceof SubscribeDiscoverySession
+ ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+
+ return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, pmk, null);
}
/**
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 3d784ba02fd6..7b6805c3da92 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -130,26 +130,26 @@ public class WifiAwareManager {
*/
/**
- * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk optional
+ * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional
* @hide
*/
public static final int NETWORK_SPECIFIER_TYPE_IB = 0;
/**
- * TYPE: in band, any peer: role, client_id, session_id, pmk optional
+ * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional
* [only permitted for RESPONDER]
* @hide
*/
public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1;
/**
- * TYPE: out-of-band: role, client_id, peer_mac, pmk optional
+ * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional
* @hide
*/
public static final int NETWORK_SPECIFIER_TYPE_OOB = 2;
/**
- * TYPE: out-of-band, any peer: role, client_id, pmk optional
+ * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional
* [only permitted for RESPONDER]
* @hide
*/
@@ -180,6 +180,9 @@ public class WifiAwareManager {
/** @hide */
public static final String NETWORK_SPECIFIER_KEY_PMK = "pmk";
+ /** @hide */
+ public static final String NETWORK_SPECIFIER_KEY_PASSPHRASE = "passphrase";
+
/**
* Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed.
* Use the {@link #isAvailable()} to query the current status.
@@ -473,11 +476,12 @@ public class WifiAwareManager {
/** @hide */
public String createNetworkSpecifier(int clientId, int role, int sessionId,
- PeerHandle peerHandle, @Nullable byte[] pmk) {
+ PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
if (VDBG) {
Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
+ ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId)
- + ", pmk=" + ((pmk == null) ? "null" : "non-null"));
+ + ", pmk=" + ((pmk == null) ? "null" : "non-null")
+ + ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
}
int type = (peerHandle == null) ? NETWORK_SPECIFIER_TYPE_IB_ANY_PEER
@@ -512,6 +516,11 @@ public class WifiAwareManager {
}
json.put(NETWORK_SPECIFIER_KEY_PMK,
Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT));
+ if (passphrase == null) {
+ passphrase = new String();
+ }
+ json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase);
+
} catch (JSONException e) {
return "";
}
@@ -521,10 +530,11 @@ public class WifiAwareManager {
/** @hide */
public String createNetworkSpecifier(int clientId, @DataPathRole int role,
- @Nullable byte[] peer, @Nullable byte[] pmk) {
+ @Nullable byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) {
if (VDBG) {
Log.v(TAG, "createNetworkSpecifier: role=" + role
- + ", pmk=" + ((pmk == null) ? "null" : "non-null"));
+ + ", pmk=" + ((pmk == null) ? "null" : "non-null")
+ + ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
}
int type = (peer == null) ?
@@ -560,6 +570,10 @@ public class WifiAwareManager {
}
json.put(NETWORK_SPECIFIER_KEY_PMK,
Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT));
+ if (passphrase == null) {
+ passphrase = new String();
+ }
+ json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase);
} catch (JSONException e) {
return "";
}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
index 856066efff62..f48f64184bfb 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -192,6 +192,9 @@ public class WifiAwareSession {
* (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
* when using Aware discovery use the alternative network specifier method -
* {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)}.
+ * <p>
+ * To set up an encrypted link use the {@link #createNetworkSpecifierPmk(int, byte[], byte[])}
+ * or {@link #createNetworkSpecifierPassphrase(int, byte[], String)} APIs.
*
* @param role The role of this device:
* {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
@@ -220,7 +223,56 @@ public class WifiAwareSession {
Log.e(TAG, "createNetworkSpecifierOpen: called after termination");
return "";
}
- return mgr.createNetworkSpecifier(mClientId, role, peer, null);
+ return mgr.createNetworkSpecifier(mClientId, role, peer, null, null);
+ }
+
+ /**
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+ * encrypted WiFi Aware connection (link) to the specified peer. The
+ * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+ * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
+ * <p>
+ * This API is targeted for applications which can obtain the peer MAC address using OOB
+ * (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
+ * when using Aware discovery use the alternative network specifier method -
+ * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
+ *
+ * @param role The role of this device:
+ * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
+ * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
+ * @param peer The MAC address of the peer's Aware discovery interface. On a RESPONDER this
+ * value is used to gate the acceptance of a connection request from only that
+ * peer. A RESPONDER may specify a null - indicating that it will accept
+ * connection requests from any device.
+ * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from
+ * the passphrase. Use the
+ * {@link #createNetworkSpecifierPmk(int, byte[], byte[])} to specify the
+ * PMK directly or {@link #createNetworkSpecifierOpen(int, byte[])} to
+ * specify an open (unencrypted) link.
+ *
+ * @return A string to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+ * android.net.ConnectivityManager.NetworkCallback)}
+ * [or other varieties of that API].
+ *
+ * @hide
+ */
+ public String createNetworkSpecifierPassphrase(@WifiAwareManager.DataPathRole int role,
+ @Nullable byte[] peer, @NonNull String passphrase) {
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.e(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager");
+ return "";
+ }
+ if (mTerminated) {
+ Log.e(TAG, "createNetworkSpecifierPassphrase: called after termination");
+ return "";
+ }
+ if (passphrase == null || passphrase.length() == 0) {
+ throw new IllegalArgumentException("Passphrase must not be null or empty");
+ }
+ return mgr.createNetworkSpecifier(mClientId, role, peer, null, passphrase);
}
/**
@@ -232,7 +284,7 @@ public class WifiAwareSession {
* This API is targeted for applications which can obtain the peer MAC address using OOB
* (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
* when using Aware discovery use the alternative network specifier method -
- * {@link DiscoverySession#createNetworkSpecifierPmk(PeerHandle, byte[])}}.
+ * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
*
* @param role The role of this device:
* {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
@@ -242,8 +294,10 @@ public class WifiAwareSession {
* peer. A RESPONDER may specify a null - indicating that it will accept
* connection requests from any device.
* @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
- * encrypting the data-path. Use the {@link #createNetworkSpecifierOpen(int, byte[])}
- * to specify an open (unencrypted) link.
+ * encrypting the data-path. Use the
+ * {@link #createNetworkSpecifierPassphrase(int, byte[], String)} to specify a
+ * Passphrase or {@link #createNetworkSpecifierOpen(int, byte[])} to specify an
+ * open (unencrypted) link.
*
* @return A string to be used to construct
* {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
@@ -267,7 +321,7 @@ public class WifiAwareSession {
if (pmk == null || pmk.length == 0) {
throw new IllegalArgumentException("PMK must not be null or empty");
}
- return mgr.createNetworkSpecifier(mClientId, role, peer, pmk);
+ return mgr.createNetworkSpecifier(mClientId, role, peer, pmk, null);
}
/**
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 992958b84e1a..eceb3658d1d6 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -974,6 +974,7 @@ public class WifiAwareManagerTest {
final PeerHandle peerHandle = new PeerHandle(123412);
final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
final byte[] pmk = "Some arbitrary byte array".getBytes();
+ final String passphrase = "A really bad password";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final PublishConfig publishConfig = new PublishConfig.Builder().build();
@@ -1038,6 +1039,23 @@ public class WifiAwareManagerTest {
collector.checkThat("pmk", pmkB64 ,
equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK)));
+ // (5) request an encrypted (Passphrase) network specifier from the session
+ networkSpecifier = publishSession.getValue().createNetworkSpecifierPassphrase(peerHandle,
+ passphrase);
+
+ // validate format
+ jsonObject = new JSONObject(networkSpecifier);
+ collector.checkThat("role", role,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+ collector.checkThat("client_id", clientId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+ collector.checkThat("session_id", sessionId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
+ collector.checkThat("peer_id", peerHandle.peerId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
+ collector.checkThat("passphrase", passphrase,
+ equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE)));
+
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
mockPublishSession, mockRttListener);
}
@@ -1053,6 +1071,7 @@ public class WifiAwareManagerTest {
final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
final byte[] pmk = "Some arbitrary pmk data".getBytes();
+ final String passphrase = "A really bad password";
String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT);
@@ -1101,6 +1120,21 @@ public class WifiAwareManagerTest {
collector.checkThat("pmk", pmkB64,
equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK)));
+ // (4) request an encrypted (Passphrase) direct network specifier
+ networkSpecifier = session.createNetworkSpecifierPassphrase(role, someMac, passphrase);
+
+ // validate format
+ jsonObject = new JSONObject(networkSpecifier);
+ collector.checkThat("role", role,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+ collector.checkThat("client_id", clientId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+ collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
+ jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
+ false)));
+ collector.checkThat("passphrase", passphrase,
+ equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE)));
+
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
mockPublishSession, mockRttListener);
}