summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp44
-rw-r--r--apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java4
-rw-r--r--api/current.txt12
-rw-r--r--api/system-current.txt11
-rw-r--r--api/test-current.txt9
-rw-r--r--cmds/statsd/src/FieldValue.cpp21
-rw-r--r--cmds/statsd/src/atoms.proto51
-rw-r--r--cmds/statsd/src/matchers/matcher_util.cpp5
-rw-r--r--cmds/statsd/tests/LogEntryMatcher_test.cpp1
-rw-r--r--config/hiddenapi-greylist.txt279
-rw-r--r--core/java/android/accessibilityservice/AccessibilityGestureEvent.aidl (renamed from core/java/android/accessibilityservice/AccessibilityGestureInfo.aidl)2
-rw-r--r--core/java/android/accessibilityservice/AccessibilityGestureEvent.java (renamed from core/java/android/accessibilityservice/AccessibilityGestureInfo.java)24
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java22
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl4
-rw-r--r--core/java/android/app/ActivityThread.java18
-rw-r--r--core/java/android/app/AppCompatCallbacks.java20
-rw-r--r--core/java/android/app/AppOpsManager.java22
-rw-r--r--core/java/android/app/Notification.java8
-rw-r--r--core/java/android/app/UiAutomation.java4
-rw-r--r--core/java/android/content/pm/LauncherApps.java6
-rw-r--r--core/java/android/content/pm/PackageManager.java4
-rw-r--r--core/java/android/content/pm/PackageManagerInternal.java28
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java2
-rw-r--r--core/java/android/os/Process.java6
-rw-r--r--core/java/android/os/SystemProperties.java2
-rw-r--r--core/java/android/service/carrier/ApnService.java2
-rw-r--r--core/java/android/service/carrier/IApnSourceService.aidl (renamed from telephony/java/com/android/internal/telephony/IApnSourceService.aidl)3
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java15
-rw-r--r--core/java/android/text/TextLine.java3
-rw-r--r--core/java/android/view/IPinnedStackController.aidl18
-rw-r--r--core/java/android/view/IPinnedStackListener.aidl62
-rw-r--r--core/java/android/view/IRecentsAnimationRunner.aidl4
-rw-r--r--core/java/android/view/IRemoteAnimationRunner.aidl2
-rw-r--r--core/java/android/view/NotificationHeaderView.java11
-rw-r--r--core/java/android/view/SyncRtSurfaceTransactionApplier.java148
-rw-r--r--core/java/android/view/ViewRootImpl.java30
-rw-r--r--core/java/android/view/WindowlessViewRoot.java3
-rw-r--r--core/java/android/view/WindowlessWindowManager.java17
-rw-r--r--core/java/android/view/accessibility/AccessibilityCache.java204
-rw-r--r--core/java/android/view/accessibility/AccessibilityInteractionClient.java13
-rw-r--r--core/java/android/view/animation/Animation.java12
-rw-r--r--core/java/android/view/contentcapture/ChildContentCaptureSession.java5
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureSession.java13
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java10
-rw-r--r--core/java/android/widget/Editor.java3
-rw-r--r--core/java/com/android/internal/compat/ChangeReporter.java73
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java45
-rw-r--r--core/jni/android_os_SystemProperties.cpp2
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp2
-rw-r--r--core/proto/android/app/settings_enums.proto4
-rw-r--r--core/proto/android/server/windowmanagerservice.proto2
-rw-r--r--core/res/res/drawable/media_seamless_background.xml26
-rw-r--r--core/res/res/layout/notification_material_media_transfer_action.xml39
-rw-r--r--core/res/res/layout/notification_template_header.xml4
-rw-r--r--core/res/res/layout/notification_template_material_big_media.xml4
-rw-r--r--core/res/res/values/strings.xml4
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/Android.bp1
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java218
-rw-r--r--core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java5
-rw-r--r--core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java30
-rw-r--r--keystore/java/android/security/Credentials.java17
-rw-r--r--keystore/java/android/security/IKeyChainService.aidl3
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp2
-rw-r--r--libs/hwui/renderthread/ReliableSurface.h2
-rw-r--r--location/lib/Android.bp2
-rw-r--r--media/java/android/media/ExifInterface.java32
-rw-r--r--media/jni/android_media_MediaCodec.cpp2
-rw-r--r--media/lib/signer/Android.bp2
-rw-r--r--packages/BackupEncryption/proto/backup_chunks_metadata.proto (renamed from core/proto/android/server/backup_chunks_metadata.proto)4
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/Chunk.java70
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java47
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java6
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java2
-rw-r--r--packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java2
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java181
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.java122
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java2
-rw-r--r--packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java2
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java2
-rw-r--r--packages/CarrierDefaultApp/tests/unit/Android.bp1
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java4
-rw-r--r--packages/SystemUI/Android.bp10
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java2
-rw-r--r--packages/SystemUI/res/layout/auth_biometric_contents.xml2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java115
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java4
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java6
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java2
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/ActivityBinder.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/ComponentBinder.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/CornerHandleView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/DependencyProvider.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/ServiceBinder.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIBinder.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIService.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java51
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java141
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java151
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java186
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java (renamed from packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java)26
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java306
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java158
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java)40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java (renamed from packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java)16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java218
-rw-r--r--proto/src/metrics_constants/metrics_constants.proto3
-rw-r--r--proto/src/wifi.proto19
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java22
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java16
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java309
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java290
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java127
-rw-r--r--services/core/java/com/android/server/MountServiceIdler.java25
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java1
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java9
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java17
-rw-r--r--services/core/java/com/android/server/am/MemoryStatUtil.java24
-rw-r--r--services/core/java/com/android/server/am/OWNERS1
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java2
-rw-r--r--services/core/java/com/android/server/audio/TEST_MAPPING15
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java20
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java2
-rw-r--r--services/core/java/com/android/server/compat/PlatformCompat.java21
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java9
-rw-r--r--services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java27
-rw-r--r--services/core/java/com/android/server/display/color/TintController.java26
-rw-r--r--services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java14
-rw-r--r--services/core/java/com/android/server/location/AbstractLocationProvider.java29
-rw-r--r--services/core/java/com/android/server/notification/NotificationComparator.java6
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java46
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java2
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerShellCommand.java106
-rw-r--r--services/core/java/com/android/server/pm/AppsFilter.java65
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java26
-rw-r--r--services/core/java/com/android/server/pm/Settings.java12
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java41
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java29
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java26
-rw-r--r--services/core/java/com/android/server/security/VerityUtils.java188
-rw-r--r--services/core/java/com/android/server/stats/ProcfsMemoryUtil.java75
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java2
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java15
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java16
-rw-r--r--services/core/java/com/android/server/wm/AnimationAdapter.java6
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java2
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java28
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java51
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java1
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java11
-rw-r--r--services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java1
-rw-r--r--services/core/java/com/android/server/wm/InsetsSourceProvider.java5
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java8
-rw-r--r--services/core/java/com/android/server/wm/LocalAnimationAdapter.java12
-rw-r--r--services/core/java/com/android/server/wm/PinnedStackController.java146
-rw-r--r--services/core/java/com/android/server/wm/PointerEventDispatcher.java3
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java79
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java50
-rw-r--r--services/core/java/com/android/server/wm/RootActivityContainer.java25
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java3
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java3
-rw-r--r--services/core/java/com/android/server/wm/Task.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java12
-rw-r--r--services/core/java/com/android/server/wm/TaskRecord.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java121
-rw-r--r--services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java179
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java1
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimationSpec.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java180
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessListener.java1
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java30
-rw-r--r--services/core/jni/com_android_server_security_VerityUtils.cpp177
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java240
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java93
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java15
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java111
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java79
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java2
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java33
-rw-r--r--services/wifi/java/android/net/wifi/WifiStackClient.java22
-rw-r--r--startop/apps/test/Android.bp1
-rw-r--r--startop/apps/test/src/CPUIntensive.java67
-rw-r--r--startop/apps/test/src/SystemServerBenchmarkActivity.java20
-rw-r--r--telecomm/java/android/telecom/ConnectionService.java10
-rw-r--r--telecomm/java/android/telecom/Logging/SessionManager.java14
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java3
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java3
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java63
-rw-r--r--telephony/java/android/telephony/UiccAccessRule.java20
-rw-r--r--telephony/java/android/telephony/ims/ImsCallProfile.java59
-rw-r--r--telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl2
-rw-r--r--telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java101
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl7
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyProperties.java7
-rw-r--r--telephony/java/com/google/android/mms/ContentType.java247
-rw-r--r--telephony/java/com/google/android/mms/InvalidHeaderValueException.java44
-rw-r--r--telephony/java/com/google/android/mms/MmsException.java66
-rwxr-xr-xtelephony/java/com/google/android/mms/package.html5
-rw-r--r--telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java95
-rw-r--r--telephony/java/com/google/android/mms/pdu/Base64.java170
-rw-r--r--telephony/java/com/google/android/mms/pdu/CharacterSets.java176
-rw-r--r--telephony/java/com/google/android/mms/pdu/DeliveryInd.java145
-rw-r--r--telephony/java/com/google/android/mms/pdu/EncodedStringValue.java298
-rw-r--r--telephony/java/com/google/android/mms/pdu/GenericPdu.java122
-rw-r--r--telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java164
-rw-r--r--telephony/java/com/google/android/mms/pdu/NotificationInd.java307
-rw-r--r--telephony/java/com/google/android/mms/pdu/NotifyRespInd.java121
-rw-r--r--telephony/java/com/google/android/mms/pdu/PduBody.java204
-rw-r--r--telephony/java/com/google/android/mms/pdu/PduComposer.java1229
-rw-r--r--telephony/java/com/google/android/mms/pdu/PduContentTypes.java113
-rw-r--r--telephony/java/com/google/android/mms/pdu/PduHeaders.java733
-rwxr-xr-xtelephony/java/com/google/android/mms/pdu/PduParser.java2023
-rw-r--r--telephony/java/com/google/android/mms/pdu/PduPart.java439
-rwxr-xr-xtelephony/java/com/google/android/mms/pdu/PduPersister.java1573
-rw-r--r--telephony/java/com/google/android/mms/pdu/QuotedPrintable.java71
-rw-r--r--telephony/java/com/google/android/mms/pdu/ReadOrigInd.java158
-rw-r--r--telephony/java/com/google/android/mms/pdu/ReadRecInd.java150
-rw-r--r--telephony/java/com/google/android/mms/pdu/RetrieveConf.java324
-rw-r--r--telephony/java/com/google/android/mms/pdu/SendConf.java124
-rw-r--r--telephony/java/com/google/android/mms/pdu/SendReq.java370
-rwxr-xr-xtelephony/java/com/google/android/mms/pdu/package.html5
-rw-r--r--telephony/java/com/google/android/mms/util/AbstractCache.java119
-rw-r--r--telephony/java/com/google/android/mms/util/DownloadDrmHelper.java115
-rw-r--r--telephony/java/com/google/android/mms/util/DrmConvertSession.java177
-rw-r--r--telephony/java/com/google/android/mms/util/PduCache.java268
-rw-r--r--telephony/java/com/google/android/mms/util/PduCacheEntry.java50
-rw-r--r--telephony/java/com/google/android/mms/util/SqliteWrapper.java128
-rwxr-xr-xtelephony/java/com/google/android/mms/util/package.html5
-rw-r--r--test-mock/Android.bp2
-rw-r--r--tools/aapt2/Debug.cpp92
-rw-r--r--tools/aapt2/Debug.h1
-rw-r--r--tools/aapt2/Main.cpp5
-rw-r--r--tools/aapt2/cmd/Dump.cpp11
-rw-r--r--tools/aapt2/cmd/Dump.h12
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java5
326 files changed, 16068 insertions, 3234 deletions
diff --git a/Android.bp b/Android.bp
index 35c7b1bc6fdd..e17a9b45d3ef 100644
--- a/Android.bp
+++ b/Android.bp
@@ -163,7 +163,7 @@ filegroup {
}
filegroup {
- name: "framework-srcs",
+ name: "framework-non-updatable-sources",
srcs: [
// Java/AIDL sources under frameworks/base
":framework-core-sources",
@@ -211,6 +211,14 @@ filegroup {
],
}
+filegroup {
+ name: "framework-all-sources",
+ srcs: [
+ ":framework-non-updatable-sources",
+ ":updatable-media-srcs",
+ ],
+}
+
java_defaults {
name: "framework-aidl-export-defaults",
aidl: {
@@ -288,15 +296,12 @@ java_defaults {
defaults: ["framework-aidl-export-defaults"],
installable: true,
- srcs: [
- ":framework-srcs",
- "core/java/**/*.logtags",
- ],
-
aidl: {
generate_get_transaction_name: true,
},
+ srcs: ["core/java/**/*.logtags"],
+
exclude_srcs: [
// See comment on framework-atb-backward-compatibility module below
"core/java/android/content/pm/AndroidTestBaseUpdater.java",
@@ -359,6 +364,7 @@ filegroup {
java_library {
name: "framework-minus-apex",
defaults: ["framework-defaults"],
+ srcs: [":framework-non-updatable-sources"],
javac_shard_size: 150,
}
@@ -379,8 +385,16 @@ java_library {
}
java_library {
+ name: "framework-all",
+ defaults: ["framework-defaults"],
+ srcs: [":framework-all-sources"],
+ installable: false,
+}
+
+java_library {
name: "framework-annotation-proc",
defaults: ["framework-defaults"],
+ srcs: [":framework-all-sources"],
installable: false,
plugins: [
"unsupportedappusage-annotation-processor",
@@ -883,10 +897,21 @@ metalava_framework_docs_args += " --replace-documentation " +
// replacement (with $1, $2 backreferences to the regex groups)
"'$$1https://docs.oracle.com/javase/8/docs/$$2\">' "
+packages_to_document = [
+ "android",
+ "java",
+ "javax",
+ "org.apache.http",
+ "org.json",
+ "org.w3c.dom",
+ "org.xml.sax",
+ "org.xmlpull",
+]
+
stubs_defaults {
name: "framework-doc-stubs-default",
srcs: [
- ":framework-srcs",
+ ":framework-non-updatable-sources",
"core/java/**/*.logtags",
"test-base/src/**/*.java",
":opt-telephony-srcs",
@@ -951,7 +976,7 @@ doc_defaults {
stubs_defaults {
name: "metalava-api-stubs-default",
srcs: [
- ":framework-srcs",
+ ":framework-non-updatable-sources",
"core/java/**/*.logtags",
":opt-telephony-srcs",
":opt-net-voip-srcs",
@@ -974,6 +999,7 @@ stubs_defaults {
"api-versions-jars-dir",
],
sdk_version: "core_platform",
+ filter_packages: packages_to_document,
}
droidstubs {
@@ -1472,7 +1498,7 @@ filegroup {
// annotations to private apis
aidl_mapping {
name: "framework-aidl-mappings",
- srcs: [":framework-srcs"],
+ srcs: [":framework-all-sources"],
output: "framework-aidl-mappings.txt",
}
diff --git a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
index 2b6f4557303b..9cfc3d272145 100644
--- a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
@@ -188,8 +188,8 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
@Override
public void onAnimationStart(IRecentsAnimationController controller,
- RemoteAnimationTarget[] apps, Rect homeContentInsets,
- Rect minimizedHomeBounds) throws RemoteException {
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ Rect homeContentInsets, Rect minimizedHomeBounds) throws RemoteException {
final Pair<String, Boolean> finishCase = finishCases.get(mIteration++ % 2);
final boolean moveRecentsToTop = finishCase.second;
makeInterval();
diff --git a/api/current.txt b/api/current.txt
index 2e099ed8e2a2..b4d110e13261 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2812,12 +2812,12 @@ package android.accessibilityservice {
method public void onClicked(android.accessibilityservice.AccessibilityButtonController);
}
- public final class AccessibilityGestureInfo implements android.os.Parcelable {
+ public final class AccessibilityGestureEvent implements android.os.Parcelable {
method public int describeContents();
method public int getDisplayId();
method public int getGestureId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureInfo> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureEvent> CREATOR;
}
public abstract class AccessibilityService extends android.app.Service {
@@ -2836,7 +2836,7 @@ package android.accessibilityservice {
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public final android.os.IBinder onBind(android.content.Intent);
method @Deprecated protected boolean onGesture(int);
- method public boolean onGesture(@NonNull android.accessibilityservice.AccessibilityGestureInfo);
+ method public boolean onGesture(@NonNull android.accessibilityservice.AccessibilityGestureEvent);
method public abstract void onInterrupt();
method protected boolean onKeyEvent(android.view.KeyEvent);
method protected void onServiceConnected();
@@ -23968,6 +23968,7 @@ package android.media {
method @Nullable public long[] getThumbnailRange();
method public boolean hasAttribute(@NonNull String);
method public boolean hasThumbnail();
+ method public static boolean isSupportedMimeType(@NonNull String);
method public boolean isThumbnailCompressed();
method public void saveAttributes() throws java.io.IOException;
method public void setAttribute(@NonNull String, @Nullable String);
@@ -52672,7 +52673,7 @@ package android.view.animation {
method protected android.view.animation.Animation clone() throws java.lang.CloneNotSupportedException;
method public long computeDurationHint();
method protected void ensureInterpolator();
- method @ColorInt public int getBackgroundColor();
+ method @Deprecated @ColorInt public int getBackgroundColor();
method @Deprecated public boolean getDetachWallpaper();
method public long getDuration();
method public boolean getFillAfter();
@@ -52696,7 +52697,7 @@ package android.view.animation {
method public void restrictDuration(long);
method public void scaleCurrentDuration(float);
method public void setAnimationListener(android.view.animation.Animation.AnimationListener);
- method public void setBackgroundColor(@ColorInt int);
+ method @Deprecated public void setBackgroundColor(@ColorInt int);
method @Deprecated public void setDetachWallpaper(boolean);
method public void setDuration(long);
method public void setFillAfter(boolean);
@@ -53040,6 +53041,7 @@ package android.view.contentcapture {
method @NonNull public android.view.autofill.AutofillId newAutofillId(@NonNull android.view.autofill.AutofillId, long);
method @NonNull public final android.view.ViewStructure newViewStructure(@NonNull android.view.View);
method @NonNull public final android.view.ViewStructure newVirtualViewStructure(@NonNull android.view.autofill.AutofillId, long);
+ method public final void notifySessionLifecycle(boolean);
method public final void notifyViewAppeared(@NonNull android.view.ViewStructure);
method public final void notifyViewDisappeared(@NonNull android.view.autofill.AutofillId);
method public final void notifyViewTextChanged(@NonNull android.view.autofill.AutofillId, @Nullable CharSequence);
diff --git a/api/system-current.txt b/api/system-current.txt
index 4c48238d655e..283433e519ba 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8242,6 +8242,7 @@ package android.telephony {
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.IccOpenLogicalChannelResponse iccOpenLogicalChannelBySlot(int, @Nullable String, int);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduBasicChannelBySlot(int, int, int, int, int, int, @Nullable String);
method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String iccTransmitApduLogicalChannelBySlot(int, int, int, int, int, int, int, @Nullable String);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
method public boolean isDataConnectivityPossible();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
@@ -8673,6 +8674,7 @@ package android.telephony.ims {
method public android.os.Bundle getCallExtras();
method public int getCallType();
method public static int getCallTypeFromVideoState(int);
+ method public int getCallerNumberVerificationStatus();
method public int getEmergencyCallRouting();
method public int getEmergencyServiceCategories();
method @NonNull public java.util.List<java.lang.String> getEmergencyUrns();
@@ -8690,6 +8692,7 @@ package android.telephony.ims {
method public void setCallExtraBoolean(String, boolean);
method public void setCallExtraInt(String, int);
method public void setCallRestrictCause(int);
+ method public void setCallerNumberVerificationStatus(int);
method public void setEmergencyCallRouting(int);
method public void setEmergencyCallTesting(boolean);
method public void setEmergencyServiceCategories(int);
@@ -8740,6 +8743,9 @@ package android.telephony.ims {
field public static final int SERVICE_TYPE_EMERGENCY = 2; // 0x2
field public static final int SERVICE_TYPE_NONE = 0; // 0x0
field public static final int SERVICE_TYPE_NORMAL = 1; // 0x1
+ field public static final int VERIFICATION_STATUS_FAILED = 2; // 0x2
+ field public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0; // 0x0
+ field public static final int VERIFICATION_STATUS_PASSED = 1; // 0x1
}
public class ImsCallSessionListener {
@@ -9514,7 +9520,9 @@ package android.telephony.ims.stub {
method public void acknowledgeSmsReport(int, int, int);
method public String getSmsFormat();
method public void onReady();
- method public final void onSendSmsResult(int, int, int, int) throws java.lang.RuntimeException;
+ method @Deprecated public final void onSendSmsResult(int, int, int, int) throws java.lang.RuntimeException;
+ method public final void onSendSmsResultError(int, int, int, int, int) throws java.lang.RuntimeException;
+ method public final void onSendSmsResultSuccess(int, int) throws java.lang.RuntimeException;
method public final void onSmsReceived(int, String, byte[]) throws java.lang.RuntimeException;
method @Deprecated public final void onSmsStatusReportReceived(int, int, String, byte[]) throws java.lang.RuntimeException;
method public final void onSmsStatusReportReceived(int, String, byte[]) throws java.lang.RuntimeException;
@@ -9523,6 +9531,7 @@ package android.telephony.ims.stub {
field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3
field public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; // 0x4
field public static final int DELIVER_STATUS_OK = 1; // 0x1
+ field public static final int RESULT_NO_NETWORK_ERROR = -1; // 0xffffffff
field public static final int SEND_STATUS_ERROR = 2; // 0x2
field public static final int SEND_STATUS_ERROR_FALLBACK = 4; // 0x4
field public static final int SEND_STATUS_ERROR_RETRY = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 6e28f67e9a68..34312f69116a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34,8 +34,8 @@ package android {
package android.accessibilityservice {
- public final class AccessibilityGestureInfo implements android.os.Parcelable {
- ctor public AccessibilityGestureInfo(int, int);
+ public final class AccessibilityGestureEvent implements android.os.Parcelable {
+ ctor public AccessibilityGestureEvent(int, int);
}
}
@@ -3335,6 +3335,11 @@ package android.view {
field @android.view.ViewDebug.ExportedProperty(flagMapping={@android.view.ViewDebug.FlagToString(mask=0x1, equals=0x1, name="FAKE_HARDWARE_ACCELERATED"), @android.view.ViewDebug.FlagToString(mask=0x2, equals=0x2, name="FORCE_HARDWARE_ACCELERATED"), @android.view.ViewDebug.FlagToString(mask=0x4, equals=0x4, name="WANTS_OFFSET_NOTIFICATIONS"), @android.view.ViewDebug.FlagToString(mask=0x10, equals=0x10, name="SHOW_FOR_ALL_USERS"), @android.view.ViewDebug.FlagToString(mask=android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION, equals=android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION, name="NO_MOVE_ANIMATION"), @android.view.ViewDebug.FlagToString(mask=0x80, equals=0x80, name="COMPATIBLE_WINDOW"), @android.view.ViewDebug.FlagToString(mask=0x100, equals=0x100, name="SYSTEM_ERROR"), @android.view.ViewDebug.FlagToString(mask=0x200, equals=0x200, name="INHERIT_TRANSLUCENT_DECOR"), @android.view.ViewDebug.FlagToString(mask=0x400, equals=0x400, name="KEYGUARD"), @android.view.ViewDebug.FlagToString(mask=0x800, equals=0x800, name="DISABLE_WALLPAPER_TOUCH_EVENTS"), @android.view.ViewDebug.FlagToString(mask=0x1000, equals=0x1000, name="FORCE_STATUS_BAR_VISIBLE_TRANSPARENT"), @android.view.ViewDebug.FlagToString(mask=0x2000, equals=0x2000, name="PRESERVE_GEOMETRY"), @android.view.ViewDebug.FlagToString(mask=0x4000, equals=0x4000, name="FORCE_DECOR_VIEW_VISIBILITY"), @android.view.ViewDebug.FlagToString(mask=0x8000, equals=0x8000, name="WILL_NOT_REPLACE_ON_RELAUNCH"), @android.view.ViewDebug.FlagToString(mask=0x10000, equals=0x10000, name="LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME"), @android.view.ViewDebug.FlagToString(mask=0x20000, equals=0x20000, name="FORCE_DRAW_STATUS_BAR_BACKGROUND"), @android.view.ViewDebug.FlagToString(mask=0x40000, equals=0x40000, name="SUSTAINED_PERFORMANCE_MODE"), @android.view.ViewDebug.FlagToString(mask=0x80000, equals=0x80000, name="HIDE_NON_SYSTEM_OVERLAY_WINDOWS"), @android.view.ViewDebug.FlagToString(mask=0x100000, equals=0x100000, name="IS_ROUNDED_CORNERS_OVERLAY"), @android.view.ViewDebug.FlagToString(mask=0x400000, equals=0x400000, name="IS_SCREEN_DECOR"), @android.view.ViewDebug.FlagToString(mask=0x800000, equals=0x800000, name="STATUS_FORCE_SHOW_NAVIGATION"), @android.view.ViewDebug.FlagToString(mask=0x1000000, equals=0x1000000, name="COLOR_SPACE_AGNOSTIC")}) public int privateFlags;
}
+ public class WindowlessViewRoot {
+ ctor public WindowlessViewRoot(android.content.Context, android.view.Display, android.view.SurfaceControl);
+ method public void addView(android.view.View, android.view.WindowManager.LayoutParams);
+ }
+
}
package android.view.accessibility {
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 1185127ab805..84a06070e431 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -116,28 +116,13 @@ void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* ou
}
bool isAttributionUidField(const FieldValue& value) {
- int field = value.mField.getField() & 0xff007f;
- if (field == 0x10001 && value.mValue.getType() == INT) {
- return true;
- }
- return false;
+ return isAttributionUidField(value.mField, value.mValue);
}
int32_t getUidIfExists(const FieldValue& value) {
- bool isUid = false;
// the field is uid field if the field is the uid field in attribution node or marked as
// is_uid in atoms.proto
- if (isAttributionUidField(value)) {
- isUid = true;
- } else {
- auto it = android::util::AtomsInfo::kAtomsWithUidField.find(value.mField.getTag());
- if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second; // uidField is the field number in proto
- isUid = value.mField.getDepth() == 0 && value.mField.getPosAtDepth(0) == uidField &&
- value.mValue.getType() == INT;
- }
- }
-
+ bool isUid = isAttributionUidField(value) || isUidField(value.mField, value.mValue);
return isUid ? value.mValue.int_value : -1;
}
@@ -153,7 +138,7 @@ bool isUidField(const Field& field, const Value& value) {
auto it = android::util::AtomsInfo::kAtomsWithUidField.find(field.getTag());
if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
- int uidField = it->second;
+ int uidField = it->second; // uidField is the field number in proto
return field.getDepth() == 0 && field.getPosAtDepth(0) == uidField &&
value.getType() == INT;
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b50ec8a84b40..78a6609e5e0d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -335,6 +335,8 @@ message Atom {
UpdateEngineUpdateAttemptReported update_engine_update_attempt_reported = 225;
UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226;
CameraActionEvent camera_action_event = 227;
+ AppCompatibilityChangeReported app_compatibility_change_reported =
+ 228 [(allow_from_any_uid) = true];
}
// Pulled events will start at field 10000.
@@ -2618,12 +2620,14 @@ message SettingsUIChanged {
message TouchEventReported {
/**
* The fields latency_{min|max|mean|stdev} represent minimum, maximum, mean,
- * and the standard deviation of latency between the kernel and framework
- * for touchscreen events. The units are microseconds.
+ * and the standard deviation of the time spent processing touchscreen events
+ * in the kernel and inputflinger. The units are microseconds.
*
- * The number is measured as the difference between the time at which
- * the input event was received in the evdev driver,
- * and the time at which the input event was received in EventHub.
+ * On supported devices, the starting point is taken during the hard interrupt inside the
+ * kernel touch driver. On all other devices, the starting point is taken inside
+ * the kernel's input event subsystem upon receipt of the input event.
+ * The ending point is taken inside InputDispatcher, just after the input event
+ * is sent to the app.
*/
// Minimum value
optional float latency_min_micros = 1;
@@ -7170,7 +7174,6 @@ message FrameTimingHistogram {
repeated int64 frame_counts = 2;
}
-
/**
* Information about camera facing and API level usage.
* Logged from:
@@ -7195,3 +7198,39 @@ message CameraActionEvent {
}
optional Facing facing = 4;
}
+
+/**
+ * Logs when a compatibility change is affecting an app.
+ *
+ * Logged from:
+ * frameworks/base/core/java/android/app/AppCompatCallbacks.java and
+ * frameworks/base/services/core/java/com/android/server/compat/PlatformCompat.java
+ */
+message AppCompatibilityChangeReported {
+ // The UID of the app being affected by the compatibilty change.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The ID of the change affecting the app.
+ optional int64 change_id = 2;
+
+ enum State {
+ UNKNOWN_STATE = 0;
+ ENABLED = 1;
+ DISABLED = 2;
+ LOGGED = 3;
+ }
+
+ // The state of the change - if logged from gating whether it was enabled or disabled, or just
+ // logged otherwise.
+ optional State state = 3;
+
+ enum Source {
+ UNKNOWN_SOURCE = 0;
+ APP_PROCESS = 1;
+ SYSTEM_SERVER = 2;
+ }
+
+ // Where it was logged from.
+ optional Source source = 4;
+
+}
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 10ac4a182f87..476fae37899d 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -358,9 +358,10 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
const LogEvent& event) {
- if (simpleMatcher.field_value_matcher_size() <= 0) {
- return event.GetTagId() == simpleMatcher.atom_id();
+ if (event.GetTagId() != simpleMatcher.atom_id()) {
+ return false;
}
+
for (const auto& matcher : simpleMatcher.field_value_matcher()) {
if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
return false;
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 70f0f6f75a59..441d3c896467 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -330,6 +330,7 @@ TEST(AtomMatcherTest, TestUidFieldMatcher) {
EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
// Tag found in kAtomsWithUidField and has matching uid
+ simpleMatcher->set_atom_id(TAG_ID_2);
EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
// Tag found in kAtomsWithUidField but has non-matching uid
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index b4913c87968d..a6e1f0ab0cde 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1180,4 +1180,283 @@ Lcom/android/server/net/BaseNetworkObserver;-><init>()V
Lcom/android/server/ResettableTimeout$T;-><init>(Lcom/android/server/ResettableTimeout;)V
Lcom/google/android/gles_jni/EGLImpl;-><init>()V
Lcom/google/android/gles_jni/GLImpl;-><init>()V
+Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
+Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedAudioType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedImageType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isSupportedVideoType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
+Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
+Lcom/google/android/mms/InvalidHeaderValueException;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/MmsException;-><init>()V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
+Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;->setReportAllowed(I)V
+Lcom/google/android/mms/pdu/AcknowledgeInd;->setTransactionId([B)V
+Lcom/google/android/mms/pdu/Base64;->decodeBase64([B)[B
+Lcom/google/android/mms/pdu/CharacterSets;->getMibEnumValue(Ljava/lang/String;)I
+Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
+Lcom/google/android/mms/pdu/DeliveryInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/DeliveryInd;->getDate()J
+Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/DeliveryInd;->getStatus()I
+Lcom/google/android/mms/pdu/DeliveryInd;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->appendTextString([B)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
+Lcom/google/android/mms/pdu/EncodedStringValue;->copy(Lcom/google/android/mms/pdu/EncodedStringValue;)Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->extract(Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/EncodedStringValue;->getCharacterSet()I
+Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
+Lcom/google/android/mms/pdu/EncodedStringValue;->getTextString()[B
+Lcom/google/android/mms/pdu/EncodedStringValue;->setCharacterSet(I)V
+Lcom/google/android/mms/pdu/EncodedStringValue;->setTextString([B)V
+Lcom/google/android/mms/pdu/GenericPdu;-><init>()V
+Lcom/google/android/mms/pdu/GenericPdu;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
+Lcom/google/android/mms/pdu/GenericPdu;->getPduHeaders()Lcom/google/android/mms/pdu/PduHeaders;
+Lcom/google/android/mms/pdu/GenericPdu;->mPduHeaders:Lcom/google/android/mms/pdu/PduHeaders;
+Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/GenericPdu;->setMessageType(I)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>()V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->addTo(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
+Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;-><init>()V
+Lcom/google/android/mms/pdu/NotificationInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/NotificationInd;->getContentClass()I
+Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getDeliveryReport()I
+Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
+Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
+Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
+Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
+Lcom/google/android/mms/pdu/NotificationInd;->setContentClass(I)V
+Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/NotificationInd;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/NotificationInd;->setExpiry(J)V
+Lcom/google/android/mms/pdu/NotificationInd;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/NotificationInd;->setMessageSize(J)V
+Lcom/google/android/mms/pdu/NotificationInd;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/NotificationInd;->setTransactionId([B)V
+Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
+Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/NotifyRespInd;->setReportAllowed(I)V
+Lcom/google/android/mms/pdu/NotifyRespInd;->setStatus(I)V
+Lcom/google/android/mms/pdu/NotifyRespInd;->setTransactionId([B)V
+Lcom/google/android/mms/pdu/PduBody;-><init>()V
+Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
+Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
+Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduBody;->getPartIndex(Lcom/google/android/mms/pdu/PduPart;)I
+Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
+Lcom/google/android/mms/pdu/PduBody;->removePart(I)Lcom/google/android/mms/pdu/PduPart;
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->copy()V
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->mark()Lcom/google/android/mms/pdu/PduComposer$PositionMarker;
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->newbuf()V
+Lcom/google/android/mms/pdu/PduComposer$BufferStack;->pop()V
+Lcom/google/android/mms/pdu/PduComposer$PositionMarker;->getLength()I
+Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendEncodedString(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendHeader(I)I
+Lcom/google/android/mms/pdu/PduComposer;->appendLongInteger(J)V
+Lcom/google/android/mms/pdu/PduComposer;->appendOctet(I)V
+Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString([B)V
+Lcom/google/android/mms/pdu/PduComposer;->appendShortInteger(I)V
+Lcom/google/android/mms/pdu/PduComposer;->appendTextString(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/PduComposer;->appendTextString([B)V
+Lcom/google/android/mms/pdu/PduComposer;->appendUintvarInteger(J)V
+Lcom/google/android/mms/pdu/PduComposer;->appendValueLength(J)V
+Lcom/google/android/mms/pdu/PduComposer;->arraycopy([BII)V
+Lcom/google/android/mms/pdu/PduComposer;->make()[B
+Lcom/google/android/mms/pdu/PduComposer;->mContentTypeMap:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduComposer;->mMessage:Ljava/io/ByteArrayOutputStream;
+Lcom/google/android/mms/pdu/PduComposer;->mPdu:Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduComposer;->mPduHeader:Lcom/google/android/mms/pdu/PduHeaders;
+Lcom/google/android/mms/pdu/PduComposer;->mPosition:I
+Lcom/google/android/mms/pdu/PduComposer;->mResolver:Landroid/content/ContentResolver;
+Lcom/google/android/mms/pdu/PduComposer;->mStack:Lcom/google/android/mms/pdu/PduComposer$BufferStack;
+Lcom/google/android/mms/pdu/PduContentTypes;->contentTypes:[Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduHeaders;-><init>()V
+Lcom/google/android/mms/pdu/PduHeaders;->appendEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
+Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValue(I)Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValues(I)[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/PduHeaders;->getLongInteger(I)J
+Lcom/google/android/mms/pdu/PduHeaders;->getOctet(I)I
+Lcom/google/android/mms/pdu/PduHeaders;->getTextString(I)[B
+Lcom/google/android/mms/pdu/PduHeaders;->setEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
+Lcom/google/android/mms/pdu/PduHeaders;->setLongInteger(JI)V
+Lcom/google/android/mms/pdu/PduHeaders;->setOctet(II)V
+Lcom/google/android/mms/pdu/PduParser;-><init>([BZ)V
+Lcom/google/android/mms/pdu/PduParser;->checkPartPosition(Lcom/google/android/mms/pdu/PduPart;)I
+Lcom/google/android/mms/pdu/PduParser;->log(Ljava/lang/String;)V
+Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduParser;->parseContentType(Ljava/io/ByteArrayInputStream;Ljava/util/HashMap;)[B
+Lcom/google/android/mms/pdu/PduParser;->parsePartHeaders(Ljava/io/ByteArrayInputStream;Lcom/google/android/mms/pdu/PduPart;I)Z
+Lcom/google/android/mms/pdu/PduParser;->parseShortInteger(Ljava/io/ByteArrayInputStream;)I
+Lcom/google/android/mms/pdu/PduParser;->parseUnsignedInt(Ljava/io/ByteArrayInputStream;)I
+Lcom/google/android/mms/pdu/PduParser;->parseValueLength(Ljava/io/ByteArrayInputStream;)I
+Lcom/google/android/mms/pdu/PduParser;->parseWapString(Ljava/io/ByteArrayInputStream;I)[B
+Lcom/google/android/mms/pdu/PduPart;-><init>()V
+Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPart;->getCharset()I
+Lcom/google/android/mms/pdu/PduPart;->getContentDisposition()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentId()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentTransferEncoding()[B
+Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
+Lcom/google/android/mms/pdu/PduPart;->getData()[B
+Lcom/google/android/mms/pdu/PduPart;->getDataLength()I
+Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
+Lcom/google/android/mms/pdu/PduPart;->getName()[B
+Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
+Lcom/google/android/mms/pdu/PduPart;->setContentDisposition([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentTransferEncoding([B)V
+Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
+Lcom/google/android/mms/pdu/PduPart;->setData([B)V
+Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
+Lcom/google/android/mms/pdu/PduPart;->setFilename([B)V
+Lcom/google/android/mms/pdu/PduPart;->setName([B)V
+Lcom/google/android/mms/pdu/PduPersister;->ADDRESS_FIELDS:[I
+Lcom/google/android/mms/pdu/PduPersister;->CHARSET_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->ENCODED_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->getByteArrayFromPartColumn(Landroid/database/Cursor;I)[B
+Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
+Lcom/google/android/mms/pdu/PduPersister;->getIntegerFromPartColumn(Landroid/database/Cursor;I)Ljava/lang/Integer;
+Lcom/google/android/mms/pdu/PduPersister;->getPartContentType(Lcom/google/android/mms/pdu/PduPart;)Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
+Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
+Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/pdu/PduPersister;->loadRecipients(ILjava/util/HashSet;Ljava/util/HashMap;Z)V
+Lcom/google/android/mms/pdu/PduPersister;->LONG_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->mContentResolver:Landroid/content/ContentResolver;
+Lcom/google/android/mms/pdu/PduPersister;->mContext:Landroid/content/Context;
+Lcom/google/android/mms/pdu/PduPersister;->MESSAGE_BOX_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->mTelephonyManager:Landroid/telephony/TelephonyManager;
+Lcom/google/android/mms/pdu/PduPersister;->OCTET_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->PART_PROJECTION:[Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->PDU_CACHE_INSTANCE:Lcom/google/android/mms/util/PduCache;
+Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->persistAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
+Lcom/google/android/mms/pdu/PduPersister;->TEXT_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
+Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
+Lcom/google/android/mms/pdu/PduPersister;->updateAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
+Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
+Lcom/google/android/mms/pdu/QuotedPrintable;->decodeQuotedPrintable([B)[B
+Lcom/google/android/mms/pdu/ReadOrigInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/ReadOrigInd;->getReadStatus()I
+Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/ReadRecInd;->getMessageId()[B
+Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
+Lcom/google/android/mms/pdu/RetrieveConf;-><init>()V
+Lcom/google/android/mms/pdu/RetrieveConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getContentType()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getDeliveryReport()I
+Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getMessageClass()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->getReadReport()I
+Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveStatus()I
+Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveText()Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/RetrieveConf;->setContentType([B)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setMessageId([B)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setReadReport(I)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveStatus(I)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveText(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/RetrieveConf;->setTransactionId([B)V
+Lcom/google/android/mms/pdu/SendConf;-><init>()V
+Lcom/google/android/mms/pdu/SendConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
+Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
+Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
+Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;-><init>()V
+Lcom/google/android/mms/pdu/SendReq;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
+Lcom/google/android/mms/pdu/SendReq;->addBcc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/SendReq;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
+Lcom/google/android/mms/pdu/SendReq;->getContentType()[B
+Lcom/google/android/mms/pdu/SendReq;->getDeliveryReport()I
+Lcom/google/android/mms/pdu/SendReq;->getExpiry()J
+Lcom/google/android/mms/pdu/SendReq;->getMessageClass()[B
+Lcom/google/android/mms/pdu/SendReq;->getMessageSize()J
+Lcom/google/android/mms/pdu/SendReq;->getReadReport()I
+Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
+Lcom/google/android/mms/pdu/SendReq;->setBcc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->setCc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->setContentType([B)V
+Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
+Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
+Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
+Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
+Lcom/google/android/mms/pdu/SendReq;->setTransactionId([B)V
+Lcom/google/android/mms/util/AbstractCache;-><init>()V
+Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/AbstractCache;->purge(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/AbstractCache;->purgeAll()V
+Lcom/google/android/mms/util/AbstractCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Z
+Lcom/google/android/mms/util/DownloadDrmHelper;->isDrmConvertNeeded(Ljava/lang/String;)Z
+Lcom/google/android/mms/util/DownloadDrmHelper;->modifyDrmFwLockFileExtension(Ljava/lang/String;)Ljava/lang/String;
+Lcom/google/android/mms/util/DrmConvertSession;->close(Ljava/lang/String;)I
+Lcom/google/android/mms/util/DrmConvertSession;->convert([BI)[B
+Lcom/google/android/mms/util/DrmConvertSession;->open(Landroid/content/Context;Ljava/lang/String;)Lcom/google/android/mms/util/DrmConvertSession;
+Lcom/google/android/mms/util/PduCache;-><init>()V
+Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
+Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
+Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
+Lcom/google/android/mms/util/PduCache;->purge(Ljava/lang/Object;)Ljava/lang/Object;
+Lcom/google/android/mms/util/PduCache;->purgeAll()V
+Lcom/google/android/mms/util/PduCacheEntry;-><init>(Lcom/google/android/mms/pdu/GenericPdu;IJ)V
+Lcom/google/android/mms/util/PduCacheEntry;->getMessageBox()I
+Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
+Lcom/google/android/mms/util/PduCacheEntry;->getThreadId()J
+Lcom/google/android/mms/util/SqliteWrapper;->checkSQLiteException(Landroid/content/Context;Landroid/database/sqlite/SQLiteException;)V
+Lcom/google/android/mms/util/SqliteWrapper;->delete(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I
+Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
+Lcom/google/android/mms/util/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
+Lcom/google/android/mms/util/SqliteWrapper;->requery(Landroid/content/Context;Landroid/database/Cursor;)Z
+Lcom/google/android/mms/util/SqliteWrapper;->update(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureInfo.aidl b/core/java/android/accessibilityservice/AccessibilityGestureEvent.aidl
index 2539051f4069..58a9b64ee9c8 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureInfo.aidl
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.aidl
@@ -16,4 +16,4 @@
package android.accessibilityservice;
-parcelable AccessibilityGestureInfo;
+parcelable AccessibilityGestureEvent;
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureInfo.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 28c1dea1dbe7..b5401755a3c8 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -44,17 +44,17 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
- * This class describes the gesture information including gesture id and which display it happens
+ * This class describes the gesture event including gesture id and which display it happens
* on.
* <p>
* <strong>Note:</strong> Accessibility services setting the
* {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE}
* flag can receive gestures.
*
- * @see AccessibilityService#onGesture(AccessibilityGestureInfo)
+ * @see AccessibilityService#onGesture(AccessibilityGestureEvent)
*/
-public final class AccessibilityGestureInfo implements Parcelable {
+public final class AccessibilityGestureEvent implements Parcelable {
/** @hide */
@IntDef(prefix = { "GESTURE_" }, value = {
@@ -84,12 +84,12 @@ public final class AccessibilityGestureInfo implements Parcelable {
/** @hide */
@TestApi
- public AccessibilityGestureInfo(int gestureId, int displayId) {
+ public AccessibilityGestureEvent(int gestureId, int displayId) {
mGestureId = gestureId;
mDisplayId = displayId;
}
- private AccessibilityGestureInfo(@NonNull Parcel parcel) {
+ private AccessibilityGestureEvent(@NonNull Parcel parcel) {
mGestureId = parcel.readInt();
mDisplayId = parcel.readInt();
}
@@ -117,7 +117,7 @@ public final class AccessibilityGestureInfo implements Parcelable {
@NonNull
@Override
public String toString() {
- StringBuilder stringBuilder = new StringBuilder("AccessibilityGestureInfo[");
+ StringBuilder stringBuilder = new StringBuilder("AccessibilityGestureEvent[");
stringBuilder.append("gestureId: ").append(mGestureId);
stringBuilder.append(", ");
stringBuilder.append("displayId: ").append(mDisplayId);
@@ -142,14 +142,14 @@ public final class AccessibilityGestureInfo implements Parcelable {
/**
* @see Parcelable.Creator
*/
- public static final @NonNull Parcelable.Creator<AccessibilityGestureInfo> CREATOR =
- new Parcelable.Creator<AccessibilityGestureInfo>() {
- public AccessibilityGestureInfo createFromParcel(Parcel parcel) {
- return new AccessibilityGestureInfo(parcel);
+ public static final @NonNull Parcelable.Creator<AccessibilityGestureEvent> CREATOR =
+ new Parcelable.Creator<AccessibilityGestureEvent>() {
+ public AccessibilityGestureEvent createFromParcel(Parcel parcel) {
+ return new AccessibilityGestureEvent(parcel);
}
- public AccessibilityGestureInfo[] newArray(int size) {
- return new AccessibilityGestureInfo[size];
+ public AccessibilityGestureEvent[] newArray(int size) {
+ return new AccessibilityGestureEvent[size];
}
};
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index a8daf91eadc0..d3c274f4bdfe 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -382,7 +382,7 @@ public abstract class AccessibilityService extends Service {
void onServiceConnected();
void init(int connectionId, IBinder windowToken);
/** The detected gesture information for different displays */
- boolean onGesture(AccessibilityGestureInfo gestureInfo);
+ boolean onGesture(AccessibilityGestureEvent gestureInfo);
boolean onKeyEvent(KeyEvent event);
/** Magnification changed callbacks for different displays */
void onMagnificationChanged(int displayId, @NonNull Region region,
@@ -517,7 +517,7 @@ public abstract class AccessibilityService extends Service {
}
/**
- * Called by {@link #onGesture(AccessibilityGestureInfo)} when the user performs a specific
+ * Called by {@link #onGesture(AccessibilityGestureEvent)} when the user performs a specific
* gesture on the default display.
*
* <strong>Note:</strong> To receive gestures an accessibility service must
@@ -528,7 +528,7 @@ public abstract class AccessibilityService extends Service {
* @param gestureId The unique id of the performed gesture.
*
* @return Whether the gesture was handled.
- * @deprecated Override {@link #onGesture(AccessibilityGestureInfo)} instead.
+ * @deprecated Override {@link #onGesture(AccessibilityGestureEvent)} instead.
*
* @see #GESTURE_SWIPE_UP
* @see #GESTURE_SWIPE_UP_AND_LEFT
@@ -564,14 +564,14 @@ public abstract class AccessibilityService extends Service {
* <strong>Note:</strong> The default implementation calls {@link #onGesture(int)} when the
* touch screen is default display.
*
- * @param gestureInfo The information of gesture.
+ * @param gestureEvent The information of gesture.
*
* @return Whether the gesture was handled.
*
*/
- public boolean onGesture(@NonNull AccessibilityGestureInfo gestureInfo) {
- if (gestureInfo.getDisplayId() == Display.DEFAULT_DISPLAY) {
- onGesture(gestureInfo.getGestureId());
+ public boolean onGesture(@NonNull AccessibilityGestureEvent gestureEvent) {
+ if (gestureEvent.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ onGesture(gestureEvent.getGestureId());
}
return false;
}
@@ -1725,8 +1725,8 @@ public abstract class AccessibilityService extends Service {
}
@Override
- public boolean onGesture(AccessibilityGestureInfo gestureInfo) {
- return AccessibilityService.this.onGesture(gestureInfo);
+ public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
+ return AccessibilityService.this.onGesture(gestureEvent);
}
@Override
@@ -1826,7 +1826,7 @@ public abstract class AccessibilityService extends Service {
}
@Override
- public void onGesture(AccessibilityGestureInfo gestureInfo) {
+ public void onGesture(AccessibilityGestureEvent gestureInfo) {
Message message = mCaller.obtainMessageO(DO_ON_GESTURE, gestureInfo);
mCaller.sendMessage(message);
}
@@ -1942,7 +1942,7 @@ public abstract class AccessibilityService extends Service {
case DO_ON_GESTURE: {
if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
- mCallback.onGesture((AccessibilityGestureInfo) message.obj);
+ mCallback.onGesture((AccessibilityGestureEvent) message.obj);
}
} return;
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index e0d5e4438f23..8ec3aea2728f 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -20,7 +20,7 @@ import android.accessibilityservice.IAccessibilityServiceConnection;
import android.graphics.Region;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityWindowInfo;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.view.KeyEvent;
/**
@@ -36,7 +36,7 @@ import android.view.KeyEvent;
void onInterrupt();
- void onGesture(in AccessibilityGestureInfo gestureInfo);
+ void onGesture(in AccessibilityGestureEvent gestureEvent);
void clearAccessibilityCache();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a6784780d72f..1cc849936fde 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3983,24 +3983,14 @@ public final class ActivityThread extends ClientTransactionHandler {
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
- java.lang.ClassLoader cl = packageInfo.getClassLoader();
- service = packageInfo.getAppFactory()
- .instantiateService(cl, data.info.name, data.intent);
- } catch (Exception e) {
- if (!mInstrumentation.onException(service, e)) {
- throw new RuntimeException(
- "Unable to instantiate service " + data.info.name
- + ": " + e.toString(), e);
- }
- }
-
- try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
- context.setOuterContext(service);
-
Application app = packageInfo.makeApplication(false, mInstrumentation);
+ java.lang.ClassLoader cl = packageInfo.getClassLoader();
+ service = packageInfo.getAppFactory()
+ .instantiateService(cl, data.info.name, data.intent);
+ context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java
index 17697dba9ccd..08c97eb284e3 100644
--- a/core/java/android/app/AppCompatCallbacks.java
+++ b/core/java/android/app/AppCompatCallbacks.java
@@ -19,6 +19,9 @@ package android.app;
import android.compat.Compatibility;
import android.os.Process;
import android.util.Log;
+import android.util.StatsLog;
+
+import com.android.internal.compat.ChangeReporter;
import java.util.Arrays;
@@ -28,10 +31,10 @@ import java.util.Arrays;
* @hide
*/
public final class AppCompatCallbacks extends Compatibility.Callbacks {
-
private static final String TAG = "Compatibility";
private final long[] mDisabledChanges;
+ private final ChangeReporter mChangeReporter;
/**
* Install this class into the current process.
@@ -45,20 +48,29 @@ public final class AppCompatCallbacks extends Compatibility.Callbacks {
private AppCompatCallbacks(long[] disabledChanges) {
mDisabledChanges = Arrays.copyOf(disabledChanges, disabledChanges.length);
Arrays.sort(mDisabledChanges);
+ mChangeReporter = new ChangeReporter();
}
protected void reportChange(long changeId) {
- Log.d(TAG, "Compat change reported: " + changeId + "; UID " + Process.myUid());
- // TODO log via StatsLog
+ reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
}
protected boolean isChangeEnabled(long changeId) {
if (Arrays.binarySearch(mDisabledChanges, changeId) < 0) {
// Not present in the disabled array
- reportChange(changeId);
+ reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
return true;
}
+ reportChange(changeId, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
return false;
}
+ private void reportChange(long changeId, int state) {
+ int uid = Process.myUid();
+ //TODO(b/138374585): Implement rate limiting for the logs.
+ Log.d(TAG, ChangeReporter.createLogString(uid, changeId, state));
+ mChangeReporter.reportChange(uid, changeId,
+ state, /* source */StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__APP_PROCESS);
+ }
+
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 9de42c3b57d5..563174bb9534 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2671,7 +2671,7 @@ public class AppOpsManager {
* @return The proxy UID.
*/
public int getProxyUid() {
- return (int) findFirstNonNegativeForFlagsInStates(mDurations,
+ return (int) findFirstNonNegativeForFlagsInStates(mProxyUids,
MAX_PRIORITY_UID_STATE, MIN_PRIORITY_UID_STATE, OP_FLAGS_ALL);
}
@@ -2693,7 +2693,7 @@ public class AppOpsManager {
* @return The proxy UID.
*/
public int getProxyUid(@UidState int uidState, @OpFlags int flags) {
- return (int) findFirstNonNegativeForFlagsInStates(mDurations,
+ return (int) findFirstNonNegativeForFlagsInStates(mProxyUids,
uidState, uidState, flags);
}
@@ -4254,8 +4254,8 @@ public class AppOpsManager {
* end UID states.
*
* @param counts The data array.
- * @param beginUidState The beginning UID state (exclusive).
- * @param endUidState The end UID state.
+ * @param beginUidState The beginning UID state (inclusive).
+ * @param endUidState The end UID state (inclusive).
* @param flags The UID flags.
* @return The sum.
*/
@@ -4284,13 +4284,13 @@ public class AppOpsManager {
* end UID states.
*
* @param counts The data array.
+ * @param beginUidState The beginning UID state (inclusive).
+ * @param endUidState The end UID state (inclusive).
* @param flags The UID flags.
- * @param beginUidState The beginning UID state (exclusive).
- * @param endUidState The end UID state.
* @return The non-negative value or -1.
*/
private static long findFirstNonNegativeForFlagsInStates(@Nullable LongSparseLongArray counts,
- @OpFlags int flags, @UidState int beginUidState, @UidState int endUidState) {
+ @UidState int beginUidState, @UidState int endUidState, @OpFlags int flags) {
if (counts == null) {
return -1;
}
@@ -4316,14 +4316,14 @@ public class AppOpsManager {
* end UID states.
*
* @param counts The data array.
+ * @param beginUidState The beginning UID state (inclusive).
+ * @param endUidState The end UID state (inclusive).
* @param flags The UID flags.
- * @param beginUidState The beginning UID state (exclusive).
- * @param endUidState The end UID state.
* @return The non-negative value or -1.
*/
private static @Nullable String findFirstNonNullForFlagsInStates(
- @Nullable LongSparseArray<String> counts, @OpFlags int flags,
- @UidState int beginUidState, @UidState int endUidState) {
+ @Nullable LongSparseArray<String> counts, @UidState int beginUidState,
+ @UidState int endUidState, @OpFlags int flags) {
if (counts == null) {
return null;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index bb4e99873f26..2f03ed484e96 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8150,7 +8150,9 @@ public class Notification implements Parcelable
Action action, StandardTemplateParams p) {
final boolean tombstone = (action.actionIntent == null);
container.setViewVisibility(buttonId, View.VISIBLE);
- container.setImageViewIcon(buttonId, action.getIcon());
+ if (buttonId != R.id.media_seamless) {
+ container.setImageViewIcon(buttonId, action.getIcon());
+ }
// If the action buttons should not be tinted, then just use the default
// notification color. Otherwise, just use the passed-in color.
@@ -8204,6 +8206,10 @@ public class Notification implements Parcelable
view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
}
}
+ bindMediaActionButton(view, R.id.media_seamless, new Action(
+ R.drawable.ic_media_seamless, mBuilder.mContext.getString(
+ com.android.internal.R.string.ext_media_seamless_action), null), p);
+ view.setViewVisibility(R.id.media_seamless, View.GONE);
handleImage(view);
// handle the content margin
int endMargin = R.dimen.notification_content_margin_end;
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index f9b96c50e0b8..fd93450c29f0 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -16,7 +16,7 @@
package android.app;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService.Callbacks;
import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -1246,7 +1246,7 @@ public final class UiAutomation {
}
@Override
- public boolean onGesture(AccessibilityGestureInfo gestureInfo) {
+ public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
/* do nothing */
return false;
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index c74daa8eadfc..3933e81c732a 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -519,9 +519,9 @@ public class LauncherApps {
* <li>The app is a system app.</li>
* <li>The app doesn't request any <a href="/guide/topics/permissions/overview">permissions</a>.
* </li>
- * <li>The <code>&lt;application&gt;</code> tag in the app's manifest doesn't contain any child
- * elements that represent
- * <a href="/guide/components/fundamentals#DeclaringComponents">app components</a>.</li>
+ * <li>The app doesn't have a <em>launcher activity</em> that is enabled by default. A launcher
+ * activity has an intent containing the <code>ACTION_MAIN</code> action and the
+ * <code>CATEGORY_LAUNCHER</code> category.</li>
* </ul>
*
* <p>Additionally, the system hides synthesized activities for some or all apps in the
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c561013e6768..8dfe00a8adf6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -39,7 +39,7 @@ import android.app.PackageInstallObserver;
import android.app.admin.DevicePolicyManager;
import android.app.usage.StorageStatsManager;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Disabled;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -3379,7 +3379,7 @@ public abstract class PackageManager {
* @hide
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ @Disabled
public static final long FILTER_APPLICATION_QUERY = 135549675L;
/** {@hide} */
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index de61d70a9260..24ee21360ed8 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.annotation.AppIdInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -321,25 +322,24 @@ public abstract class PackageManagerInternal {
Bundle verificationBundle, int userId);
/**
- * Grants access to the package metadata for an ephemeral application.
+ * Grants implicit access based on an interaction between two apps. This grants the target app
+ * access to the calling application's package metadata.
* <p>
- * When an ephemeral application explicitly tries to interact with a full
- * install application [via an activity, service or provider that has been
- * exposed using the {@code visibleToInstantApp} attribute], the normal
- * application must be able to see metadata about the connecting ephemeral
- * app. If the ephemeral application uses an implicit intent [ie action VIEW,
- * category BROWSABLE], it remains hidden from the launched activity.
+ * When an application explicitly tries to interact with another application [via an
+ * activity, service or provider that is either declared in the caller's
+ * manifest via the {@code <queries>} tag or has been exposed via the target apps manifest using
+ * the {@code visibleToInstantApp} attribute], the target application must be able to see
+ * metadata about the calling app. If the calling application uses an implicit intent [ie
+ * action VIEW, category BROWSABLE], it remains hidden from the launched app.
* <p>
- * If the {@code sourceUid} is not for an ephemeral app or {@code targetUid}
- * is not for a fully installed app, this method will be a no-op.
- *
* @param userId the user
* @param intent the intent that triggered the grant
- * @param targetAppId The app ID of the fully installed application
- * @param ephemeralAppId The app ID of the ephemeral application
+ * @param callingAppId The app ID of the calling application
+ * @param targetAppId The app ID of the target application
*/
- public abstract void grantEphemeralAccess(int userId, Intent intent,
- int targetAppId, int ephemeralAppId);
+ public abstract void grantImplicitAccess(
+ @UserIdInt int userId, Intent intent, @AppIdInt int callingAppId,
+ @AppIdInt int targetAppId);
public abstract boolean isInstantAppInstallerComponent(ComponentName component);
/**
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 271020db0ccd..bcb94ce2d2d5 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -79,6 +79,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
/**
* Wrapped {@link ParcelFileDescriptor}, if any. Used to avoid
* double-closing {@link #mFd}.
+ * mClosed is always true if mWrapped is non-null.
*/
private final ParcelFileDescriptor mWrapped;
@@ -1023,6 +1024,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
}
try {
if (!mClosed) {
+ // mWrapped was and is null.
closeWithStatus(Status.LEAKED, null);
}
} finally {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 30e59590022c..76e728a11e83 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -182,6 +182,12 @@ public class Process {
*/
public static final int NETWORK_STACK_UID = 1073;
+ /**
+ * Defines the UID/GID for fs-verity certificate ownership in keystore.
+ * @hide
+ */
+ public static final int FSVERITY_CERT_UID = 1075;
+
/** {@hide} */
public static final int NOBODY_UID = 9999;
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index cdae72ebd313..b6af82948da3 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -189,6 +189,8 @@ public class SystemProperties {
* Set the value for the given {@code key} to {@code val}.
*
* @throws IllegalArgumentException if the {@code val} exceeds 91 characters
+ * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
+ * SELinux. libc will log the underlying reason.
* @hide
*/
@UnsupportedAppUsage
diff --git a/core/java/android/service/carrier/ApnService.java b/core/java/android/service/carrier/ApnService.java
index 57e4b1b40748..0c12fd409078 100644
--- a/core/java/android/service/carrier/ApnService.java
+++ b/core/java/android/service/carrier/ApnService.java
@@ -26,7 +26,7 @@ import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
-import com.android.internal.telephony.IApnSourceService;
+import android.service.carrier.IApnSourceService;
import java.util.List;
diff --git a/telephony/java/com/android/internal/telephony/IApnSourceService.aidl b/core/java/android/service/carrier/IApnSourceService.aidl
index 34c9067c3f2f..fadd2ff1a772 100644
--- a/telephony/java/com/android/internal/telephony/IApnSourceService.aidl
+++ b/core/java/android/service/carrier/IApnSourceService.aidl
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.internal.telephony;
+package android.service.carrier;
import android.content.ContentValues;
+/** @hide */
interface IApnSourceService {
/** Retreive APNs. */
ContentValues[] getApns(int subId);
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 78e30ab8cdc3..85f13d552fcf 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1522,6 +1522,7 @@ public abstract class NotificationListenerService extends Service {
private ArrayList<Notification.Action> mSmartActions;
private ArrayList<CharSequence> mSmartReplies;
private boolean mCanBubble;
+ private boolean mVisuallyInterruptive;
private static final int PARCEL_VERSION = 2;
@@ -1553,6 +1554,7 @@ public abstract class NotificationListenerService extends Service {
out.writeTypedList(mSmartActions, flags);
out.writeCharSequenceList(mSmartReplies);
out.writeBoolean(mCanBubble);
+ out.writeBoolean(mVisuallyInterruptive);
}
/** @hide */
@@ -1585,6 +1587,7 @@ public abstract class NotificationListenerService extends Service {
mSmartActions = in.createTypedArrayList(Notification.Action.CREATOR);
mSmartReplies = in.readCharSequenceList();
mCanBubble = in.readBoolean();
+ mVisuallyInterruptive = in.readBoolean();
}
@@ -1772,6 +1775,11 @@ public abstract class NotificationListenerService extends Service {
}
/** @hide */
+ public boolean visuallyInterruptive() {
+ return mVisuallyInterruptive;
+ }
+
+ /** @hide */
public boolean isNoisy() {
return mNoisy;
}
@@ -1787,7 +1795,8 @@ public abstract class NotificationListenerService extends Service {
ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
int userSentiment, boolean hidden, long lastAudiblyAlertedMs,
boolean noisy, ArrayList<Notification.Action> smartActions,
- ArrayList<CharSequence> smartReplies, boolean canBubble) {
+ ArrayList<CharSequence> smartReplies, boolean canBubble,
+ boolean visuallyInterruptive) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1808,6 +1817,7 @@ public abstract class NotificationListenerService extends Service {
mSmartActions = smartActions;
mSmartReplies = smartReplies;
mCanBubble = canBubble;
+ mVisuallyInterruptive = visuallyInterruptive;
}
/**
@@ -1832,7 +1842,8 @@ public abstract class NotificationListenerService extends Service {
other.mNoisy,
other.mSmartActions,
other.mSmartReplies,
- other.mCanBubble);
+ other.mCanBubble,
+ other.mVisuallyInterruptive);
}
/**
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 86651060a394..1c50d73c4953 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -276,6 +276,7 @@ public class TextLine {
final int runCount = mDirections.getRunCount();
for (int runIndex = 0; runIndex < runCount; runIndex++) {
final int runStart = mDirections.getRunStart(runIndex);
+ if (runStart > mLen) break;
final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
final boolean runIsRtl = mDirections.isRunRtl(runIndex);
@@ -360,6 +361,7 @@ public class TextLine {
float h = 0;
for (int runIndex = 0; runIndex < mDirections.getRunCount(); runIndex++) {
final int runStart = mDirections.getRunStart(runIndex);
+ if (runStart > mLen) break;
final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
final boolean runIsRtl = mDirections.isRunRtl(runIndex);
@@ -417,6 +419,7 @@ public class TextLine {
float h = 0;
for (int runIndex = 0; runIndex < mDirections.getRunCount(); runIndex++) {
final int runStart = mDirections.getRunStart(runIndex);
+ if (runStart > mLen) break;
final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
final boolean runIsRtl = mDirections.isRunRtl(runIndex);
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index d2dcb568ef6c..f1d152ba29af 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -16,6 +16,8 @@
package android.view;
+import android.graphics.Rect;
+
/**
* An interface to the PinnedStackController to update it of state changes, and to query
* information based on the current state.
@@ -30,15 +32,17 @@ interface IPinnedStackController {
oneway void setIsMinimized(boolean isMinimized);
/**
- * Notifies the controller of the current min edge size, this is needed to allow the system to
- * properly calculate the aspect ratio of the expanded PIP. The given {@param minEdgeSize} is
- * always bounded to be larger than the default minEdgeSize, so the caller can call this method
- * with 0 to reset to the default size.
+ * @return what WM considers to be the current device rotation.
*/
- oneway void setMinEdgeSize(int minEdgeSize);
+ int getDisplayRotation();
/**
- * @return what WM considers to be the current device rotation.
+ * Notifies the controller to actually start the PiP animation.
+ * The bounds would be calculated based on the last save reentry fraction internally.
+ * {@param destinationBounds} is the stack bounds of the final PiP window
+ * and {@param sourceRectHint} is the source bounds hint used when entering picture-in-picture,
+ * expect the same bound passed via IPinnedStackListener#onPrepareAnimation.
+ * {@param animationDuration} suggests the animation duration transitioning to PiP window.
*/
- int getDisplayRotation();
+ void startAnimation(in Rect destinationBounds, in Rect sourceRectHint, int animationDuration);
}
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index 2da353b1f0ee..806d81e39cca 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -16,8 +16,10 @@
package android.view;
+import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
+import android.view.DisplayInfo;
import android.view.IPinnedStackController;
/**
@@ -36,18 +38,13 @@ oneway interface IPinnedStackListener {
/**
* Called when the window manager has detected a change that would cause the movement bounds
* 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. 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.
- *
- * The {@param displayRotation} is provided so that the client can verify when making certain
- * calls that it will not provide stale information based on an old display rotation (ie. if
- * the WM has changed in the mean time but the client has not received onMovementBoundsChanged).
+ * the components that allow the listener to calculate the movement bounds itself.
+ * 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, in Rect animatingBounds,
- boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation);
+ void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment);
/**
* Called when window manager decides to adjust the pinned stack bounds because of the IME, or
@@ -76,4 +73,47 @@ oneway interface IPinnedStackListener {
* is first registered to allow the listener to synchronized its state with the controller.
*/
void onActionsChanged(in ParceledListSlice actions);
+
+ /**
+ * Called by the window manager to notify the listener to save the reentry fraction,
+ * typically when an Activity leaves PiP (picture-in-picture) mode to fullscreen.
+ * {@param componentName} represents the application component of PiP window
+ * while {@param bounds} is the current PiP bounds used to calculate the
+ * reentry snap fraction.
+ */
+ void onSaveReentrySnapFraction(in ComponentName componentName, in Rect bounds);
+
+ /**
+ * Called by the window manager to notify the listener to reset saved reentry fraction,
+ * typically when an Activity enters PiP (picture-in-picture) mode from fullscreen.
+ * {@param componentName} represents the application component of PiP window.
+ */
+ void onResetReentrySnapFraction(in ComponentName componentName);
+
+ /**
+ * Called when the window manager has detected change on DisplayInfo, or
+ * when the listener is first registered to allow the listener to synchronized its state with
+ * the controller.
+ */
+ void onDisplayInfoChanged(in DisplayInfo displayInfo);
+
+ /**
+ * Called by the window manager at the beginning of a configuration update cascade
+ * since the metrics from these resources are used for bounds calculations.
+ */
+ void onConfigurationChanged();
+
+ /**
+ * Called by the window manager when the aspect ratio is reset.
+ */
+ void onAspectRatioChanged(float aspectRatio);
+
+ /**
+ * Called by the window manager to notify the listener to prepare for PiP animation.
+ * Internally, the target bounds would be calculated from the given {@param aspectRatio}
+ * and {@param bounds}, the saved reentry snap fraction also contributes.
+ * Caller would wait for a IPinnedStackController#startAnimation callback to actually
+ * start the animation, see details in IPinnedStackController.
+ */
+ void onPrepareAnimation(in Rect sourceRectHint, float aspectRatio, in Rect bounds);
}
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
index 724d96ed9585..6eb90fc54286 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -54,6 +54,6 @@ oneway interface IRecentsAnimationRunner {
*/
@UnsupportedAppUsage
void onAnimationStart(in IRecentsAnimationController controller,
- in RemoteAnimationTarget[] apps, in Rect homeContentInsets,
- in Rect minimizedHomeBounds) = 2;
+ in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
+ in Rect homeContentInsets, in Rect minimizedHomeBounds) = 2;
}
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
index 73b023c99665..7b35aa2d2b45 100644
--- a/core/java/android/view/IRemoteAnimationRunner.aidl
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -34,7 +34,7 @@ oneway interface IRemoteAnimationRunner {
* @param finishedCallback The callback to invoke when the animation is finished.
*/
@UnsupportedAppUsage
- void onAnimationStart(in RemoteAnimationTarget[] apps,
+ void onAnimationStart(in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
in IRemoteAnimationFinishedCallback finishedCallback);
/**
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index b2f3f5edcd20..d54e9d58356b 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -30,6 +30,7 @@ import android.graphics.drawable.Drawable;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.RemoteViews;
import com.android.internal.R;
@@ -54,6 +55,7 @@ public class NotificationHeaderView extends ViewGroup {
private OnClickListener mExpandClickListener;
private OnClickListener mAppOpsListener;
private HeaderTouchListener mTouchListener = new HeaderTouchListener();
+ private LinearLayout mTransferChip;
private ImageView mExpandButton;
private CachingIconView mIcon;
private View mProfileBadge;
@@ -116,6 +118,7 @@ public class NotificationHeaderView extends ViewGroup {
mAppName = findViewById(com.android.internal.R.id.app_name_text);
mHeaderText = findViewById(com.android.internal.R.id.header_text);
mSecondaryHeaderText = findViewById(com.android.internal.R.id.header_text_secondary);
+ mTransferChip = findViewById(com.android.internal.R.id.media_seamless);
mExpandButton = findViewById(com.android.internal.R.id.expand_button);
mIcon = findViewById(com.android.internal.R.id.icon);
mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
@@ -148,9 +151,11 @@ public class NotificationHeaderView extends ViewGroup {
int childHeightSpec = getChildMeasureSpec(wrapContentHeightSpec,
lp.topMargin + lp.bottomMargin, lp.height);
child.measure(childWidthSpec, childHeightSpec);
+ // Icons that should go at the end
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|| child == mProfileBadge
- || child == mAppOps) {
+ || child == mAppOps
+ || child == mTransferChip) {
iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
} else {
totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth();
@@ -211,9 +216,11 @@ public class NotificationHeaderView extends ViewGroup {
int layoutRight;
int top = (int) (getPaddingTop() + (ownHeight - childHeight) / 2.0f);
int bottom = top + childHeight;
+ // Icons that should go at the end
if ((child == mExpandButton && mShowExpandButtonAtEnd)
|| child == mProfileBadge
- || child == mAppOps) {
+ || child == mAppOps
+ || child == mTransferChip) {
if (end == getMeasuredWidth()) {
layoutRight = end - mContentEndMargin;
} else {
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index 85457cb48218..a6536f5d83b7 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -30,6 +30,14 @@ import java.util.function.Consumer;
*/
public class SyncRtSurfaceTransactionApplier {
+ public static final int FLAG_ALL = 0xffffffff;
+ public static final int FLAG_ALPHA = 1;
+ public static final int FLAG_MATRIX = 1 << 1;
+ public static final int FLAG_WINDOW_CROP = 1 << 2;
+ public static final int FLAG_LAYER = 1 << 3;
+ public static final int FLAG_CORNER_RADIUS = 1 << 4;
+ public static final int FLAG_VISIBILITY = 1 << 5;
+
private final Surface mTargetSurface;
private final ViewRootImpl mTargetViewRootImpl;
private final float[] mTmpFloat9 = new float[9];
@@ -72,15 +80,27 @@ public class SyncRtSurfaceTransactionApplier {
}
public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
- t.setMatrix(params.surface, params.matrix, tmpFloat9);
- t.setWindowCrop(params.surface, params.windowCrop);
- t.setAlpha(params.surface, params.alpha);
- t.setLayer(params.surface, params.layer);
- t.setCornerRadius(params.surface, params.cornerRadius);
- if (params.visible) {
- t.show(params.surface);
- } else {
- t.hide(params.surface);
+ if ((params.flags & FLAG_MATRIX) != 0) {
+ t.setMatrix(params.surface, params.matrix, tmpFloat9);
+ }
+ if ((params.flags & FLAG_WINDOW_CROP) != 0) {
+ t.setWindowCrop(params.surface, params.windowCrop);
+ }
+ if ((params.flags & FLAG_ALPHA) != 0) {
+ t.setAlpha(params.surface, params.alpha);
+ }
+ if ((params.flags & FLAG_LAYER) != 0) {
+ t.setLayer(params.surface, params.layer);
+ }
+ if ((params.flags & FLAG_CORNER_RADIUS) != 0) {
+ t.setCornerRadius(params.surface, params.cornerRadius);
+ }
+ if ((params.flags & FLAG_VISIBILITY) != 0) {
+ if (params.visible) {
+ t.show(params.surface);
+ } else {
+ t.hide(params.surface);
+ }
}
}
@@ -115,6 +135,105 @@ public class SyncRtSurfaceTransactionApplier {
public static class SurfaceParams {
+ public static class Builder {
+ final SurfaceControl surface;
+ int flags;
+ float alpha;
+ float cornerRadius;
+ Matrix matrix;
+ Rect windowCrop;
+ int layer;
+ boolean visible;
+
+ /**
+ * @param surface The surface to modify.
+ */
+ public Builder(SurfaceControl surface) {
+ this.surface = surface;
+ }
+
+ /**
+ * @param alpha The alpha value to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withAlpha(float alpha) {
+ this.alpha = alpha;
+ flags |= FLAG_ALPHA;
+ return this;
+ }
+
+ /**
+ * @param matrix The matrix to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withMatrix(Matrix matrix) {
+ this.matrix = matrix;
+ flags |= FLAG_MATRIX;
+ return this;
+ }
+
+ /**
+ * @param windowCrop The window crop to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withWindowCrop(Rect windowCrop) {
+ this.windowCrop = windowCrop;
+ flags |= FLAG_WINDOW_CROP;
+ return this;
+ }
+
+ /**
+ * @param layer The layer to assign the surface.
+ * @return this Builder
+ */
+ public Builder withLayer(int layer) {
+ this.layer = layer;
+ flags |= FLAG_LAYER;
+ return this;
+ }
+
+ /**
+ * @param radius the Radius for rounded corners to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withCornerRadius(float radius) {
+ this.cornerRadius = radius;
+ flags |= FLAG_CORNER_RADIUS;
+ return this;
+ }
+
+ /**
+ * @param visible The visibility to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withVisibility(boolean visible) {
+ this.visible = visible;
+ flags |= FLAG_VISIBILITY;
+ return this;
+ }
+
+ /**
+ * @return a new SurfaceParams instance
+ */
+ public SurfaceParams build() {
+ return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer,
+ cornerRadius, visible);
+ }
+ }
+
+ private SurfaceParams(SurfaceControl surface, int params, float alpha, Matrix matrix,
+ Rect windowCrop, int layer, float cornerRadius, boolean visible) {
+ this.flags = params;
+ this.surface = surface;
+ this.alpha = alpha;
+ this.matrix = new Matrix(matrix);
+ this.windowCrop = new Rect(windowCrop);
+ this.layer = layer;
+ this.cornerRadius = cornerRadius;
+ this.visible = visible;
+ }
+
+
/**
* Constructs surface parameters to be applied when the current view state gets pushed to
* RenderThread.
@@ -123,18 +242,27 @@ public class SyncRtSurfaceTransactionApplier {
* @param alpha Alpha to apply.
* @param matrix Matrix to apply.
* @param windowCrop Crop to apply.
+ * @param layer The layer to apply.
+ * @param cornerRadius The corner radius to apply.
+ * @param visible The visibility to apply.
+ *
+ * @deprecated Use {@link SurfaceParams.Builder} to create an instance.
*/
+ @Deprecated
public SurfaceParams(SurfaceControl surface, float alpha, Matrix matrix,
Rect windowCrop, int layer, float cornerRadius, boolean visible) {
+ this.flags = FLAG_ALL;
this.surface = surface;
this.alpha = alpha;
this.matrix = new Matrix(matrix);
- this.windowCrop = new Rect(windowCrop);
+ this.windowCrop = windowCrop != null ? new Rect(windowCrop) : null;
this.layer = layer;
this.cornerRadius = cornerRadius;
this.visible = visible;
}
+ private final int flags;
+
@VisibleForTesting
public final SurfaceControl surface;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 85aba3c3f535..3756a7de47c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1997,7 +1997,6 @@ public final class ViewRootImpl implements ViewParent,
mIsInTraversal = true;
mWillDrawSoon = true;
boolean windowSizeMayChange = false;
- final boolean windowAttributesChanged = mWindowAttributesChanged;
WindowManager.LayoutParams lp = mWindowAttributes;
int desiredWindowWidth;
@@ -2014,10 +2013,6 @@ public final class ViewRootImpl implements ViewParent,
((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
WindowManager.LayoutParams params = null;
- if (mWindowAttributesChanged) {
- mWindowAttributesChanged = false;
- params = lp;
- }
CompatibilityInfo compatibilityInfo =
mDisplay.getDisplayAdjustments().getCompatibilityInfo();
if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {
@@ -2194,16 +2189,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- if (params != null) {
- if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
- if (!PixelFormat.formatHasAlpha(params.format)) {
- params.format = PixelFormat.TRANSLUCENT;
- }
- }
- mAttachInfo.mOverscanRequested = (params.flags
- & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0;
- }
-
if (mApplyInsetsRequested) {
mApplyInsetsRequested = false;
mLastOverscanRequested = mAttachInfo.mOverscanRequested;
@@ -2259,6 +2244,21 @@ public final class ViewRootImpl implements ViewParent,
/* True if surface generation id changes. */
boolean surfaceReplaced = false;
+ final boolean windowAttributesChanged = mWindowAttributesChanged;
+ if (windowAttributesChanged) {
+ mWindowAttributesChanged = false;
+ params = lp;
+ }
+
+ if (params != null) {
+ if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0
+ && !PixelFormat.formatHasAlpha(params.format)) {
+ params.format = PixelFormat.TRANSLUCENT;
+ }
+ mAttachInfo.mOverscanRequested =
+ (params.flags & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0;
+ }
+
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
diff --git a/core/java/android/view/WindowlessViewRoot.java b/core/java/android/view/WindowlessViewRoot.java
index 75057a26afae..b76e1fad563e 100644
--- a/core/java/android/view/WindowlessViewRoot.java
+++ b/core/java/android/view/WindowlessViewRoot.java
@@ -21,12 +21,15 @@ import android.content.Context;
import android.view.SurfaceControl;
import android.view.View;
+import android.annotation.TestApi;
+
/**
* Utility class for adding a view hierarchy to a SurfaceControl.
*
* See WindowlessWmTest for example usage.
* @hide
*/
+@TestApi
public class WindowlessViewRoot {
ViewRootImpl mViewRoot;
WindowlessWindowManager mWm;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 59e7bd236907..430fb6dd9770 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -95,8 +95,9 @@ class WindowlessWindowManager implements IWindowSession {
public void remove(android.view.IWindow window) {}
private boolean isOpaque(WindowManager.LayoutParams attrs) {
- if (attrs.surfaceInsets.left != 0 || attrs.surfaceInsets.top != 0 ||
- attrs.surfaceInsets.right != 0 || attrs.surfaceInsets.bottom != 0) {
+ if (attrs.surfaceInsets != null && attrs.surfaceInsets.left != 0 ||
+ attrs.surfaceInsets.top != 0 || attrs.surfaceInsets.right != 0 ||
+ attrs.surfaceInsets.bottom != 0) {
return false;
}
return !PixelFormat.formatHasAlpha(attrs.format);
@@ -118,9 +119,15 @@ class WindowlessWindowManager implements IWindowSession {
"Invalid window token (never added or removed already)");
}
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.show(sc).setBufferSize(sc,
- requestedWidth + attrs.surfaceInsets.left + attrs.surfaceInsets.right,
- requestedHeight + attrs.surfaceInsets.top + attrs.surfaceInsets.bottom)
+
+ final Rect surfaceInsets = attrs.surfaceInsets;
+ int width = surfaceInsets != null ?
+ requestedWidth + surfaceInsets.left + surfaceInsets.right : requestedWidth;
+ int height = surfaceInsets != null ?
+ requestedHeight + surfaceInsets.top + surfaceInsets.bottom : requestedHeight;
+
+ t.show(sc)
+ .setBufferSize(sc, width, height)
.setOpaque(sc, isOpaque(attrs))
.apply();
outSurfaceControl.copyFrom(sc);
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index 484d9a1d173b..dc8bf9b5fbae 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -71,7 +71,9 @@ public class AccessibilityCache {
private boolean mIsAllWindowsCached;
- private final SparseArray<AccessibilityWindowInfo> mWindowCache =
+ // The SparseArray of all {@link AccessibilityWindowInfo}s on all displays.
+ // The key of outer SparseArray is display ID and the key of inner SparseArray is window ID.
+ private final SparseArray<SparseArray<AccessibilityWindowInfo>> mWindowCacheByDisplay =
new SparseArray<>();
private final SparseArray<LongSparseArray<AccessibilityNodeInfo>> mNodeCache =
@@ -84,34 +86,66 @@ public class AccessibilityCache {
mAccessibilityNodeRefresher = nodeRefresher;
}
- public void setWindows(List<AccessibilityWindowInfo> windows) {
+ /**
+ * Sets all {@link AccessibilityWindowInfo}s of all displays into the cache.
+ * The key of SparseArray is display ID.
+ *
+ * @param windowsOnAllDisplays The accessibility windows of all displays.
+ */
+ public void setWindowsOnAllDisplays(
+ SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays) {
synchronized (mLock) {
if (DEBUG) {
Log.i(LOG_TAG, "Set windows");
}
- clearWindowCache();
- if (windows == null) {
+ clearWindowCacheLocked();
+ if (windowsOnAllDisplays == null) {
return;
}
- final int windowCount = windows.size();
- for (int i = 0; i < windowCount; i++) {
- final AccessibilityWindowInfo window = windows.get(i);
- addWindow(window);
+
+ final int displayCounts = windowsOnAllDisplays.size();
+ for (int i = 0; i < displayCounts; i++) {
+ final List<AccessibilityWindowInfo> windowsOfDisplay =
+ windowsOnAllDisplays.valueAt(i);
+
+ if (windowsOfDisplay == null) {
+ continue;
+ }
+
+ final int displayId = windowsOnAllDisplays.keyAt(i);
+ final int windowCount = windowsOfDisplay.size();
+ for (int j = 0; j < windowCount; j++) {
+ addWindowByDisplayLocked(displayId, windowsOfDisplay.get(j));
+ }
}
mIsAllWindowsCached = true;
}
}
+ /**
+ * Sets an {@link AccessibilityWindowInfo} into the cache.
+ *
+ * @param window The accessibility window.
+ */
public void addWindow(AccessibilityWindowInfo window) {
synchronized (mLock) {
if (DEBUG) {
- Log.i(LOG_TAG, "Caching window: " + window.getId());
+ Log.i(LOG_TAG, "Caching window: " + window.getId() + " at display Id [ "
+ + window.getDisplayId() + " ]");
}
- final int windowId = window.getId();
- mWindowCache.put(windowId, new AccessibilityWindowInfo(window));
+ addWindowByDisplayLocked(window.getDisplayId(), window);
}
}
+ private void addWindowByDisplayLocked(int displayId, AccessibilityWindowInfo window) {
+ SparseArray<AccessibilityWindowInfo> windows = mWindowCacheByDisplay.get(displayId);
+ if (windows == null) {
+ windows = new SparseArray<>();
+ mWindowCacheByDisplay.put(displayId, windows);
+ }
+ final int windowId = window.getId();
+ windows.put(windowId, new AccessibilityWindowInfo(window));
+ }
/**
* Notifies the cache that the something in the UI changed. As a result
* the cache will either refresh some nodes or evict some nodes.
@@ -236,44 +270,82 @@ public class AccessibilityCache {
}
}
- public List<AccessibilityWindowInfo> getWindows() {
+ /**
+ * Gets all {@link AccessibilityWindowInfo}s of all displays from the cache.
+ *
+ * @return All cached {@link AccessibilityWindowInfo}s of all displays
+ * or null if such not found. The key of SparseArray is display ID.
+ */
+ public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
synchronized (mLock) {
if (!mIsAllWindowsCached) {
return null;
}
+ final SparseArray<List<AccessibilityWindowInfo>> returnWindows = new SparseArray<>();
+ final int displayCounts = mWindowCacheByDisplay.size();
- final int windowCount = mWindowCache.size();
- if (windowCount > 0) {
- // Careful to return the windows in a decreasing layer order.
- SparseArray<AccessibilityWindowInfo> sortedWindows = mTempWindowArray;
- sortedWindows.clear();
+ if (displayCounts > 0) {
+ for (int i = 0; i < displayCounts; i++) {
+ final int displayId = mWindowCacheByDisplay.keyAt(i);
+ final SparseArray<AccessibilityWindowInfo> windowsOfDisplay =
+ mWindowCacheByDisplay.valueAt(i);
- for (int i = 0; i < windowCount; i++) {
- AccessibilityWindowInfo window = mWindowCache.valueAt(i);
- sortedWindows.put(window.getLayer(), window);
- }
+ if (windowsOfDisplay == null) {
+ continue;
+ }
- // It's possible in transient conditions for two windows to share the same
- // layer, which results in sortedWindows being smaller than mWindowCache
- final int sortedWindowCount = sortedWindows.size();
- List<AccessibilityWindowInfo> windows = new ArrayList<>(sortedWindowCount);
- for (int i = sortedWindowCount - 1; i >= 0; i--) {
- AccessibilityWindowInfo window = sortedWindows.valueAt(i);
- windows.add(new AccessibilityWindowInfo(window));
- sortedWindows.removeAt(i);
- }
+ final int windowCount = windowsOfDisplay.size();
+ if (windowCount > 0) {
+ // Careful to return the windows in a decreasing layer order.
+ SparseArray<AccessibilityWindowInfo> sortedWindows = mTempWindowArray;
+ sortedWindows.clear();
- return windows;
+ for (int j = 0; j < windowCount; j++) {
+ AccessibilityWindowInfo window = windowsOfDisplay.valueAt(j);
+ sortedWindows.put(window.getLayer(), window);
+ }
+
+ // It's possible in transient conditions for two windows to share the same
+ // layer, which results in sortedWindows being smaller than
+ // mWindowCacheByDisplay
+ final int sortedWindowCount = sortedWindows.size();
+ List<AccessibilityWindowInfo> windows =
+ new ArrayList<>(sortedWindowCount);
+ for (int j = sortedWindowCount - 1; j >= 0; j--) {
+ AccessibilityWindowInfo window = sortedWindows.valueAt(j);
+ windows.add(new AccessibilityWindowInfo(window));
+ sortedWindows.removeAt(j);
+ }
+ returnWindows.put(displayId, windows);
+ }
+ }
+ return returnWindows;
}
return null;
}
}
+ /**
+ * Gets an {@link AccessibilityWindowInfo} by windowId.
+ *
+ * @param windowId The id of the window.
+ *
+ * @return The {@link AccessibilityWindowInfo} or null if such not found.
+ */
public AccessibilityWindowInfo getWindow(int windowId) {
synchronized (mLock) {
- AccessibilityWindowInfo window = mWindowCache.get(windowId);
- if (window != null) {
- return new AccessibilityWindowInfo(window);
+ final int displayCounts = mWindowCacheByDisplay.size();
+ for (int i = 0; i < displayCounts; i++) {
+ final SparseArray<AccessibilityWindowInfo> windowsOfDisplay =
+ mWindowCacheByDisplay.valueAt(i);
+ if (windowsOfDisplay == null) {
+ continue;
+ }
+
+ AccessibilityWindowInfo window = windowsOfDisplay.get(windowId);
+ if (window != null) {
+ return new AccessibilityWindowInfo(window);
+ }
}
return null;
}
@@ -358,7 +430,7 @@ public class AccessibilityCache {
if (DEBUG) {
Log.i(LOG_TAG, "clear()");
}
- clearWindowCache();
+ clearWindowCacheLocked();
final int nodesForWindowCount = mNodeCache.size();
for (int i = nodesForWindowCount - 1; i >= 0; i--) {
final int windowId = mNodeCache.keyAt(i);
@@ -370,8 +442,23 @@ public class AccessibilityCache {
}
}
- private void clearWindowCache() {
- mWindowCache.clear();
+ private void clearWindowCacheLocked() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "clearWindowCacheLocked");
+ }
+ final int displayCounts = mWindowCacheByDisplay.size();
+
+ if (displayCounts > 0) {
+ for (int i = displayCounts - 1; i >= 0; i--) {
+ final int displayId = mWindowCacheByDisplay.keyAt(i);
+ final SparseArray<AccessibilityWindowInfo> windows =
+ mWindowCacheByDisplay.get(displayId);
+ if (windows != null) {
+ windows.clear();
+ }
+ mWindowCacheByDisplay.remove(displayId);
+ }
+ }
mIsAllWindowsCached = false;
}
@@ -444,32 +531,41 @@ public class AccessibilityCache {
public void checkIntegrity() {
synchronized (mLock) {
// Get the root.
- if (mWindowCache.size() <= 0 && mNodeCache.size() == 0) {
+ if (mWindowCacheByDisplay.size() <= 0 && mNodeCache.size() == 0) {
return;
}
AccessibilityWindowInfo focusedWindow = null;
AccessibilityWindowInfo activeWindow = null;
- final int windowCount = mWindowCache.size();
- for (int i = 0; i < windowCount; i++) {
- AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+ final int displayCounts = mWindowCacheByDisplay.size();
+ for (int i = 0; i < displayCounts; i++) {
+ final SparseArray<AccessibilityWindowInfo> windowsOfDisplay =
+ mWindowCacheByDisplay.valueAt(i);
- // Check for one active window.
- if (window.isActive()) {
- if (activeWindow != null) {
- Log.e(LOG_TAG, "Duplicate active window:" + window);
- } else {
- activeWindow = window;
- }
+ if (windowsOfDisplay == null) {
+ continue;
}
- // Check for one focused window.
- if (window.isFocused()) {
- if (focusedWindow != null) {
- Log.e(LOG_TAG, "Duplicate focused window:" + window);
- } else {
- focusedWindow = window;
+ final int windowCount = windowsOfDisplay.size();
+ for (int j = 0; j < windowCount; j++) {
+ final AccessibilityWindowInfo window = windowsOfDisplay.valueAt(j);
+
+ // Check for one active window.
+ if (window.isActive()) {
+ if (activeWindow != null) {
+ Log.e(LOG_TAG, "Duplicate active window:" + window);
+ } else {
+ activeWindow = window;
+ }
+ }
+ // Check for one focused window.
+ if (window.isFocused()) {
+ if (focusedWindow != null) {
+ Log.e(LOG_TAG, "Duplicate focused window:" + window);
+ } else {
+ focusedWindow = window;
+ }
}
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 4db6f4f808f2..d9fa9f24f1ae 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -28,6 +28,7 @@ import android.os.SystemClock;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
+import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -267,12 +268,14 @@ public final class AccessibilityInteractionClient
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
- List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows();
- if (windows != null) {
+ SparseArray<List<AccessibilityWindowInfo>> allWindows =
+ sAccessibilityCache.getWindowsOnAllDisplays();
+ List<AccessibilityWindowInfo> windows;
+ if (allWindows != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache hit");
}
- return windows;
+ return allWindows.valueAt(Display.DEFAULT_DISPLAY);
}
if (DEBUG) {
Log.i(LOG_TAG, "Windows cache miss");
@@ -284,7 +287,9 @@ public final class AccessibilityInteractionClient
Binder.restoreCallingIdentity(identityToken);
}
if (windows != null) {
- sAccessibilityCache.setWindows(windows);
+ allWindows = new SparseArray<>();
+ allWindows.put(Display.DEFAULT_DISPLAY, windows);
+ sAccessibilityCache.setWindowsOnAllDisplays(allWindows);
return windows;
}
} else {
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 3b60aee8f604..b03732a2a7f0 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -636,9 +636,12 @@ public abstract class Animation implements Cloneable {
*
* @param bg The background color. If 0, no background. Currently must
* be black, with any desired alpha level.
+ *
+ * @deprecated None of window animations are running with background color.
*/
+ @Deprecated
public void setBackgroundColor(@ColorInt int bg) {
- mBackgroundColor = bg;
+ // The background color is not needed any more, do nothing.
}
/**
@@ -665,6 +668,7 @@ public abstract class Animation implements Cloneable {
*
* @deprecated All window animations are running with detached wallpaper.
*/
+ @Deprecated
public void setDetachWallpaper(boolean detachWallpaper) {
}
@@ -793,10 +797,13 @@ public abstract class Animation implements Cloneable {
/**
* Returns the background color behind the animation.
+ *
+ * @deprecated None of window animations are running with background color.
*/
+ @Deprecated
@ColorInt
public int getBackgroundColor() {
- return mBackgroundColor;
+ return 0;
}
/**
@@ -805,6 +812,7 @@ public abstract class Animation implements Cloneable {
*
* @deprecated All window animations are running with detached wallpaper.
*/
+ @Deprecated
public boolean getDetachWallpaper() {
return true;
}
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index b3b0b72c8799..5e02de451fa3 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -89,6 +89,11 @@ final class ChildContentCaptureSession extends ContentCaptureSession {
}
@Override
+ public void internalNotifySessionLifecycle(boolean started) {
+ getMainCaptureSession().notifySessionLifecycle(mId, started);
+ }
+
+ @Override
boolean isContentCaptureEnabled() {
return getMainCaptureSession().isContentCaptureEnabled();
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 625fcda76563..cf08c18b019a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -439,6 +439,19 @@ public abstract class ContentCaptureSession implements AutoCloseable {
public abstract void internalNotifyViewTreeEvent(boolean started);
/**
+ * Notifies the Content Capture Service that a session has paused/resumed.
+ *
+ * @param started whether session has resumed.
+ */
+ public final void notifySessionLifecycle(boolean started) {
+ if (!isContentCaptureEnabled()) return;
+
+ internalNotifySessionLifecycle(started);
+ }
+
+ abstract void internalNotifySessionLifecycle(boolean started);
+
+ /**
* Creates a {@link ViewStructure} for a "standard" view.
*
* <p>This method should be called after a visible view is laid out; the view then must populate
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 1e7440bd5a43..349ef09cf059 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -583,6 +583,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
}
@Override
+ public void internalNotifySessionLifecycle(boolean started) {
+ notifySessionLifecycle(mId, started);
+ }
+
+ @Override
boolean isContentCaptureEnabled() {
return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
}
@@ -637,10 +642,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH);
}
- /** Public because is also used by ViewRootImpl */
- public void notifySessionLifecycle(boolean started) {
+ void notifySessionLifecycle(int sessionId, boolean started) {
final int type = started ? TYPE_SESSION_RESUMED : TYPE_SESSION_PAUSED;
- sendEvent(new ContentCaptureEvent(mId, type), FORCE_FLUSH);
+ sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH);
}
void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index cac75cfd8432..57ce28e5059a 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2561,7 +2561,8 @@ public class Editor {
* @return True when the TextView isFocused and has a valid zero-length selection (cursor).
*/
private boolean shouldBlink() {
- if (!isCursorVisible() || !mTextView.isFocused()) return false;
+ if (!isCursorVisible() || !mTextView.isFocused() ||
+ !mTextView.isVisibleToUser()) return false;
final int start = mTextView.getSelectionStart();
if (start < 0) return false;
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
new file mode 100644
index 000000000000..1ce071bd005a
--- /dev/null
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.compat;
+
+import android.util.StatsLog;
+
+/**
+ * A helper class to report changes to stats log.
+ *
+ * @hide
+ */
+public final class ChangeReporter {
+
+ /**
+ * Transforms StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE enum to a string.
+ *
+ * @param state to transform
+ * @return a string representing the state
+ */
+ private static String stateToString(int state) {
+ switch (state) {
+ case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED:
+ return "LOGGED";
+ case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED:
+ return "ENABLED";
+ case StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED:
+ return "DISABLED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ /**
+ * Constructs and returns a string to be logged to logcat when a change is reported.
+ *
+ * @param uid affected by the change
+ * @param changeId the reported change id
+ * @param state of the reported change - enabled/disabled/only logged
+ * @return string to log
+ */
+ public static String createLogString(int uid, long changeId, int state) {
+ return String.format("Compat change id reported: %d; UID %d; state: %s", changeId, uid,
+ stateToString(state));
+ }
+
+ /**
+ * Report the change to stats log.
+ *
+ * @param uid affected by the change
+ * @param changeId the reported change id
+ * @param state of the reported change - enabled/disabled/only logged
+ * @param source of the logging - app process or system server
+ */
+ public void reportChange(int uid, long changeId, int state, int source) {
+ //TODO(b/138374585): Implement rate limiting for stats log.
+ StatsLog.write(StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid, changeId,
+ state, source);
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8978414034ff..9bddd2aa0f1d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -13325,7 +13325,8 @@ public class BatteryStatsImpl extends BatteryStats {
return;
}
- if (mBatteryStatsHistory.getActiveFile() == null) {
+ final AtomicFile activeHistoryFile = mBatteryStatsHistory.getActiveFile();
+ if (activeHistoryFile == null) {
Slog.w(TAG,
"readLocked: no history file associated with this instance");
return;
@@ -13336,14 +13337,16 @@ public class BatteryStatsImpl extends BatteryStats {
Parcel stats = Parcel.obtain();
try {
final long start = SystemClock.uptimeMillis();
- byte[] raw = mStatsFile.readFully();
- stats.unmarshall(raw, 0, raw.length);
- stats.setDataPosition(0);
- readSummaryFromParcel(stats);
- if (DEBUG) {
- Slog.d(TAG, "readLocked stats file:" + mStatsFile.getBaseFile().getPath()
- + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
- - start));
+ if (mStatsFile.exists()) {
+ byte[] raw = mStatsFile.readFully();
+ stats.unmarshall(raw, 0, raw.length);
+ stats.setDataPosition(0);
+ readSummaryFromParcel(stats);
+ if (DEBUG) {
+ Slog.d(TAG, "readLocked stats file:" + mStatsFile.getBaseFile().getPath()
+ + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
+ - start));
+ }
}
} catch (Exception e) {
Slog.e(TAG, "Error reading battery statistics", e);
@@ -13355,17 +13358,19 @@ public class BatteryStatsImpl extends BatteryStats {
Parcel history = Parcel.obtain();
try {
final long start = SystemClock.uptimeMillis();
- byte[] raw = mBatteryStatsHistory.getActiveFile().readFully();
- if (raw.length > 0) {
- history.unmarshall(raw, 0, raw.length);
- history.setDataPosition(0);
- readHistoryBuffer(history, true);
- }
- if (DEBUG) {
- Slog.d(TAG, "readLocked history file::"
- + mBatteryStatsHistory.getActiveFile().getBaseFile().getPath()
- + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
- - start));
+ if (activeHistoryFile.exists()) {
+ byte[] raw = activeHistoryFile.readFully();
+ if (raw.length > 0) {
+ history.unmarshall(raw, 0, raw.length);
+ history.setDataPosition(0);
+ readHistoryBuffer(history, true);
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "readLocked history file::"
+ + activeHistoryFile.getBaseFile().getPath()
+ + " bytes:" + raw.length + " takes ms:" + (SystemClock.uptimeMillis()
+ - start));
+ }
}
} catch (Exception e) {
Slog.e(TAG, "Error reading battery history", e);
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index ab5231425b6d..cfdf26abc25c 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -108,7 +108,7 @@ void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
if (!ConvertKeyAndForward(env, keyJ, true, handler)) {
// Must have been a failure in SetProperty.
jniThrowException(env, "java/lang/RuntimeException",
- "failed to set system property");
+ "failed to set system property (check logcat for reason)");
}
}
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 7975c8675954..c380fd5e439f 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -83,7 +83,7 @@ private:
return mInputConsumer.getChannel()->getName();
}
- virtual int handleEvent(int receiveFd, int events, void* data);
+ virtual int handleEvent(int receiveFd, int events, void* data) override;
};
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 3323095a6244..b8c5270ef9d8 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2402,6 +2402,10 @@ enum PageId {
// Note: Gear icon is shown next to gesture navigation preference and opens sensitivity dialog
SETTINGS_GESTURE_NAV_BACK_SENSITIVITY_DLG = 1748;
+ // OPEN: Settings > System > Aware > Aware Display
+ // CATEGORY: SETTINGS
+ // OS: Q
+ SETTINGS_AWARE_DISPLAY = 1750;
// ---- End Q Constants, all Q constants go above this line ----
// OPEN: Settings > Network & Internet > Wi-Fi > Click new network
// CATEGORY: SETTINGS
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 777902578e1b..fd1050393de1 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -196,7 +196,7 @@ message StackProto {
repeated TaskProto tasks = 3;
optional bool fills_parent = 4;
optional .android.graphics.RectProto bounds = 5;
- optional bool animation_background_surface_is_dimming = 6;
+ optional bool animation_background_surface_is_dimming = 6 [deprecated=true];
optional bool defer_removal = 7;
optional float minimize_amount = 8;
optional bool adjusted_for_ime = 9;
diff --git a/core/res/res/drawable/media_seamless_background.xml b/core/res/res/drawable/media_seamless_background.xml
new file mode 100644
index 000000000000..aec89e0e8980
--- /dev/null
+++ b/core/res/res/drawable/media_seamless_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<ripple
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#1f000000">
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <stroke android:width="1dp" android:color="#1f000000"/>
+ <corners android:radius="20dp"/>
+ </shape>
+ </item>
+</ripple>
diff --git a/core/res/res/layout/notification_material_media_transfer_action.xml b/core/res/res/layout/notification_material_media_transfer_action.xml
new file mode 100644
index 000000000000..98d8f1eee8c9
--- /dev/null
+++ b/core/res/res/layout/notification_material_media_transfer_action.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:visibility="gone"
+ android:padding="4dp"
+ android:layout_marginStart="10dp"
+ android:gravity="center"
+ android:background="@drawable/media_seamless_background">
+ <ImageView
+ android:layout_width="?attr/notificationHeaderIconSize"
+ android:layout_height="?attr/notificationHeaderIconSize"
+ android:src="@drawable/ic_media_seamless"
+ android:id="@+id/media_seamless_image" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/notificationHeaderTextAppearance"
+ android:text="@string/ext_media_seamless_action"
+ android:id="@+id/media_seamless_text"
+ android:paddingEnd="2dp" />
+</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 0f53549a966c..f5fa1b6a795a 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -175,5 +175,9 @@
android:contentDescription="@string/notification_appops_overlay_active"
/>
</LinearLayout>
+ <include
+ layout="@layout/notification_material_media_transfer_action"
+ android:id="@+id/media_seamless"
+ />
</NotificationHeaderView>
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index 3267f726c69d..6c47c2c51601 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -80,10 +80,6 @@
layout="@layout/notification_material_media_action"
android:id="@+id/action4"
/>
- <include
- layout="@layout/notification_material_media_action"
- android:id="@+id/media_seamless"
- />
</LinearLayout>
<ViewStub android:id="@+id/notification_media_seekbar_container"
android:layout_width="match_parent"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 18d77067e2b3..d2989d9a8ab6 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5015,9 +5015,9 @@
<string name="importance_from_person">This is important because of the people involved.</string>
<!-- Message to user that app trying to create user for an account that already exists. [CHAR LIMIT=none] -->
- <string name="user_creation_account_exists">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> ?</string>
+ <string name="user_creation_account_exists">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> (a User with this account already exists) ?</string>
<!-- Message to user that app is trying to create user for a specified account. [CHAR LIMIT=none] -->
- <string name="user_creation_adding">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar">%2$s</xliff:g> (a User with this account already exists) ?</string>
+ <string name="user_creation_adding">Allow <xliff:g id="app" example="Gmail">%1$s</xliff:g> to create a new User with <xliff:g id="account" example="foobar@gmail.com">%2$s</xliff:g> ?</string>
<!-- Locale picker strings -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8aa4a8cc01b5..761e02fe5dad 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -206,6 +206,8 @@
<java-symbol type="id" name="action3" />
<java-symbol type="id" name="action4" />
<java-symbol type="id" name="media_seamless" />
+ <java-symbol type="id" name="media_seamless_image" />
+ <java-symbol type="id" name="media_seamless_text" />
<java-symbol type="id" name="notification_media_seekbar_container" />
<java-symbol type="id" name="notification_media_content" />
<java-symbol type="id" name="notification_media_progress" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 1670d49a46c4..a4c504b9cbdf 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -43,7 +43,6 @@ android_test {
libs: [
"android.test.runner",
- "telephony-common",
"testables",
"org.apache.http.legacy",
"android.test.base",
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 1a22a70eed36..6bce6517a85d 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -29,6 +29,8 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.util.SparseArray;
+import android.view.Display;
import android.view.View;
import androidx.test.filters.LargeTest;
@@ -51,12 +53,17 @@ import java.util.List;
public class AccessibilityCacheTest {
private static final int WINDOW_ID_1 = 0xBEEF;
private static final int WINDOW_ID_2 = 0xFACE;
+ private static final int WINDOW_ID_3 = 0xABCD;
+ private static final int WINDOW_ID_4 = 0xDCBA;
private static final int SINGLE_VIEW_ID = 0xCAFE;
private static final int OTHER_VIEW_ID = 0xCAB2;
private static final int PARENT_VIEW_ID = 0xFED4;
private static final int CHILD_VIEW_ID = 0xFEED;
private static final int OTHER_CHILD_VIEW_ID = 0xACE2;
private static final int MOCK_CONNECTION_ID = 1;
+ private static final int SECONDARY_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1;
+ private static final int DEFAULT_WINDOW_LAYER = 0;
+ private static final int SPECIFIC_WINDOW_LAYER = 5;
AccessibilityCache mAccessibilityCache;
AccessibilityCache.AccessibilityNodeRefresher mAccessibilityNodeRefresher;
@@ -70,7 +77,7 @@ public class AccessibilityCacheTest {
@After
public void tearDown() {
- // Make sure we're recycling all of our window and node infos
+ // Make sure we're recycling all of our window and node infos.
mAccessibilityCache.clear();
AccessibilityInteractionClient.getInstance().clearCache();
}
@@ -78,7 +85,7 @@ public class AccessibilityCacheTest {
@Test
public void testEmptyCache_returnsNull() {
assertNull(mAccessibilityCache.getNode(0, 0));
- assertNull(mAccessibilityCache.getWindows());
+ assertNull(mAccessibilityCache.getWindowsOnAllDisplays());
assertNull(mAccessibilityCache.getWindow(0));
}
@@ -114,10 +121,11 @@ public class AccessibilityCacheTest {
try {
windowInfo = AccessibilityWindowInfo.obtain();
windowInfo.setId(WINDOW_ID_1);
+ windowInfo.setDisplayId(Display.DEFAULT_DISPLAY);
mAccessibilityCache.addWindow(windowInfo);
// Make a copy
copyOfInfo = AccessibilityWindowInfo.obtain(windowInfo);
- windowInfo.setId(WINDOW_ID_2); // Simulate recycling and reusing the original info
+ windowInfo.setId(WINDOW_ID_2); // Simulate recycling and reusing the original info.
windowFromCache = mAccessibilityCache.getWindow(WINDOW_ID_1);
assertEquals(copyOfInfo, windowFromCache);
} finally {
@@ -129,39 +137,40 @@ public class AccessibilityCacheTest {
@Test
public void addWindowThenClear_noLongerInCache() {
- putWindowWithIdInCache(WINDOW_ID_1);
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
+ DEFAULT_WINDOW_LAYER);
mAccessibilityCache.clear();
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1));
}
@Test
public void addWindowGetOtherId_returnsNull() {
- putWindowWithIdInCache(WINDOW_ID_1);
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
+ DEFAULT_WINDOW_LAYER);
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1 + 1));
}
@Test
public void addWindowThenGetWindows_returnsNull() {
- putWindowWithIdInCache(WINDOW_ID_1);
- assertNull(mAccessibilityCache.getWindows());
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
+ DEFAULT_WINDOW_LAYER);
+ assertNull(mAccessibilityCache.getWindowsOnAllDisplays());
}
@Test
public void setWindowsThenGetWindows_returnsInDecreasingLayerOrder() {
- AccessibilityWindowInfo windowInfo1 = null, windowInfo2 = null;
- AccessibilityWindowInfo window1Out = null, window2Out = null;
+ AccessibilityWindowInfo windowInfo1 = null;
+ AccessibilityWindowInfo windowInfo2 = null;
+ AccessibilityWindowInfo window1Out = null;
+ AccessibilityWindowInfo window2Out = null;
List<AccessibilityWindowInfo> windowsOut = null;
try {
- windowInfo1 = AccessibilityWindowInfo.obtain();
- windowInfo1.setId(WINDOW_ID_1);
- windowInfo1.setLayer(5);
- windowInfo2 = AccessibilityWindowInfo.obtain();
- windowInfo2.setId(WINDOW_ID_2);
- windowInfo2.setLayer(windowInfo1.getLayer() + 1);
+ windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER);
+ windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 1);
List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2);
- mAccessibilityCache.setWindows(windowsIn);
+ setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn);
- windowsOut = mAccessibilityCache.getWindows();
+ windowsOut = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2);
@@ -182,8 +191,151 @@ public class AccessibilityCacheTest {
}
@Test
+ public void setWindowsAndAddWindow_thenGetWindows_returnsInDecreasingLayerOrder() {
+ AccessibilityWindowInfo windowInfo1 = null;
+ AccessibilityWindowInfo windowInfo2 = null;
+ AccessibilityWindowInfo window1Out = null;
+ AccessibilityWindowInfo window2Out = null;
+ AccessibilityWindowInfo window3Out = null;
+ List<AccessibilityWindowInfo> windowsOut = null;
+ try {
+ windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER);
+ windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 2);
+ List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2);
+ setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn);
+
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_3, Display.DEFAULT_DISPLAY,
+ windowInfo1.getLayer() + 1);
+
+ windowsOut = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
+ window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
+ window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2);
+ window3Out = mAccessibilityCache.getWindow(WINDOW_ID_3);
+
+ assertEquals(3, windowsOut.size());
+ assertEquals(windowInfo2, windowsOut.get(0));
+ assertEquals(windowInfo1, windowsOut.get(2));
+ assertEquals(windowInfo1, window1Out);
+ assertEquals(windowInfo2, window2Out);
+ assertEquals(window3Out, windowsOut.get(1));
+ } finally {
+ window1Out.recycle();
+ window2Out.recycle();
+ window3Out.recycle();
+ windowInfo1.recycle();
+ windowInfo2.recycle();
+ for (AccessibilityWindowInfo windowInfo : windowsOut) {
+ windowInfo.recycle();
+ }
+ }
+ }
+
+ @Test
+ public void
+ setWindowsAtFirstDisplay_thenAddWindowAtSecondDisplay_returnWindowLayerOrderUnchange() {
+ AccessibilityWindowInfo windowInfo1 = null;
+ AccessibilityWindowInfo windowInfo2 = null;
+ AccessibilityWindowInfo window1Out = null;
+ AccessibilityWindowInfo window2Out = null;
+ List<AccessibilityWindowInfo> windowsOut = null;
+ try {
+ // Sets windows to default display.
+ windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER);
+ windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 2);
+ List<AccessibilityWindowInfo> windowsIn = Arrays.asList(windowInfo1, windowInfo2);
+ setWindowsByDisplay(Display.DEFAULT_DISPLAY, windowsIn);
+ // Adds one window to second display.
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_3, SECONDARY_DISPLAY_ID,
+ windowInfo1.getLayer() + 1);
+
+ windowsOut = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
+ window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
+ window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2);
+
+ assertEquals(2, windowsOut.size());
+ assertEquals(windowInfo2, windowsOut.get(0));
+ assertEquals(windowInfo1, windowsOut.get(1));
+ assertEquals(windowInfo1, window1Out);
+ assertEquals(windowInfo2, window2Out);
+ } finally {
+ window1Out.recycle();
+ window2Out.recycle();
+ windowInfo1.recycle();
+ windowInfo2.recycle();
+ for (AccessibilityWindowInfo windowInfo : windowsOut) {
+ windowInfo.recycle();
+ }
+ }
+ }
+
+ @Test
+ public void setWindowsAtTwoDisplays_thenGetWindows_returnsInDecreasingLayerOrder() {
+ AccessibilityWindowInfo windowInfo1 = null;
+ AccessibilityWindowInfo windowInfo2 = null;
+ AccessibilityWindowInfo window1Out = null;
+ AccessibilityWindowInfo window2Out = null;
+ AccessibilityWindowInfo windowInfo3 = null;
+ AccessibilityWindowInfo windowInfo4 = null;
+ AccessibilityWindowInfo window3Out = null;
+ AccessibilityWindowInfo window4Out = null;
+ List<AccessibilityWindowInfo> windowsOut1 = null;
+ List<AccessibilityWindowInfo> windowsOut2 = null;
+ try {
+ // Prepares all windows for default display.
+ windowInfo1 = obtainAccessibilityWindowInfo(WINDOW_ID_1, SPECIFIC_WINDOW_LAYER);
+ windowInfo2 = obtainAccessibilityWindowInfo(WINDOW_ID_2, windowInfo1.getLayer() + 1);
+ List<AccessibilityWindowInfo> windowsIn1 = Arrays.asList(windowInfo1, windowInfo2);
+ // Prepares all windows for second display.
+ windowInfo3 = obtainAccessibilityWindowInfo(WINDOW_ID_3, windowInfo1.getLayer() + 2);
+ windowInfo4 = obtainAccessibilityWindowInfo(WINDOW_ID_4, windowInfo1.getLayer() + 3);
+ List<AccessibilityWindowInfo> windowsIn2 = Arrays.asList(windowInfo3, windowInfo4);
+ // Sets all windows of all displays into A11y cache.
+ SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>();
+ allWindows.put(Display.DEFAULT_DISPLAY, windowsIn1);
+ allWindows.put(SECONDARY_DISPLAY_ID, windowsIn2);
+ mAccessibilityCache.setWindowsOnAllDisplays(allWindows);
+ // Gets windows at default display.
+ windowsOut1 = getWindowsByDisplay(Display.DEFAULT_DISPLAY);
+ window1Out = mAccessibilityCache.getWindow(WINDOW_ID_1);
+ window2Out = mAccessibilityCache.getWindow(WINDOW_ID_2);
+
+ assertEquals(2, windowsOut1.size());
+ assertEquals(windowInfo2, windowsOut1.get(0));
+ assertEquals(windowInfo1, windowsOut1.get(1));
+ assertEquals(windowInfo1, window1Out);
+ assertEquals(windowInfo2, window2Out);
+ // Gets windows at seocnd display.
+ windowsOut2 = getWindowsByDisplay(SECONDARY_DISPLAY_ID);
+ window3Out = mAccessibilityCache.getWindow(WINDOW_ID_3);
+ window4Out = mAccessibilityCache.getWindow(WINDOW_ID_4);
+
+ assertEquals(2, windowsOut2.size());
+ assertEquals(windowInfo4, windowsOut2.get(0));
+ assertEquals(windowInfo3, windowsOut2.get(1));
+ assertEquals(windowInfo3, window3Out);
+ assertEquals(windowInfo4, window4Out);
+ } finally {
+ window1Out.recycle();
+ window2Out.recycle();
+ windowInfo1.recycle();
+ windowInfo2.recycle();
+ window3Out.recycle();
+ window4Out.recycle();
+ windowInfo3.recycle();
+ windowInfo4.recycle();
+ for (AccessibilityWindowInfo windowInfo : windowsOut1) {
+ windowInfo.recycle();
+ }
+ for (AccessibilityWindowInfo windowInfo : windowsOut2) {
+ windowInfo.recycle();
+ }
+ }
+ }
+
+ @Test
public void addWindowThenStateChangedEvent_noLongerInCache() {
- putWindowWithIdInCache(WINDOW_ID_1);
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
+ DEFAULT_WINDOW_LAYER);
mAccessibilityCache.onAccessibilityEvent(
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED));
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1));
@@ -191,7 +343,8 @@ public class AccessibilityCacheTest {
@Test
public void addWindowThenWindowsChangedEvent_noLongerInCache() {
- putWindowWithIdInCache(WINDOW_ID_1);
+ putWindowWithWindowIdAndDisplayIdInCache(WINDOW_ID_1, Display.DEFAULT_DISPLAY,
+ DEFAULT_WINDOW_LAYER);
mAccessibilityCache.onAccessibilityEvent(
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOWS_CHANGED));
assertNull(mAccessibilityCache.getWindow(WINDOW_ID_1));
@@ -622,9 +775,16 @@ public class AccessibilityCacheTest {
}
}
- private void putWindowWithIdInCache(int id) {
+ private AccessibilityWindowInfo obtainAccessibilityWindowInfo(int windowId, int layer) {
AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain();
- windowInfo.setId(id);
+ windowInfo.setId(windowId);
+ windowInfo.setLayer(layer);
+ return windowInfo;
+ }
+
+ private void putWindowWithWindowIdAndDisplayIdInCache(int windowId, int displayId, int layer) {
+ AccessibilityWindowInfo windowInfo = obtainAccessibilityWindowInfo(windowId, layer);
+ windowInfo.setDisplayId(displayId);
mAccessibilityCache.addWindow(windowInfo);
windowInfo.recycle();
}
@@ -713,4 +873,20 @@ public class AccessibilityCacheTest {
}
}
}
+
+ private void setWindowsByDisplay(int displayId, List<AccessibilityWindowInfo> windows) {
+ SparseArray<List<AccessibilityWindowInfo>> allWindows = new SparseArray<>();
+ allWindows.put(displayId, windows);
+ mAccessibilityCache.setWindowsOnAllDisplays(allWindows);
+ }
+
+ private List<AccessibilityWindowInfo> getWindowsByDisplay(int displayId) {
+ final SparseArray<List<AccessibilityWindowInfo>> allWindows =
+ mAccessibilityCache.getWindowsOnAllDisplays();
+
+ if (allWindows != null && allWindows.size() > 0) {
+ return allWindows.get(displayId);
+ }
+ return null;
+ }
}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 81ce15a4d8d2..c5da54936653 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -162,6 +162,11 @@ public class ContentCaptureSessionTest {
}
@Override
+ public void internalNotifySessionLifecycle(boolean started) {
+ throw new UnsupportedOperationException("Should not have been called");
+ }
+
+ @Override
public void updateContentCaptureContext(ContentCaptureContext context) {
throw new UnsupportedOperationException("should not have been called");
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index c44b7d81868d..cc6eeedaab46 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -481,6 +481,36 @@ public class ChooserActivityTest {
}
@Test
+ public void copyTextToClipboardLogging() throws Exception {
+ Intent sendIntent = createSendTextIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+ Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ MetricsLogger mockLogger = sOverrides.metricsLogger;
+ ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
+
+ final ChooserWrapperActivity activity = mActivityRule
+ .launchActivity(Intent.createChooser(sendIntent, null));
+ waitForIdle();
+
+ onView(withId(R.id.copy_button)).check(matches(isDisplayed()));
+ onView(withId(R.id.copy_button)).perform(click());
+
+ verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture());
+ // First is Activity shown, Second is "with preview"
+ assertThat(logMakerCaptor.getAllValues().get(2).getCategory(),
+ is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET));
+ assertThat(logMakerCaptor
+ .getAllValues().get(2)
+ .getSubtype(),
+ is(1));
+ }
+
+ @Test
public void oneVisibleImagePreview() throws InterruptedException {
Uri uri = Uri.parse("android.resource://com.android.frameworks.coretests/"
+ com.android.frameworks.coretests.R.drawable.test320x240);
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 08f417662523..54995ac9d050 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -16,11 +16,12 @@
package android.security;
+import android.annotation.UnsupportedAppUsage;
+
import com.android.org.bouncycastle.util.io.pem.PemObject;
import com.android.org.bouncycastle.util.io.pem.PemReader;
import com.android.org.bouncycastle.util.io.pem.PemWriter;
-import android.annotation.UnsupportedAppUsage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -90,9 +91,9 @@ public class Credentials {
public static final String EXTRA_INSTALL_AS_UID = "install_as_uid";
/**
- * Intent extra: name for the user's private key.
+ * Intent extra: name for the user's key pair.
*/
- public static final String EXTRA_USER_PRIVATE_KEY_NAME = "user_private_key_name";
+ public static final String EXTRA_USER_KEY_ALIAS = "user_key_pair_name";
/**
* Intent extra: data for the user's private key in PEM-encoded PKCS#8.
@@ -100,21 +101,11 @@ public class Credentials {
public static final String EXTRA_USER_PRIVATE_KEY_DATA = "user_private_key_data";
/**
- * Intent extra: name for the user's certificate.
- */
- public static final String EXTRA_USER_CERTIFICATE_NAME = "user_certificate_name";
-
- /**
* Intent extra: data for the user's certificate in PEM-encoded X.509.
*/
public static final String EXTRA_USER_CERTIFICATE_DATA = "user_certificate_data";
/**
- * Intent extra: name for CA certificate chain
- */
- public static final String EXTRA_CA_CERTIFICATES_NAME = "ca_certificates_name";
-
- /**
* Intent extra: data for CA certificate chain in PEM-encoded X.509.
*/
public static final String EXTRA_CA_CERTIFICATES_DATA = "ca_certificates_data";
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index b3cdff7eedf7..97da3cc6f80f 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -43,7 +43,8 @@ interface IKeyChainService {
String installCaCertificate(in byte[] caCertificate);
// APIs used by DevicePolicyManager
- boolean installKeyPair(in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias);
+ boolean installKeyPair(
+ in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid);
boolean removeKeyPair(String alias);
// APIs used by Settings
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b5ef8f43dfb6..684dc22ee4d1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -146,7 +146,7 @@ void CanvasContext::setSurface(sp<Surface>&& surface) {
if (surface) {
mNativeSurface = new ReliableSurface{std::move(surface)};
// TODO: Fix error handling & re-shorten timeout
- mNativeSurface->setDequeueTimeout(4000_ms);
+ ANativeWindow_setDequeueTimeout(mNativeSurface.get(), 4000_ms);
mNativeSurface->enableFrameTimestamps(true);
} else {
mNativeSurface = nullptr;
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index 0d251b1f10e7..f768df37ba7d 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -31,8 +31,6 @@ public:
ReliableSurface(sp<Surface>&& surface);
~ReliableSurface();
- void setDequeueTimeout(nsecs_t timeout) { mSurface->setDequeueTimeout(timeout); }
-
int reserveNext();
void allocateBuffers() { mSurface->allocateBuffers(); }
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index ff6921d3e1c2..b36aa036daba 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -18,7 +18,7 @@ java_sdk_library {
name: "com.android.location.provider",
srcs: [
"java/**/*.java",
- ":framework-srcs",
+ ":framework-all-sources",
],
libs: [
"androidx.annotation_annotation",
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 55583d58e0c2..5b535651abd9 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -62,6 +62,7 @@ import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
@@ -1451,6 +1452,37 @@ public class ExifInterface {
}
/**
+ * Returns whether ExifInterface currently supports parsing data from the specified mime type
+ * or not.
+ *
+ * @param mimeType the string value of mime type
+ */
+ public static boolean isSupportedMimeType(@NonNull String mimeType) {
+ if (mimeType == null) {
+ throw new NullPointerException("mimeType shouldn't be null");
+ }
+
+ switch (mimeType.toLowerCase(Locale.ROOT)) {
+ case "image/jpeg":
+ case "image/x-adobe-dng":
+ case "image/x-canon-cr2":
+ case "image/x-nikon-nef":
+ case "image/x-nikon-nrw":
+ case "image/x-sony-arw":
+ case "image/x-panasonic-rw2":
+ case "image/x-olympus-orf":
+ case "image/x-pentax-pef":
+ case "image/x-samsung-srw":
+ case "image/x-fuji-raf":
+ case "image/heic":
+ case "image/heif":
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
* Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in
* the image file.
*
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index cbc820b6fde0..05aaa82f8ac8 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -745,6 +745,8 @@ status_t JMediaCodec::getCodecInfo(JNIEnv *env, jobject *codecInfoObject) const
status_t JMediaCodec::getMetrics(JNIEnv *, MediaAnalyticsItem * &reply) const {
mediametrics_handle_t reply2 = MediaAnalyticsItem::convert(reply);
status_t status = mCodec->getMetrics(reply2);
+ // getMetrics() updates reply2, pass the converted update along to our caller.
+ reply = MediaAnalyticsItem::convert(reply2);
return status;
}
diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp
index 85a007f427b8..2286c5379e1a 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -18,7 +18,7 @@ java_sdk_library {
name: "com.android.mediadrm.signer",
srcs: [
"java/**/*.java",
- ":framework-srcs",
+ ":framework-all-sources",
],
api_packages: ["com.android.mediadrm.signer"],
}
diff --git a/core/proto/android/server/backup_chunks_metadata.proto b/packages/BackupEncryption/proto/backup_chunks_metadata.proto
index a375f02545c5..2fdedbf70975 100644
--- a/core/proto/android/server/backup_chunks_metadata.proto
+++ b/packages/BackupEncryption/proto/backup_chunks_metadata.proto
@@ -15,8 +15,10 @@
*/
syntax = "proto2";
-package com.android.server.backup.encryption.chunk;
+package android_backup_crypto;
+
+option java_package = "com.android.server.backup.encryption.protos";
option java_outer_classname = "ChunksMetadataProto";
// Cipher type with which the chunks are encrypted. For now we only support AES/GCM/NoPadding, but
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/Chunk.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/Chunk.java
deleted file mode 100644
index ba328609a77e..000000000000
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/Chunk.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup.encryption.chunk;
-
-import android.util.proto.ProtoInputStream;
-
-import java.io.IOException;
-
-/**
- * Information about a chunk entry in a protobuf. Only used for reading from a {@link
- * ProtoInputStream}.
- */
-public class Chunk {
- /**
- * Reads a Chunk from a {@link ProtoInputStream}. Expects the message to be of format {@link
- * ChunksMetadataProto.Chunk}.
- *
- * @param inputStream currently at a {@link ChunksMetadataProto.Chunk} message.
- * @throws IOException when the message is not structured as expected or a field can not be
- * read.
- */
- static Chunk readFromProto(ProtoInputStream inputStream) throws IOException {
- Chunk result = new Chunk();
-
- while (inputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (inputStream.getFieldNumber()) {
- case (int) ChunksMetadataProto.Chunk.HASH:
- result.mHash = inputStream.readBytes(ChunksMetadataProto.Chunk.HASH);
- break;
- case (int) ChunksMetadataProto.Chunk.LENGTH:
- result.mLength = inputStream.readInt(ChunksMetadataProto.Chunk.LENGTH);
- break;
- }
- }
-
- return result;
- }
-
- private int mLength;
- private byte[] mHash;
-
- /** Private constructor. This class should only be instantiated by calling readFromProto. */
- private Chunk() {
- // Set default values for fields in case they are not available in the proto.
- mHash = new byte[]{};
- mLength = 0;
- }
-
- public int getLength() {
- return mLength;
- }
-
- public byte[] getHash() {
- return mHash;
- }
-}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java
index a44890118717..51d7d532c920 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java
@@ -17,51 +17,41 @@
package com.android.server.backup.encryption.chunk;
import android.annotation.Nullable;
-import android.util.proto.ProtoInputStream;
-import java.io.IOException;
-import java.util.Collections;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
+
import java.util.HashMap;
import java.util.Map;
/**
- * Chunk listing in a format optimized for quick look-up of chunks via their hash keys. This is
+ * Chunk listing in a format optimized for quick look up of chunks via their hash keys. This is
* useful when building an incremental backup. After a chunk has been produced, the algorithm can
* quickly look up whether the chunk existed in the previous backup by checking this chunk listing.
* It can then tell the server to use that chunk, through telling it the position and length of the
* chunk in the previous backup's blob.
*/
public class ChunkListingMap {
- /**
- * Reads a ChunkListingMap from a {@link ProtoInputStream}. Expects the message to be of format
- * {@link ChunksMetadataProto.ChunkListing}.
- *
- * @param inputStream Currently at a {@link ChunksMetadataProto.ChunkListing} message.
- * @throws IOException when the message is not structured as expected or a field can not be
- * read.
- */
- public static ChunkListingMap readFromProto(ProtoInputStream inputStream) throws IOException {
- Map<ChunkHash, Entry> entries = new HashMap();
+
+ private final Map<ChunkHash, Entry> mChunksByHash;
+
+ /** Construct a map from a {@link ChunksMetadataProto.ChunkListing} protobuf */
+ public static ChunkListingMap fromProto(ChunksMetadataProto.ChunkListing chunkListingProto) {
+ Map<ChunkHash, Entry> entries = new HashMap<>();
long start = 0;
- while (inputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- if (inputStream.getFieldNumber() == (int) ChunksMetadataProto.ChunkListing.CHUNKS) {
- long chunkToken = inputStream.start(ChunksMetadataProto.ChunkListing.CHUNKS);
- Chunk chunk = Chunk.readFromProto(inputStream);
- entries.put(new ChunkHash(chunk.getHash()), new Entry(start, chunk.getLength()));
- start += chunk.getLength();
- inputStream.end(chunkToken);
- }
+ for (ChunksMetadataProto.Chunk chunk : chunkListingProto.chunks) {
+ entries.put(new ChunkHash(chunk.hash), new Entry(start, chunk.length));
+ start += chunk.length;
}
return new ChunkListingMap(entries);
}
- private final Map<ChunkHash, Entry> mChunksByHash;
-
private ChunkListingMap(Map<ChunkHash, Entry> chunksByHash) {
- mChunksByHash = Collections.unmodifiableMap(new HashMap<>(chunksByHash));
+ // This is only called from the {@link #fromProto} method, so we don't
+ // need to take a copy.
+ this.mChunksByHash = chunksByHash;
}
/** Returns {@code true} if there is a chunk with the given SHA-256 MAC key in the listing. */
@@ -81,19 +71,14 @@ public class ChunkListingMap {
return mChunksByHash.get(hash);
}
- /** Returns the number of chunks in this listing. */
- public int getChunkCount() {
- return mChunksByHash.size();
- }
-
/** Information about a chunk entry in a backup blob - i.e., its position and length. */
public static final class Entry {
private final int mLength;
private final long mStart;
private Entry(long start, int length) {
- mStart = start;
mLength = length;
+ mStart = start;
}
/** Returns the length of the chunk in bytes. */
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
index 8cb028e46e9d..9cda3395f79a 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
@@ -16,9 +16,9 @@
package com.android.server.backup.encryption.chunk;
-import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
-import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.EXPLICIT_STARTS;
-import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.INLINE_LENGTHS;
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.EXPLICIT_STARTS;
+import static com.android.server.backup.encryption.protos.nano.ChunksMetadataProto.INLINE_LENGTHS;
import android.annotation.IntDef;
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
index 7b38dd4a1dc3..6b9be9fd91d3 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
@@ -17,7 +17,7 @@
package com.android.server.backup.encryption.chunking;
import com.android.server.backup.encryption.chunk.ChunkOrderingType;
-import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
import java.io.IOException;
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
index 567f75d59513..e707350505fb 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
@@ -17,7 +17,7 @@
package com.android.server.backup.encryption.chunking;
import com.android.server.backup.encryption.chunk.ChunkOrderingType;
-import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
import java.io.IOException;
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
index 24e5573b891d..c5f78c254cd1 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
@@ -19,10 +19,8 @@ package com.android.server.backup.encryption.chunk;
import static com.google.common.truth.Truth.assertThat;
import android.platform.test.annotations.Presubmit;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-import com.android.internal.util.Preconditions;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
import com.google.common.base.Charsets;
@@ -31,167 +29,86 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
-import java.io.ByteArrayInputStream;
import java.util.Arrays;
@RunWith(RobolectricTestRunner.class)
@Presubmit
public class ChunkListingMapTest {
- private static final String CHUNK_A = "CHUNK_A";
- private static final String CHUNK_B = "CHUNK_B";
- private static final String CHUNK_C = "CHUNK_C";
+ private static final ChunkHash CHUNK_A_HASH = getHash("CHUNK_A");
+ private static final ChunkHash CHUNK_B_HASH = getHash("CHUNK_B");
+ private static final ChunkHash CHUNK_C_HASH = getHash("CHUNK_C");
private static final int CHUNK_A_LENGTH = 256;
private static final int CHUNK_B_LENGTH = 1024;
private static final int CHUNK_C_LENGTH = 4055;
- private ChunkHash mChunkHashA;
- private ChunkHash mChunkHashB;
- private ChunkHash mChunkHashC;
+ private static final int CHUNK_A_START = 0;
+ private static final int CHUNK_B_START = CHUNK_A_START + CHUNK_A_LENGTH;
+ private static final int CHUNK_C_START = CHUNK_B_START + CHUNK_B_LENGTH;
+
+ private ChunkListingMap mChunkListingMap;
@Before
- public void setUp() throws Exception {
- mChunkHashA = getHash(CHUNK_A);
- mChunkHashB = getHash(CHUNK_B);
- mChunkHashC = getHash(CHUNK_C);
+ public void setUp() {
+ mChunkListingMap = createFromFixture();
}
@Test
- public void testHasChunk_whenChunkInListing_returnsTrue() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
-
- boolean chunkAInList = chunkListingMap.hasChunk(mChunkHashA);
- boolean chunkBInList = chunkListingMap.hasChunk(mChunkHashB);
- boolean chunkCInList = chunkListingMap.hasChunk(mChunkHashC);
-
- assertThat(chunkAInList).isTrue();
- assertThat(chunkBInList).isTrue();
- assertThat(chunkCInList).isTrue();
+ public void hasChunk_isTrueForExistingChunks() {
+ assertThat(mChunkListingMap.hasChunk(CHUNK_A_HASH)).isTrue();
+ assertThat(mChunkListingMap.hasChunk(CHUNK_B_HASH)).isTrue();
+ assertThat(mChunkListingMap.hasChunk(CHUNK_C_HASH)).isTrue();
}
@Test
- public void testHasChunk_whenChunkNotInListing_returnsFalse() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH});
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
- ChunkHash chunkHashEmpty = getHash("");
-
- boolean chunkCInList = chunkListingMap.hasChunk(mChunkHashC);
- boolean emptyChunkInList = chunkListingMap.hasChunk(chunkHashEmpty);
-
- assertThat(chunkCInList).isFalse();
- assertThat(emptyChunkInList).isFalse();
+ public void hasChunk_isFalseForNonexistentChunks() {
+ assertThat(mChunkListingMap.hasChunk(getHash("CHUNK_D"))).isFalse();
+ assertThat(mChunkListingMap.hasChunk(getHash(""))).isFalse();
}
@Test
- public void testGetChunkEntry_returnsEntryWithCorrectLength() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
-
- ChunkListingMap.Entry entryA = chunkListingMap.getChunkEntry(mChunkHashA);
- ChunkListingMap.Entry entryB = chunkListingMap.getChunkEntry(mChunkHashB);
- ChunkListingMap.Entry entryC = chunkListingMap.getChunkEntry(mChunkHashC);
-
- assertThat(entryA.getLength()).isEqualTo(CHUNK_A_LENGTH);
- assertThat(entryB.getLength()).isEqualTo(CHUNK_B_LENGTH);
- assertThat(entryC.getLength()).isEqualTo(CHUNK_C_LENGTH);
+ public void getChunkListing_hasCorrectLengths() {
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_A_HASH).getLength())
+ .isEqualTo(CHUNK_A_LENGTH);
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_B_HASH).getLength())
+ .isEqualTo(CHUNK_B_LENGTH);
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_C_HASH).getLength())
+ .isEqualTo(CHUNK_C_LENGTH);
}
@Test
- public void testGetChunkEntry_returnsEntryWithCorrectStart() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
-
- ChunkListingMap.Entry entryA = chunkListingMap.getChunkEntry(mChunkHashA);
- ChunkListingMap.Entry entryB = chunkListingMap.getChunkEntry(mChunkHashB);
- ChunkListingMap.Entry entryC = chunkListingMap.getChunkEntry(mChunkHashC);
-
- assertThat(entryA.getStart()).isEqualTo(0);
- assertThat(entryB.getStart()).isEqualTo(CHUNK_A_LENGTH);
- assertThat(entryC.getStart()).isEqualTo(CHUNK_A_LENGTH + CHUNK_B_LENGTH);
+ public void getChunkListing_hasCorrectStarts() {
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_A_HASH).getStart())
+ .isEqualTo(CHUNK_A_START);
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_B_HASH).getStart())
+ .isEqualTo(CHUNK_B_START);
+ assertThat(mChunkListingMap.getChunkEntry(CHUNK_C_HASH).getStart())
+ .isEqualTo(CHUNK_C_START);
}
@Test
- public void testGetChunkEntry_returnsNullForNonExistentChunk() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH});
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
-
- ChunkListingMap.Entry chunkEntryNonexistentChunk =
- chunkListingMap.getChunkEntry(mChunkHashC);
-
- assertThat(chunkEntryNonexistentChunk).isNull();
+ public void getChunkListing_isNullForNonExistentChunks() {
+ assertThat(mChunkListingMap.getChunkEntry(getHash("Hey"))).isNull();
}
- @Test
- public void testReadFromProto_whenEmptyProto_returnsChunkListingMapWith0Chunks()
- throws Exception {
- ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {}));
-
- ChunkListingMap chunkListingMap = ChunkListingMap.readFromProto(emptyProto);
-
- assertThat(chunkListingMap.getChunkCount()).isEqualTo(0);
- }
-
- @Test
- public void testReadFromProto_returnsChunkListingWithCorrectSize() throws Exception {
- byte[] chunkListingProto =
- createChunkListingProto(
- new ChunkHash[] {mChunkHashA, mChunkHashB, mChunkHashC},
- new int[] {CHUNK_A_LENGTH, CHUNK_B_LENGTH, CHUNK_C_LENGTH});
-
- ChunkListingMap chunkListingMap =
- ChunkListingMap.readFromProto(
- new ProtoInputStream(new ByteArrayInputStream(chunkListingProto)));
-
- assertThat(chunkListingMap.getChunkCount()).isEqualTo(3);
+ private static ChunkListingMap createFromFixture() {
+ ChunksMetadataProto.ChunkListing chunkListing = new ChunksMetadataProto.ChunkListing();
+ chunkListing.chunks = new ChunksMetadataProto.Chunk[3];
+ chunkListing.chunks[0] = newChunk(CHUNK_A_HASH.getHash(), CHUNK_A_LENGTH);
+ chunkListing.chunks[1] = newChunk(CHUNK_B_HASH.getHash(), CHUNK_B_LENGTH);
+ chunkListing.chunks[2] = newChunk(CHUNK_C_HASH.getHash(), CHUNK_C_LENGTH);
+ return ChunkListingMap.fromProto(chunkListing);
}
- private byte[] createChunkListingProto(ChunkHash[] hashes, int[] lengths) {
- Preconditions.checkArgument(hashes.length == lengths.length);
- ProtoOutputStream outputStream = new ProtoOutputStream();
-
- for (int i = 0; i < hashes.length; ++i) {
- writeToProtoOutputStream(outputStream, hashes[i], lengths[i]);
- }
- outputStream.flush();
-
- return outputStream.getBytes();
- }
-
- private void writeToProtoOutputStream(ProtoOutputStream out, ChunkHash chunkHash, int length) {
- long token = out.start(ChunksMetadataProto.ChunkListing.CHUNKS);
- out.write(ChunksMetadataProto.Chunk.HASH, chunkHash.getHash());
- out.write(ChunksMetadataProto.Chunk.LENGTH, length);
- out.end(token);
- }
-
- private ChunkHash getHash(String name) {
+ private static ChunkHash getHash(String name) {
return new ChunkHash(
Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES));
}
+
+ public static ChunksMetadataProto.Chunk newChunk(byte[] hash, int length) {
+ ChunksMetadataProto.Chunk newChunk = new ChunksMetadataProto.Chunk();
+ newChunk.hash = Arrays.copyOf(hash, hash.length);
+ newChunk.length = length;
+ return newChunk;
+ }
}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.java
deleted file mode 100644
index 1796f56ce17a..000000000000
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.backup.encryption.chunk;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-
-import com.google.common.base.Charsets;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.io.ByteArrayInputStream;
-import java.util.Arrays;
-
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class ChunkTest {
- private static final String CHUNK_A = "CHUNK_A";
- private static final int CHUNK_A_LENGTH = 256;
-
- private ChunkHash mChunkHashA;
-
- @Before
- public void setUp() throws Exception {
- mChunkHashA = getHash(CHUNK_A);
- }
-
- @Test
- public void testReadFromProto_readsCorrectly() throws Exception {
- ProtoOutputStream out = new ProtoOutputStream();
- out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash());
- out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH);
- out.flush();
- byte[] protoBytes = out.getBytes();
-
- Chunk chunk =
- Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
-
- assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash());
- assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH);
- }
-
- @Test
- public void testReadFromProto_whenFieldsWrittenInReversedOrder_readsCorrectly()
- throws Exception {
- ProtoOutputStream out = new ProtoOutputStream();
- // Write fields of Chunk proto in reverse order.
- out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH);
- out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash());
- out.flush();
- byte[] protoBytes = out.getBytes();
-
- Chunk chunk =
- Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
-
- assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash());
- assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH);
- }
-
- @Test
- public void testReadFromProto_whenEmptyProto_returnsEmptyHash() throws Exception {
- ProtoInputStream emptyProto = new ProtoInputStream(new ByteArrayInputStream(new byte[] {}));
-
- Chunk chunk = Chunk.readFromProto(emptyProto);
-
- assertThat(chunk.getHash()).asList().hasSize(0);
- assertThat(chunk.getLength()).isEqualTo(0);
- }
-
- @Test
- public void testReadFromProto_whenOnlyHashSet_returnsChunkWithOnlyHash() throws Exception {
- ProtoOutputStream out = new ProtoOutputStream();
- out.write(ChunksMetadataProto.Chunk.HASH, mChunkHashA.getHash());
- out.flush();
- byte[] protoBytes = out.getBytes();
-
- Chunk chunk =
- Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
-
- assertThat(chunk.getHash()).isEqualTo(mChunkHashA.getHash());
- assertThat(chunk.getLength()).isEqualTo(0);
- }
-
- @Test
- public void testReadFromProto_whenOnlyLengthSet_returnsChunkWithOnlyLength() throws Exception {
- ProtoOutputStream out = new ProtoOutputStream();
- out.write(ChunksMetadataProto.Chunk.LENGTH, CHUNK_A_LENGTH);
- out.flush();
- byte[] protoBytes = out.getBytes();
-
- Chunk chunk =
- Chunk.readFromProto(new ProtoInputStream(new ByteArrayInputStream(protoBytes)));
-
- assertThat(chunk.getHash()).isEqualTo(new byte[] {});
- assertThat(chunk.getLength()).isEqualTo(CHUNK_A_LENGTH);
- }
-
- private ChunkHash getHash(String name) {
- return new ChunkHash(
- Arrays.copyOf(name.getBytes(Charsets.UTF_8), ChunkHash.HASH_LENGTH_BYTES));
- }
-}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
index 634acdc42eaf..7e1fdedcac80 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.mock;
import android.platform.test.annotations.Presubmit;
import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
index d231603e18b1..6f58ee148b83 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.mock;
import android.platform.test.annotations.Presubmit;
import com.android.server.backup.encryption.chunk.ChunkHash;
-import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.backup.encryption.protos.nano.ChunksMetadataProto;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index 27146fbac789..264b7d52c02f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -25,8 +25,6 @@ import dagger.Component;
modules = {
DependencyProvider.class,
DependencyBinder.class,
- ServiceBinder.class,
- SystemUIBinder.class,
SystemUIFactory.ContextHolder.class,
SystemUIModule.class,
CarSystemUIModule.class
diff --git a/packages/CarrierDefaultApp/tests/unit/Android.bp b/packages/CarrierDefaultApp/tests/unit/Android.bp
index 96144cf76ff7..5655abb046b2 100644
--- a/packages/CarrierDefaultApp/tests/unit/Android.bp
+++ b/packages/CarrierDefaultApp/tests/unit/Android.bp
@@ -17,7 +17,6 @@ android_test {
certificate: "platform",
libs: [
"android.test.runner",
- "telephony-common",
"android.test.base",
],
static_libs: [
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 8c0108dace69..602fe3ec12fd 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -1264,9 +1264,7 @@ public class BugreportProgressService extends Service {
}
return new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
.addExtras(sNotificationBundle)
- .setSmallIcon(
- isTv(context) ? R.drawable.ic_bug_report_black_24dp
- : com.android.internal.R.drawable.stat_sys_adb)
+ .setSmallIcon(R.drawable.ic_bug_report_black_24dp)
.setLocalOnly(true)
.setColor(context.getColor(
com.android.internal.R.color.system_notification_accent_color))
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d674be4c8fc0..37fefc2d37d7 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -76,6 +76,16 @@ android_library {
plugins: ["dagger2-compiler-2.19"],
}
+filegroup {
+ name: "SystemUI-tests-utils",
+ srcs: [
+ "tests/src/com/android/systemui/statusbar/NotificationEntryBuilder.java",
+ "tests/src/com/android/systemui/statusbar/RankingBuilder.java",
+ "tests/src/com/android/systemui/statusbar/SbnBuilder.java",
+ ],
+ path: "tests/src",
+}
+
android_library {
name: "SystemUI-tests",
manifest: "tests/AndroidManifest.xml",
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
index 60435d0dec35..d62c1d411cff 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/SensorManagerPlugin.java
@@ -22,7 +22,7 @@ import com.android.systemui.plugins.annotations.ProvidesInterface;
/**
* Allows for additional sensors to be retrieved from
- * {@link com.android.systemui.util.AsyncSensorManager}.
+ * {@link com.android.systemui.util.sensors.AsyncSensorManager}.
*/
@ProvidesInterface(action = SensorManagerPlugin.ACTION, version = SensorManagerPlugin.VERSION)
public interface SensorManagerPlugin extends Plugin {
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index 925e4fad103d..a1006a8396e0 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -42,7 +42,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="24dp"
- android:paddingBottom="48dp"
android:paddingTop="8dp"
android:gravity="@integer/biometric_dialog_text_gravity"
android:textSize="16sp"
@@ -52,6 +51,7 @@
android:id="@+id/biometric_icon"
android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
+ android:paddingTop="48dp"
android:layout_gravity="center_horizontal"
android:scaleType="fitXY" />
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 82287873f5ad..00e8b535ccac 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -225,14 +225,16 @@ public class ActivityManagerWrapper {
runner = new IRecentsAnimationRunner.Stub() {
@Override
public void onAnimationStart(IRecentsAnimationController controller,
- RemoteAnimationTarget[] apps, Rect homeContentInsets,
- Rect minimizedHomeBounds) {
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ Rect homeContentInsets, Rect minimizedHomeBounds) {
final RecentsAnimationControllerCompat controllerCompat =
new RecentsAnimationControllerCompat(controller);
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
+ final RemoteAnimationTargetCompat[] wallpapersCompat =
+ RemoteAnimationTargetCompat.wrap(wallpapers);
animationHandler.onAnimationStart(controllerCompat, appsCompat,
- homeContentInsets, minimizedHomeBounds);
+ wallpapersCompat, homeContentInsets, minimizedHomeBounds);
}
@Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
index 3ae2df5b97bf..2797042ac160 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
@@ -16,9 +16,10 @@
package com.android.systemui.shared.system;
+import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.graphics.Rect;
-import android.os.RemoteException;
+import android.view.DisplayInfo;
import android.view.IPinnedStackController;
import android.view.IPinnedStackListener;
@@ -32,62 +33,132 @@ import java.util.List;
* previously set listener.
*/
public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub {
- private List<IPinnedStackListener> mListeners = new ArrayList<>();
+ private List<PinnedStackListener> mListeners = new ArrayList<>();
/** Adds a listener to receive updates from the WindowManagerService. */
- public void addListener(IPinnedStackListener listener) {
+ public void addListener(PinnedStackListener listener) {
mListeners.add(listener);
}
/** Removes a listener so it will no longer receive updates from the WindowManagerService. */
- public void removeListener(IPinnedStackListener listener) {
+ public void removeListener(PinnedStackListener listener) {
mListeners.remove(listener);
}
@Override
- public void onListenerRegistered(IPinnedStackController controller) throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
+ public void onListenerRegistered(IPinnedStackController controller) {
+ for (PinnedStackListener listener : mListeners) {
listener.onListenerRegistered(controller);
}
}
@Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
- boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation)
- throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
- listener.onMovementBoundsChanged(
- insetBounds, normalBounds, animatingBounds,
- fromImeAdjustment, fromShelfAdjustment, displayRotation);
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment,
+ fromShelfAdjustment);
}
}
@Override
- public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+ for (PinnedStackListener listener : mListeners) {
listener.onImeVisibilityChanged(imeVisible, imeHeight);
}
}
@Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight)
- throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
+ public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
+ for (PinnedStackListener listener : mListeners) {
listener.onShelfVisibilityChanged(shelfVisible, shelfHeight);
}
}
@Override
- public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
+ public void onMinimizedStateChanged(boolean isMinimized) {
+ for (PinnedStackListener listener : mListeners) {
listener.onMinimizedStateChanged(isMinimized);
}
}
@Override
- public void onActionsChanged(ParceledListSlice actions) throws RemoteException {
- for (IPinnedStackListener listener : mListeners) {
+ public void onActionsChanged(ParceledListSlice actions) {
+ for (PinnedStackListener listener : mListeners) {
listener.onActionsChanged(actions);
}
}
+
+ @Override
+ public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onSaveReentrySnapFraction(componentName, bounds);
+ }
+ }
+
+ @Override
+ public void onResetReentrySnapFraction(ComponentName componentName) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onResetReentrySnapFraction(componentName);
+ }
+ }
+
+ @Override
+ public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onDisplayInfoChanged(displayInfo);
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged() {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onConfigurationChanged();
+ }
+ }
+
+ @Override
+ public void onAspectRatioChanged(float aspectRatio) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onAspectRatioChanged(aspectRatio);
+ }
+ }
+
+ @Override
+ public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
+ for (PinnedStackListener listener : mListeners) {
+ listener.onPrepareAnimation(sourceRectHint, aspectRatio, bounds);
+ }
+ }
+
+ /**
+ * A counterpart of {@link IPinnedStackListener} with empty implementations.
+ * Subclasses can ignore those methods they do not intend to take action upon.
+ */
+ public static class PinnedStackListener {
+ public void onListenerRegistered(IPinnedStackController controller) {}
+
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment) {}
+
+ public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {}
+
+ public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {}
+
+ public void onMinimizedStateChanged(boolean isMinimized) {}
+
+ public void onActionsChanged(ParceledListSlice actions) {}
+
+ public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {}
+
+ public void onResetReentrySnapFraction(ComponentName componentName) {}
+
+ public void onDisplayInfoChanged(DisplayInfo displayInfo) {}
+
+ public void onConfigurationChanged() {}
+
+ public void onAspectRatioChanged(float aspectRatio) {}
+
+ public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {}
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index 579858a4f9b4..2c99c5c84015 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -21,12 +21,12 @@ import android.graphics.Rect;
import com.android.systemui.shared.recents.model.ThumbnailData;
public interface RecentsAnimationListener {
-
/**
* Called when the animation into Recents can start. This call is made on the binder thread.
*/
void onAnimationStart(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds);
+ RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
+ Rect homeContentInsets, Rect minimizedHomeBounds);
/**
* Called when the animation into Recents was canceled. This call is made on the binder thread.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 61be076ac48a..02e509eef25f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -45,9 +45,12 @@ public class RemoteAnimationAdapterCompat {
return new IRemoteAnimationRunner.Stub() {
@Override
public void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
final IRemoteAnimationFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
+ final RemoteAnimationTargetCompat[] wallpapersCompat =
+ RemoteAnimationTargetCompat.wrap(wallpapers);
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
@@ -59,7 +62,8 @@ public class RemoteAnimationAdapterCompat {
}
}
};
- remoteAnimationAdapter.onAnimationStart(appsCompat, animationFinishedCallback);
+ remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
+ animationFinishedCallback);
}
@Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 5a85df967197..33372f6bd0b9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -17,6 +17,7 @@
package com.android.systemui.shared.system;
public interface RemoteAnimationRunnerCompat {
- void onAnimationStart(RemoteAnimationTargetCompat[] apps, Runnable finishedCallback);
+ void onAnimationStart(RemoteAnimationTargetCompat[] apps,
+ RemoteAnimationTargetCompat[] wallpapers, Runnable finishedCallback);
void onAnimationCancelled();
} \ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 221782e950d0..ca88f13932ad 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -68,7 +68,7 @@ public class RemoteAnimationTargetCompat {
public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
final RemoteAnimationTargetCompat[] appsCompat =
- new RemoteAnimationTargetCompat[apps.length];
+ new RemoteAnimationTargetCompat[apps != null ? apps.length : 0];
for (int i = 0; i < apps.length; i++) {
appsCompat[i] = new RemoteAnimationTargetCompat(apps[i]);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 9ba21a328ca4..e80b43739557 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -142,7 +142,9 @@ public class SyncRtSurfaceTransactionApplierCompat {
public static void applyParams(TransactionCompat t,
SyncRtSurfaceTransactionApplierCompat.SurfaceParams params) {
t.setMatrix(params.surface, params.matrix);
- t.setWindowCrop(params.surface, params.windowCrop);
+ if (params.windowCrop != null) {
+ t.setWindowCrop(params.surface, params.windowCrop);
+ }
t.setAlpha(params.surface, params.alpha);
t.setLayer(params.surface, params.layer);
t.setCornerRadius(params.surface, params.cornerRadius);
@@ -187,14 +189,14 @@ public class SyncRtSurfaceTransactionApplierCompat {
* @param surface The surface to modify.
* @param alpha Alpha to apply.
* @param matrix Matrix to apply.
- * @param windowCrop Crop to apply.
+ * @param windowCrop Crop to apply, only applied if not {@code null}
*/
public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
Rect windowCrop, int layer, float cornerRadius) {
this.surface = surface;
this.alpha = alpha;
this.matrix = new Matrix(matrix);
- this.windowCrop = new Rect(windowCrop);
+ this.windowCrop = windowCrop != null ? new Rect(windowCrop) : null;
this.layer = layer;
this.cornerRadius = cornerRadius;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 794c30a7c7c1..9f1a1fafeec6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -27,12 +27,12 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
-import android.view.IPinnedStackListener;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
public class WindowManagerWrapper {
@@ -212,7 +212,7 @@ public class WindowManagerWrapper {
* Adds a pinned stack listener, which will receive updates from the window manager service
* along with any other pinned stack listeners that were added via this method.
*/
- public void addPinnedStackListener(IPinnedStackListener listener) throws RemoteException {
+ public void addPinnedStackListener(PinnedStackListener listener) throws RemoteException {
mPinnedStackListenerForwarder.addListener(listener);
WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
DEFAULT_DISPLAY, mPinnedStackListenerForwarder);
@@ -221,7 +221,7 @@ public class WindowManagerWrapper {
/**
* Removes a pinned stack listener.
*/
- public void removePinnedStackListener(IPinnedStackListener listener) {
+ public void removePinnedStackListener(PinnedStackListener listener) {
mPinnedStackListenerForwarder.removeListener(listener);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityBinder.java b/packages/SystemUI/src/com/android/systemui/ActivityBinder.java
new file mode 100644
index 000000000000..2c8a67270d94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ActivityBinder.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.app.Activity;
+
+import com.android.systemui.tuner.TunerActivity;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * Services and Activities that are injectable should go here.
+ */
+@Module
+public abstract class ActivityBinder {
+ /** Inject into TunerActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(TunerActivity.class)
+ public abstract Activity bindTunerActivity(TunerActivity activity);
+
+ /** Inject into ForegroundServicesDialog. */
+ @Binds
+ @IntoMap
+ @ClassKey(ForegroundServicesDialog.class)
+ public abstract Activity bindForegroundServicesDialog(ForegroundServicesDialog activity);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ComponentBinder.java b/packages/SystemUI/src/com/android/systemui/ComponentBinder.java
new file mode 100644
index 000000000000..3b35c61e8eb2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ComponentBinder.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Dagger Module that collects related sub-modules together.
+ */
+@Module(includes = {ActivityBinder.class, ServiceBinder.class, SystemUIBinder.class})
+public abstract class ComponentBinder {
+ /** */
+ @Binds
+ public abstract ContextComponentHelper bindComponentHelper(
+ ContextComponentResolver componentHelper);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
index 5fe5792219c3..2cf0f8dafcad 100644
--- a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.app.Activity;
import android.app.Service;
/**
@@ -23,6 +24,9 @@ import android.app.Service;
*/
public interface ContextComponentHelper {
/** Turns a classname into an instance of the class or returns null. */
+ Activity resolveActivity(String className);
+
+ /** Turns a classname into an instance of the class or returns null. */
Service resolveService(String className);
/** Turns a classname into an instance of the class or returns null. */
diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java
index cee21c167fa0..995263240e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.app.Activity;
import android.app.Service;
import java.util.Map;
@@ -29,18 +30,29 @@ import javax.inject.Singleton;
*/
@Singleton
public class ContextComponentResolver implements ContextComponentHelper {
+ private final Map<Class<?>, Provider<Activity>> mActivityCreators;
private final Map<Class<?>, Provider<Service>> mServiceCreators;
private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
@Inject
ContextComponentResolver(
+ Map<Class<?>, Provider<Activity>> activityCreators,
Map<Class<?>, Provider<Service>> serviceCreators,
Map<Class<?>, Provider<SystemUI>> systemUICreators) {
+ mActivityCreators = activityCreators;
mServiceCreators = serviceCreators;
mSystemUICreators = systemUICreators;
}
/**
+ * Looks up the Activity class name to see if Dagger has an instance of it.
+ */
+ @Override
+ public Activity resolveActivity(String className) {
+ return resolve(className, mActivityCreators);
+ }
+
+ /**
* Looks up the Service class name to see if Dagger has an instance of it.
*/
@Override
@@ -57,12 +69,12 @@ public class ContextComponentResolver implements ContextComponentHelper {
}
private <T> T resolve(String className, Map<Class<?>, Provider<T>> creators) {
- for (Map.Entry<Class<?>, Provider<T>> p : creators.entrySet()) {
- if (p.getKey().getName().equals(className)) {
- return p.getValue().get();
- }
+ try {
+ Class<?> clazz = Class.forName(className);
+ Provider<T> provider = creators.get(clazz);
+ return provider == null ? null : provider.get();
+ } catch (ClassNotFoundException e) {
+ return null;
}
-
- return null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
index 6209c2c36206..a94952c5bc19 100644
--- a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
@@ -19,7 +19,6 @@ package com.android.systemui;
import android.animation.ArgbEvaluator;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
@@ -28,7 +27,6 @@ import android.util.DisplayMetrics;
import android.view.ContextThemeWrapper;
import android.view.View;
-import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
/**
@@ -109,7 +107,6 @@ public class CornerHandleView extends View {
mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
mLightColor,
mDarkColor));
- updateShadow();
if (getVisibility() == VISIBLE) {
invalidate();
}
@@ -121,21 +118,6 @@ public class CornerHandleView extends View {
canvas.drawPath(mPath, mPaint);
}
- private void updateShadow() {
- if (ColorUtils.calculateLuminance(mPaint.getColor()) > 0.7f) {
- mPaint.setShadowLayer(/** radius */ 5,/** shadowDx */ 0, /** shadowDy */ -1,
- /** color */ ColorUtils.setAlphaComponent(/** color */ Color.BLACK,
- /** alpha */ 102));
- } else {
- mPaint.setShadowLayer(/** radius */ 0, /** shadowDx */ 0, /** shadowDy */ 0,
- /** color */ Color.TRANSPARENT);
- }
-
- if (getVisibility() == VISIBLE) {
- invalidate();
- }
- }
-
private static float convertDpToPixel(float dp, Context context) {
return dp * ((float) context.getResources().getDisplayMetrics().densityDpi
/ DisplayMetrics.DENSITY_DEFAULT);
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index bd5b9c724eb3..7771f8655128 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -15,6 +15,7 @@
package com.android.systemui;
import android.annotation.Nullable;
+import android.app.AlarmManager;
import android.app.INotificationManager;
import android.content.res.Configuration;
import android.hardware.SensorPrivacyManager;
@@ -110,10 +111,10 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.AsyncSensorManager;
import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.leak.LeakReporter;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -314,24 +315,15 @@ public class Dependency {
@Inject Lazy<INotificationManager> mINotificationManager;
@Inject Lazy<FalsingManager> mFalsingManager;
@Inject Lazy<SysUiState> mSysUiStateFlagsContainer;
+ @Inject Lazy<AlarmManager> mAlarmManager;
@Inject
public Dependency() {
}
-
/**
* Initialize Depenency.
*/
- public static void initDependencies(SystemUIRootComponent rootComponent) {
- if (sDependency != null) {
- return;
- }
- sDependency = new Dependency();
- rootComponent.createDependency().createSystemUI(sDependency);
- sDependency.start();
- }
-
protected void start() {
// TODO: Think about ways to push these creation rules out of Dependency to cut down
// on imports.
@@ -508,6 +500,7 @@ public class Dependency {
mProviders.put(INotificationManager.class, mINotificationManager::get);
mProviders.put(FalsingManager.class, mFalsingManager::get);
mProviders.put(SysUiState.class, mSysUiStateFlagsContainer::get);
+ mProviders.put(AlarmManager.class, mAlarmManager::get);
// TODO(b/118592525): to support multi-display , we start to add something which is
// per-display, while others may be global. I think it's time to add
diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
index d46a86cc728f..239cbfe38975 100644
--- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/DependencyProvider.java
@@ -23,6 +23,7 @@ import static com.android.systemui.Dependency.MAIN_LOOPER_NAME;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.annotation.Nullable;
+import android.app.AlarmManager;
import android.app.INotificationManager;
import android.content.Context;
import android.hardware.SensorPrivacyManager;
@@ -230,4 +231,11 @@ public class DependencyProvider {
@Named(MAIN_HANDLER_NAME) Handler mainHandler) {
return new DeviceProvisionedControllerImpl(context, mainHandler);
}
+
+ /** */
+ @Singleton
+ @Provides
+ public AlarmManager provideAlarmManager(Context context) {
+ return context.getSystemService(AlarmManager.class);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
index 6fec92c84fec..710980a274a2 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServicesDialog.java
@@ -44,6 +44,8 @@ import com.android.internal.logging.nano.MetricsProto;
import java.util.ArrayList;
+import javax.inject.Inject;
+
/**
* Show a list of currently running foreground services (supplied by the caller)
* that the user can tap through to their application details.
@@ -72,10 +74,14 @@ public final class ForegroundServicesDialog extends AlertActivity implements
}
};
+ @Inject
+ ForegroundServicesDialog() {
+ super();
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Dependency.initDependencies(SystemUIFactory.getInstance().getRootComponent());
mMetricsLogger = Dependency.get(MetricsLogger.class);
diff --git a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java b/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
index 131a0f8c81a9..e761a2be0b0f 100644
--- a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/ServiceBinder.java
@@ -26,15 +26,10 @@ import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/**
- * Services and Activities that are injectable should go here.
+ * Services that are injectable should go here.
*/
@Module
public abstract class ServiceBinder {
-
- @Binds
- public abstract ContextComponentHelper bindComponentHelper(
- ContextComponentResolver componentHelper);
-
@Binds
@IntoMap
@ClassKey(DozeService.class)
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
index 3eb330e04381..2c8324cafca0 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.app.Activity;
import android.app.Application;
import android.app.Service;
import android.content.ContentProvider;
@@ -23,6 +24,7 @@ import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.core.app.AppComponentFactory;
import javax.inject.Inject;
@@ -87,6 +89,18 @@ public class SystemUIAppComponentFactory extends AppComponentFactory {
@NonNull
@Override
+ public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className,
+ @Nullable Intent intent)
+ throws InstantiationException, IllegalAccessException, ClassNotFoundException {
+ Activity activity = mComponentHelper.resolveActivity(className);
+ if (activity != null) {
+ return activity;
+ }
+ return super.instantiateActivityCompat(cl, className, intent);
+ }
+
+ @NonNull
+ @Override
public Service instantiateServiceCompat(
@NonNull ClassLoader cl, @NonNull String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
index 88d659039b4b..4531c892a022 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
@@ -24,7 +24,7 @@ import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
/**
- * Services and Activities that are injectable should go here.
+ * SystemUI objects that are injectable should go here.
*/
@Module
public abstract class SystemUIBinder {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index cd16d85685de..8e693185ab7f 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -102,7 +102,10 @@ public class SystemUIFactory {
// Every other part of our codebase currently relies on Dependency, so we
// really need to ensure the Dependency gets initialized early on.
- Dependency.initDependencies(mRootComponent);
+
+ Dependency dependency = new Dependency();
+ mRootComponent.createDependency().createSystemUI(dependency);
+ dependency.start();
}
protected void initWithRootComponent(@NonNull SystemUIRootComponent rootComponent) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
index ff4eb83bd796..b0316e22de06 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIModule.java
@@ -24,7 +24,7 @@ import com.android.systemui.assist.AssistModule;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import javax.inject.Singleton;
@@ -35,7 +35,7 @@ import dagger.Provides;
* A dagger module for injecting components of System UI that are not overridden by the System UI
* implementation.
*/
-@Module(includes = {AssistModule.class})
+@Module(includes = {AssistModule.class, ComponentBinder.class})
public abstract class SystemUIModule {
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
index 6ce673b0e9b6..c70b2fc3292a 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
@@ -37,8 +37,6 @@ import dagger.Component;
@Component(modules = {
DependencyProvider.class,
DependencyBinder.class,
- ServiceBinder.class,
- SystemUIBinder.class,
SystemUIFactory.ContextHolder.class,
SystemUIModule.class,
SystemUIDefaultModule.class})
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 1c5e80005d84..8f1fcae8e0f7 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -84,7 +84,7 @@ public class SystemUIService extends Service {
}
} else {
String svc = args[0].toLowerCase();
- if (Dependency.class.getName().endsWith(svc)) {
+ if (Dependency.class.getName().toLowerCase().endsWith(svc)) {
Dependency.staticDump(fd, pw, args);
}
for (SystemUI ui: services) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
index a0d8b4c58f1a..ccca447ad842 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleLikeHomeBehavior.java
@@ -23,6 +23,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
import java.io.PrintWriter;
@@ -39,44 +40,55 @@ import dagger.Lazy;
@Singleton
final class AssistHandleLikeHomeBehavior implements BehaviorController {
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ handleDozingChanged(isDozing);
+ }
+ };
private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver =
new WakefulnessLifecycle.Observer() {
@Override
public void onStartedWakingUp() {
- handleDozingChanged(/* isDozing = */ true);
+ handleWakefullnessChanged(/* isAwake = */ false);
}
@Override
public void onFinishedWakingUp() {
- handleDozingChanged(/* isDozing = */ false);
+ handleWakefullnessChanged(/* isAwake = */ true);
}
@Override
public void onStartedGoingToSleep() {
- handleDozingChanged(/* isDozing = */ true);
+ handleWakefullnessChanged(/* isAwake = */ false);
}
@Override
public void onFinishedGoingToSleep() {
- handleDozingChanged(/* isDozing = */ true);
+ handleWakefullnessChanged(/* isAwake = */ false);
}
};
private final SysUiState.SysUiStateCallback mSysUiStateCallback =
this::handleSystemUiStateChange;
+ private final Lazy<StatusBarStateController> mStatusBarStateController;
private final Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
private final Lazy<SysUiState> mSysUiFlagContainer;
private boolean mIsDozing;
+ private boolean mIsAwake;
private boolean mIsHomeHandleHiding;
@Nullable private AssistHandleCallbacks mAssistHandleCallbacks;
@Inject
AssistHandleLikeHomeBehavior(
+ Lazy<StatusBarStateController> statusBarStateController,
Lazy<WakefulnessLifecycle> wakefulnessLifecycle,
Lazy<SysUiState> sysUiFlagContainer) {
+ mStatusBarStateController = statusBarStateController;
mWakefulnessLifecycle = wakefulnessLifecycle;
mSysUiFlagContainer = sysUiFlagContainer;
}
@@ -84,8 +96,10 @@ final class AssistHandleLikeHomeBehavior implements BehaviorController {
@Override
public void onModeActivated(Context context, AssistHandleCallbacks callbacks) {
mAssistHandleCallbacks = callbacks;
- mIsDozing = mWakefulnessLifecycle.get().getWakefulness()
- != WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+ mIsDozing = mStatusBarStateController.get().isDozing();
+ mStatusBarStateController.get().addCallback(mStatusBarStateListener);
+ mIsAwake = mWakefulnessLifecycle.get().getWakefulness()
+ == WakefulnessLifecycle.WAKEFULNESS_AWAKE;
mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver);
mSysUiFlagContainer.get().addCallback(mSysUiStateCallback);
callbackForCurrentState();
@@ -94,6 +108,7 @@ final class AssistHandleLikeHomeBehavior implements BehaviorController {
@Override
public void onModeDeactivated() {
mAssistHandleCallbacks = null;
+ mStatusBarStateController.get().removeCallback(mStatusBarStateListener);
mWakefulnessLifecycle.get().removeObserver(mWakefulnessLifecycleObserver);
mSysUiFlagContainer.get().removeCallback(mSysUiStateCallback);
}
@@ -111,6 +126,15 @@ final class AssistHandleLikeHomeBehavior implements BehaviorController {
callbackForCurrentState();
}
+ private void handleWakefullnessChanged(boolean isAwake) {
+ if (mIsAwake == isAwake) {
+ return;
+ }
+
+ mIsAwake = isAwake;
+ callbackForCurrentState();
+ }
+
private void handleSystemUiStateChange(int sysuiStateFlags) {
boolean isHomeHandleHiding = isHomeHandleHiding(sysuiStateFlags);
if (mIsHomeHandleHiding == isHomeHandleHiding) {
@@ -126,18 +150,23 @@ final class AssistHandleLikeHomeBehavior implements BehaviorController {
return;
}
- if (mIsHomeHandleHiding || mIsDozing) {
+ if (mIsHomeHandleHiding || !isFullyAwake()) {
mAssistHandleCallbacks.hide();
} else {
mAssistHandleCallbacks.showAndStay();
}
}
+ private boolean isFullyAwake() {
+ return mIsAwake && !mIsDozing;
+ }
+
@Override
public void dump(PrintWriter pw, String prefix) {
- pw.println("Current AssistHandleLikeHomeBehavior State:");
+ pw.println(prefix + "Current AssistHandleLikeHomeBehavior State:");
pw.println(prefix + " mIsDozing=" + mIsDozing);
+ pw.println(prefix + " mIsAwake=" + mIsAwake);
pw.println(prefix + " mIsHomeHandleHiding=" + mIsHomeHandleHiding);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index d371dbead42b..3ff1b97c3753 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -94,6 +94,11 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
public void onStateChanged(int newState) {
handleStatusBarStateChanged(newState);
}
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ handleDozingChanged(isDozing);
+ }
};
private final TaskStackChangeListener mTaskStackChangeListener =
new TaskStackChangeListener() {
@@ -119,15 +124,25 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private final WakefulnessLifecycle.Observer mWakefulnessLifecycleObserver =
new WakefulnessLifecycle.Observer() {
@Override
+ public void onStartedWakingUp() {
+ handleWakefullnessChanged(/* isAwake = */ false);
+ }
+
+ @Override
public void onFinishedWakingUp() {
- handleDozingChanged(false);
+ handleWakefullnessChanged(/* isAwake = */ true);
}
@Override
public void onStartedGoingToSleep() {
- handleDozingChanged(true);
+ handleWakefullnessChanged(/* isAwake = */ false);
}
- };
+
+ @Override
+ public void onFinishedGoingToSleep() {
+ handleWakefullnessChanged(/* isAwake = */ false);
+ }
+ };
private final BroadcastReceiver mDefaultHomeBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -149,6 +164,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
private boolean mOnLockscreen;
private boolean mIsDozing;
+ private boolean mIsAwake;
private int mRunningTaskId;
private boolean mIsNavBarHidden;
private boolean mIsLauncherShowing;
@@ -201,6 +217,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
mDefaultHome = getCurrentDefaultHome();
context.registerReceiver(mDefaultHomeBroadcastReceiver, mDefaultHomeIntentFilter);
mOnLockscreen = onLockscreen(mStatusBarStateController.get().getState());
+ mIsDozing = mStatusBarStateController.get().isDozing();
mStatusBarStateController.get().addCallback(mStatusBarStateListener);
ActivityManager.RunningTaskInfo runningTaskInfo =
mActivityManagerWrapper.get().getRunningTask();
@@ -208,8 +225,8 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
mActivityManagerWrapper.get().registerTaskStackListener(mTaskStackChangeListener);
mOverviewProxyService.get().addCallback(mOverviewProxyListener);
mSysUiFlagContainer.get().addCallback(mSysUiStateCallback);
- mIsDozing = mWakefulnessLifecycle.get().getWakefulness()
- != WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+ mIsAwake = mWakefulnessLifecycle.get().getWakefulness()
+ == WakefulnessLifecycle.WAKEFULNESS_AWAKE;
mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver);
mLearningTimeElapsed = Settings.Secure.getLong(
@@ -252,7 +269,10 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
@Override
public void onAssistHandlesRequested() {
- if (mAssistHandleCallbacks != null && !mIsDozing && !mIsNavBarHidden && !mOnLockscreen) {
+ if (mAssistHandleCallbacks != null
+ && isFullyAwake()
+ && !mIsNavBarHidden
+ && !mOnLockscreen) {
mAssistHandleCallbacks.showAndGo();
}
}
@@ -299,6 +319,16 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
callbackForCurrentState(/* justUnlocked = */ false);
}
+ private void handleWakefullnessChanged(boolean isAwake) {
+ if (mIsAwake == isAwake) {
+ return;
+ }
+
+ resetConsecutiveTaskSwitches();
+ mIsAwake = isAwake;
+ callbackForCurrentState(/* justUnlocked = */ false);
+ }
+
private void handleTaskStackTopChanged(int taskId, @Nullable ComponentName taskComponentName) {
if (mRunningTaskId == taskId || taskComponentName == null) {
return;
@@ -352,7 +382,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
return;
}
- if (mIsDozing || mIsNavBarHidden || mOnLockscreen || !getShowWhenTaught()) {
+ if (!isFullyAwake() || mIsNavBarHidden || mOnLockscreen || !getShowWhenTaught()) {
mAssistHandleCallbacks.hide();
} else if (justUnlocked) {
long currentEpochDay = LocalDate.now().toEpochDay();
@@ -374,7 +404,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
return;
}
- if (mIsDozing || mIsNavBarHidden || isSuppressed()) {
+ if (!isFullyAwake() || mIsNavBarHidden || isSuppressed()) {
mAssistHandleCallbacks.hide();
} else if (mOnLockscreen) {
mAssistHandleCallbacks.showAndStay();
@@ -425,6 +455,10 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
mHandler.postDelayed(mResetConsecutiveTaskSwitches, getShowAndGoDelayResetTimeoutMs());
}
+ private boolean isFullyAwake() {
+ return mIsAwake && !mIsDozing;
+ }
+
private long getLearningTimeMs() {
return mPhenotypeHelper.getLong(
SystemUiDeviceConfigFlags.ASSIST_HANDLES_LEARN_TIME_MS,
@@ -484,6 +518,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController {
pw.println(prefix + "Current AssistHandleReminderExpBehavior State:");
pw.println(prefix + " mOnLockscreen=" + mOnLockscreen);
pw.println(prefix + " mIsDozing=" + mIsDozing);
+ pw.println(prefix + " mIsAwake=" + mIsAwake);
pw.println(prefix + " mRunningTaskId=" + mRunningTaskId);
pw.println(prefix + " mDefaultHome=" + mDefaultHome);
pw.println(prefix + " mIsNavBarHidden=" + mIsNavBarHidden);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index e4d2005c8867..73bbce9c5b35 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -608,7 +608,10 @@ public abstract class AuthBiometricView extends LinearLayout {
MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
- totalHeight += child.getMeasuredHeight();
+
+ if (child.getVisibility() != View.GONE) {
+ totalHeight += child.getMeasuredHeight();
+ }
}
// Use the new width so it's centered horizontally
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 67fc3e32883f..72ada6e90cd0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -48,12 +48,10 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.provider.Settings;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.ZenModeConfig;
import android.util.ArraySet;
@@ -61,8 +59,6 @@ import android.util.Log;
import android.util.Pair;
import android.util.SparseSetArray;
import android.view.Display;
-import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -76,6 +72,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -131,9 +128,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
public static final int MAX_BUBBLES = 5; // TODO: actually enforce this
- /** Flag to enable or disable the entire feature */
- private static final String ENABLE_BUBBLES = "experiment_enable_bubbles";
-
private final Context mContext;
private final NotificationEntryManager mNotificationEntryManager;
private final BubbleTaskStackListener mTaskStackListener;
@@ -560,7 +554,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Need to check for !appCancel here because the notification may have
// previously been dismissed & entry.isRowDismissed would still be true
- boolean userRemovedNotif = (entry.isRowDismissed() && !isAppCancel)
+ boolean userRemovedNotif = (entry != null && entry.isRowDismissed() && !isAppCancel)
|| isClearAll || isUserDimiss || isSummaryCancel;
if (isSummaryOfBubbles) {
@@ -570,7 +564,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// The bubble notification sticks around in the data as long as the bubble is
// not dismissed and the app hasn't cancelled the notification.
Bubble bubble = mBubbleData.getBubbleWithKey(key);
- boolean bubbleExtended = entry.isBubble() && userRemovedNotif;
+ boolean bubbleExtended = entry != null && entry.isBubble() && userRemovedNotif;
if (bubbleExtended) {
bubble.setShowInShadeWhenBubble(false);
bubble.setShowBubbleDot(false);
@@ -579,7 +573,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
mNotificationEntryManager.updateNotifications();
return true;
- } else if (!userRemovedNotif) {
+ } else if (!userRemovedNotif && entry != null) {
// This wasn't a user removal so we should remove the bubble as well
mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
return false;
@@ -638,9 +632,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
- if (!areBubblesEnabled(mContext)) {
- return;
- }
if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
&& canLaunchInActivityView(mContext, entry)) {
updateBubble(entry);
@@ -649,9 +640,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
- if (!areBubblesEnabled(mContext)) {
- return;
- }
boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
&& canLaunchInActivityView(mContext, entry);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.key)) {
@@ -926,7 +914,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void onSingleTaskDisplayDrawn(int displayId) {
- final Bubble expandedBubble = getExpandedBubble(mContext);
+ final Bubble expandedBubble = mStackView != null
+ ? mStackView.getExpandedBubble()
+ : null;
if (expandedBubble != null && expandedBubble.getDisplayId() == displayId) {
expandedBubble.setContentVisibility(true);
}
@@ -934,22 +924,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void onSingleTaskDisplayEmpty(int displayId) {
- final Bubble expandedBubble = getExpandedBubble(mContext);
- if (expandedBubble == null) {
- return;
- }
- if (expandedBubble.getDisplayId() == displayId) {
+ final Bubble expandedBubble = mStackView != null
+ ? mStackView.getExpandedBubble()
+ : null;
+ int expandedId = expandedBubble != null ? expandedBubble.getDisplayId() : -1;
+ if (mStackView != null && mStackView.isExpanded() && expandedId == displayId) {
mBubbleData.setExpanded(false);
- expandedBubble.getExpandedView().notifyDisplayEmpty();
}
+ mBubbleData.notifyDisplayEmpty(displayId);
}
}
- private static boolean areBubblesEnabled(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- ENABLE_BUBBLES, 1) != 0;
- }
-
/**
* Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
*
@@ -993,32 +978,12 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/** PinnedStackListener that dispatches IME visibility updates to the stack. */
- private class BubblesImeListener extends IPinnedStackListener.Stub {
-
- @Override
- public void onListenerRegistered(IPinnedStackController controller) throws RemoteException {
- }
-
- @Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
- int displayRotation) throws RemoteException {}
-
+ private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
if (mStackView != null && mStackView.getBubbleCount() > 0) {
mStackView.post(() -> mStackView.onImeVisibilityChanged(imeVisible, imeHeight));
}
}
-
- @Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight)
- throws RemoteException {}
-
- @Override
- public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException {}
-
- @Override
- public void onActionsChanged(ParceledListSlice actions) throws RemoteException {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 81c8da8247ec..d43e030ed9eb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -184,6 +184,8 @@ public class BubbleData {
Log.d(TAG, "notificationEntryUpdated: " + entry);
}
Bubble bubble = getBubbleWithKey(entry.key);
+ suppressFlyout = !entry.isVisuallyInterruptive || suppressFlyout;
+
if (bubble == null) {
// Create a new bubble
bubble = new Bubble(mContext, entry);
@@ -193,8 +195,10 @@ public class BubbleData {
} else {
// Updates an existing bubble
bubble.updateEntry(entry);
+ bubble.setSuppressFlyout(suppressFlyout);
doUpdate(bubble);
}
+
if (bubble.shouldAutoExpand()) {
setSelectedBubbleInternal(bubble);
if (!mExpanded) {
@@ -392,6 +396,22 @@ public class BubbleData {
dispatchPendingChanges();
}
+ /**
+ * Indicates that the provided display is no longer in use and should be cleaned up.
+ *
+ * @param displayId the id of the display to clean up.
+ */
+ void notifyDisplayEmpty(int displayId) {
+ for (Bubble b : mBubbles) {
+ if (b.getDisplayId() == displayId) {
+ if (b.getExpandedView() != null) {
+ b.getExpandedView().notifyDisplayEmpty();
+ }
+ return;
+ }
+ }
+ }
+
private void dispatchPendingChanges() {
if (mListener != null && mStateChange.anythingChanged()) {
mListener.applyUpdate(mStateChange);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index be10dc565159..521ebde7d2f0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -105,6 +105,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
private Drawable mAppIcon;
private BubbleController mBubbleController = Dependency.get(BubbleController.class);
+ private WindowManager mWindowManager;
private BubbleStackView mStackView;
@@ -202,9 +203,9 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
super(context, attrs, defStyleAttr, defStyleRes);
mPm = context.getPackageManager();
mDisplaySize = new Point();
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// Get the real size -- this includes screen decorations (notches, statusbar, navbar).
- wm.getDefaultDisplay().getRealSize(mDisplaySize);
+ mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Resources res = getResources();
mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
@@ -326,7 +327,10 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
if (usingActivityView()) {
int[] screenLoc = mActivityView.getLocationOnScreen();
final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
- final int keyboardTop = mDisplaySize.y - insets.getSystemWindowInsetBottom();
+ final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
+ insets.getDisplayCutout() != null
+ ? insets.getDisplayCutout().getSafeInsetBottom()
+ : 0);
final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
}
@@ -444,6 +448,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
private int getMaxExpandedHeight() {
+ mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
int[] windowLocation = mActivityView.getLocationOnScreen();
int bottomInset = getRootWindowInsets() != null
? getRootWindowInsets().getStableInsetBottom()
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index cc250b46c040..31cf853dce04 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -45,6 +45,7 @@ import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.util.StatsLog;
import android.view.Choreographer;
+import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -340,7 +341,8 @@ public class BubbleStackView extends FrameLayout {
mDisplaySize = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getSize(mDisplaySize);
+ // We use the real size & subtract screen decorations / window insets ourselves when needed
+ wm.getDefaultDisplay().getRealSize(mDisplaySize);
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
@@ -417,7 +419,35 @@ public class BubbleStackView extends FrameLayout {
mOrientationChangedListener =
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- mExpandedAnimationController.updateOrientation(mOrientation);
+ mExpandedAnimationController.updateOrientation(mOrientation, mDisplaySize);
+ mStackAnimationController.updateOrientation(mOrientation);
+
+ // Reposition & adjust the height for new orientation
+ if (mIsExpanded) {
+ mExpandedViewContainer.setTranslationY(getExpandedViewY());
+ mExpandedBubble.getExpandedView().updateView();
+ }
+
+ // Need to update the padding around the view
+ WindowInsets insets = getRootWindowInsets();
+ int leftPadding = mExpandedViewPadding;
+ int rightPadding = mExpandedViewPadding;
+ if (insets != null) {
+ // Can't have the expanded view overlaying notches
+ int cutoutLeft = 0;
+ int cutoutRight = 0;
+ DisplayCutout cutout = insets.getDisplayCutout();
+ if (cutout != null) {
+ cutoutLeft = cutout.getSafeInsetLeft();
+ cutoutRight = cutout.getSafeInsetRight();
+ }
+ // Or overlaying nav or status bar
+ leftPadding += Math.max(cutoutLeft, insets.getStableInsetLeft());
+ rightPadding += Math.max(cutoutRight, insets.getStableInsetRight());
+ }
+ mExpandedViewContainer.setPadding(leftPadding, mExpandedViewPadding,
+ rightPadding, mExpandedViewPadding);
+
if (mIsExpanded) {
// Re-draw bubble row and pointer for new orientation.
mExpandedAnimationController.expandFromStack(() -> {
@@ -488,6 +518,11 @@ public class BubbleStackView extends FrameLayout {
public void onOrientationChanged(int orientation) {
mOrientation = orientation;
+ // Display size is based on the rotation device was in when requested, we should update it
+ // We use the real size & subtract screen decorations / window insets ourselves when needed
+ WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getRealSize(mDisplaySize);
+
// Some resources change depending on orientation
Resources res = getContext().getResources();
mStatusBarHeight = res.getDimensionPixelSize(
@@ -1584,11 +1619,9 @@ public class BubbleStackView extends FrameLayout {
int index = getBubbleIndex(expandedBubble);
float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index);
float halfBubble = mBubbleSize / 2f;
-
- // Bubbles live in expanded view container (x includes expanded view padding).
- // Pointer lives in expanded view, which has padding (x does not include padding).
- // Remove padding when deriving pointer location from bubbles.
- float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble - mExpandedViewPadding;
+ float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble;
+ // Padding might be adjusted for insets, so get it directly from the view
+ bubbleCenter -= mExpandedViewContainer.getPaddingLeft();
expandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index c332d15a9b47..59d68bca93c3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -21,6 +21,7 @@ import android.content.res.Resources;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
+import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
@@ -57,6 +58,9 @@ public class ExpandedAnimationController
/** Stiffness for the expand/collapse path-following animation. */
private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 1000;
+ /** What percentage of the screen to use when centering the bubbles in landscape. */
+ private static final float CENTER_BUBBLES_LANDSCAPE_PERCENT = 0.66f;
+
/** Horizontal offset between bubbles, which we need to know to re-stack them. */
private float mStackOffsetPx;
/** Space between status bar and bubbles in the expanded state. */
@@ -69,8 +73,8 @@ public class ExpandedAnimationController
private Point mDisplaySize;
/** Max number of bubbles shown in row above expanded view.*/
private int mBubblesMaxRendered;
- /** Width of current screen orientation. */
- private float mScreenWidth;
+ /** What the current screen orientation is. */
+ private int mScreenOrientation;
/** Whether the dragged-out bubble is in the dismiss target. */
private boolean mIndividualBubbleWithinDismissTarget = false;
@@ -97,8 +101,7 @@ public class ExpandedAnimationController
public ExpandedAnimationController(Point displaySize, int expandedViewPadding,
int orientation) {
- mDisplaySize = displaySize;
- updateOrientation(orientation);
+ updateOrientation(orientation, displaySize);
mExpandedViewPadding = expandedViewPadding;
mLauncherGridDiff = 30f;
}
@@ -136,18 +139,16 @@ public class ExpandedAnimationController
/**
* Update effective screen width based on current orientation.
* @param orientation Landscape or portrait.
+ * @param displaySize Updated display size.
*/
- public void updateOrientation(int orientation) {
- if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
- mScreenWidth = mDisplaySize.y;
- } else {
- mScreenWidth = mDisplaySize.x;
- }
+ public void updateOrientation(int orientation, Point displaySize) {
+ mScreenOrientation = orientation;
+ mDisplaySize = displaySize;
if (mLayout != null) {
Resources res = mLayout.getContext().getResources();
+ mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mStatusBarHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
- mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
}
}
@@ -499,6 +500,50 @@ public class ExpandedAnimationController
return getRowLeft() + bubbleFromRowLeft;
}
+ /**
+ * When expanded, the bubbles are centered in the screen. In portrait, all available space is
+ * used. In landscape we have too much space so the value is restricted. This method accounts
+ * for window decorations (nav bar, cutouts).
+ *
+ * @return the desired width to display the expanded bubbles in.
+ */
+ private float getWidthForDisplayingBubbles() {
+ final float availableWidth = getAvailableScreenWidth(true /* includeStableInsets */);
+ if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ // display size y in landscape will be the smaller dimension of the screen
+ return Math.max(mDisplaySize.y, availableWidth * CENTER_BUBBLES_LANDSCAPE_PERCENT);
+ } else {
+ return availableWidth;
+ }
+ }
+
+ /**
+ * Determines the available screen width without the cutout.
+ *
+ * @param subtractStableInsets Whether or not stable insets should also be removed from the
+ * returned width.
+ * @return the total screen width available accounting for cutouts and insets,
+ * iff {@param includeStableInsets} is true.
+ */
+ private float getAvailableScreenWidth(boolean subtractStableInsets) {
+ float availableSize = mDisplaySize.x;
+ WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null;
+ if (insets != null) {
+ int cutoutLeft = 0;
+ int cutoutRight = 0;
+ DisplayCutout cutout = insets.getDisplayCutout();
+ if (cutout != null) {
+ cutoutLeft = cutout.getSafeInsetLeft();
+ cutoutRight = cutout.getSafeInsetRight();
+ }
+ final int stableLeft = subtractStableInsets ? insets.getStableInsetLeft() : 0;
+ final int stableRight = subtractStableInsets ? insets.getStableInsetRight() : 0;
+ availableSize -= Math.max(stableLeft, cutoutLeft);
+ availableSize -= Math.max(stableRight, cutoutRight);
+ }
+ return availableSize;
+ }
+
private float getRowLeft() {
if (mLayout == null) {
return 0;
@@ -510,9 +555,12 @@ public class ExpandedAnimationController
final float totalGapWidth = (bubbleCount - 1) * getSpaceBetweenBubbles();
final float rowWidth = totalGapWidth + totalBubbleWidth;
- final float centerScreen = mScreenWidth / 2f;
+ // This display size we're using includes the size of the insets, we want the true
+ // center of the display minus the notch here, which means we should include the
+ // stable insets (e.g. status bar, nav bar) in this calculation.
+ final float trueCenter = getAvailableScreenWidth(false /* subtractStableInsets */) / 2f;
final float halfRow = rowWidth / 2f;
- final float rowLeft = centerScreen - halfRow;
+ final float rowLeft = trueCenter - halfRow;
return rowLeft;
}
@@ -530,7 +578,7 @@ public class ExpandedAnimationController
* Launcher's app icon grid edge that we must match
*/
final float rowMargins = (mExpandedViewPadding + mLauncherGridDiff) * 2;
- final float maxRowWidth = mScreenWidth - rowMargins;
+ final float maxRowWidth = getWidthForDisplayingBubbles() - rowMargins;
final float totalBubbleWidth = mBubblesMaxRendered * mBubbleSizePx;
final float totalGapWidth = maxRowWidth - totalBubbleWidth;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index c2b0fe4ab2d9..20742d6d2358 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -43,7 +43,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 85bc22bab36f..914258f48b46 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -20,6 +20,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHT
import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
import android.content.Context;
+import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Handler;
import android.provider.DeviceConfig;
@@ -35,7 +36,7 @@ import com.android.systemui.plugins.FalsingPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
import java.io.PrintWriter;
@@ -66,6 +67,7 @@ public class FalsingManagerProxy implements FalsingManager {
DeviceConfigProxy deviceConfig) {
mProximitySensor = proximitySensor;
mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
+ mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
mDeviceConfig = deviceConfig;
mDeviceConfigListener =
properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace());
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index 3f5cae678d01..bd0906a63370 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -30,11 +30,12 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* FalsingManager designed to make clear why a touch was rejected.
@@ -42,7 +43,7 @@ import java.util.List;
public class BrightLineFalsingManager implements FalsingManager {
static final boolean DEBUG = false;
- private static final String TAG = "FalsingManagerPlugin";
+ private static final String TAG = "FalsingManager";
private final FalsingDataProvider mDataProvider;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -142,7 +143,15 @@ public class BrightLineFalsingManager implements FalsingManager {
boolean r = !mJustUnlockedWithFace && mClassifiers.stream().anyMatch(falsingClassifier -> {
boolean result = falsingClassifier.isFalseTouch();
if (result) {
- logInfo(falsingClassifier.getClass().getName() + ": true");
+ logInfo(String.format(
+ (Locale) null,
+ "{classifier=%s, interactionType=%d}",
+ falsingClassifier.getClass().getName(),
+ mDataProvider.getInteractionType()));
+ String reason = falsingClassifier.getReason();
+ if (reason != null) {
+ logInfo(reason);
+ }
} else {
logDebug(falsingClassifier.getClass().getName() + ": false");
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
index 9c03b915000e..520e0a12dec4 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java
@@ -25,6 +25,8 @@ import android.provider.DeviceConfig;
import com.android.systemui.util.DeviceConfigProxy;
+import java.util.Locale;
+
/**
* False on swipes that are too close to 45 degrees.
*
@@ -84,6 +86,15 @@ class DiagonalClassifier extends FalsingClassifier {
maxAngle + ONE_HUNDRED_EIGHTY_DEG);
}
+ @Override
+ String getReason() {
+ return String.format(
+ (Locale) null,
+ "{angle=%f, vertical=%s}",
+ getAngle(),
+ isVertical());
+ }
+
private boolean angleBetween(float angle, float min, float max) {
// No need to normalize angle as it is guaranteed to be between 0 and 2*PI.
min = normalizeAngle(min);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
index 0954f9e6e672..7f45cc8f4c56 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java
@@ -30,6 +30,7 @@ import android.view.VelocityTracker;
import com.android.systemui.util.DeviceConfigProxy;
import java.util.List;
+import java.util.Locale;
/**
* Ensure that the swipe + momentum covers a minimum distance.
@@ -144,15 +145,66 @@ class DistanceClassifier extends FalsingClassifier {
@Override
public boolean isFalseTouch() {
- return !getDistances().getPassedFlingThreshold();
+ return !getPassedFlingThreshold();
+ }
+
+ @Override
+ String getReason() {
+ DistanceVectors distanceVectors = getDistances();
+
+ return String.format(
+ (Locale) null,
+ "{distanceVectors=%s, isHorizontal=%s, velocityToDistanceMultiplier=%f, "
+ + "horizontalFlingThreshold=%f, verticalFlingThreshold=%f, "
+ + "horizontalSwipeThreshold=%f, verticalSwipeThreshold=%s}",
+ distanceVectors,
+ isHorizontal(),
+ mVelocityToDistanceMultiplier,
+ mHorizontalFlingThresholdPx,
+ mVerticalFlingThresholdPx,
+ mHorizontalSwipeThresholdPx,
+ mVerticalSwipeThresholdPx);
}
boolean isLongSwipe() {
- boolean longSwipe = getDistances().getPassedDistanceThreshold();
+ boolean longSwipe = getPassedDistanceThreshold();
logDebug("Is longSwipe? " + longSwipe);
return longSwipe;
}
+ private boolean getPassedDistanceThreshold() {
+ DistanceVectors distanceVectors = getDistances();
+ if (isHorizontal()) {
+ logDebug("Horizontal swipe distance: " + Math.abs(distanceVectors.mDx));
+ logDebug("Threshold: " + mHorizontalSwipeThresholdPx);
+
+ return Math.abs(distanceVectors.mDx) >= mHorizontalSwipeThresholdPx;
+ }
+
+ logDebug("Vertical swipe distance: " + Math.abs(distanceVectors.mDy));
+ logDebug("Threshold: " + mVerticalSwipeThresholdPx);
+ return Math.abs(distanceVectors.mDy) >= mVerticalSwipeThresholdPx;
+ }
+
+ private boolean getPassedFlingThreshold() {
+ DistanceVectors distanceVectors = getDistances();
+
+ float dX = distanceVectors.mDx + distanceVectors.mVx * mVelocityToDistanceMultiplier;
+ float dY = distanceVectors.mDy + distanceVectors.mVy * mVelocityToDistanceMultiplier;
+
+ if (isHorizontal()) {
+ logDebug("Horizontal swipe and fling distance: " + distanceVectors.mDx + ", "
+ + distanceVectors.mVx * mVelocityToDistanceMultiplier);
+ logDebug("Threshold: " + mHorizontalFlingThresholdPx);
+ return Math.abs(dX) >= mHorizontalFlingThresholdPx;
+ }
+
+ logDebug("Vertical swipe and fling distance: " + distanceVectors.mDy + ", "
+ + distanceVectors.mVy * mVelocityToDistanceMultiplier);
+ logDebug("Threshold: " + mVerticalFlingThresholdPx);
+ return Math.abs(dY) >= mVerticalFlingThresholdPx;
+ }
+
private class DistanceVectors {
final float mDx;
final float mDy;
@@ -166,34 +218,9 @@ class DistanceClassifier extends FalsingClassifier {
this.mVy = vY;
}
- boolean getPassedDistanceThreshold() {
- if (isHorizontal()) {
- logDebug("Horizontal swipe distance: " + Math.abs(mDx));
- logDebug("Threshold: " + mHorizontalSwipeThresholdPx);
-
- return Math.abs(mDx) >= mHorizontalSwipeThresholdPx;
- }
-
- logDebug("Vertical swipe distance: " + Math.abs(mDy));
- logDebug("Threshold: " + mVerticalSwipeThresholdPx);
- return Math.abs(mDy) >= mVerticalSwipeThresholdPx;
- }
-
- boolean getPassedFlingThreshold() {
- float dX = this.mDx + this.mVx * mVelocityToDistanceMultiplier;
- float dY = this.mDy + this.mVy * mVelocityToDistanceMultiplier;
-
- if (isHorizontal()) {
- logDebug("Horizontal swipe and fling distance: " + this.mDx + ", "
- + this.mVx * mVelocityToDistanceMultiplier);
- logDebug("Threshold: " + mHorizontalFlingThresholdPx);
- return Math.abs(dX) >= mHorizontalFlingThresholdPx;
- }
-
- logDebug("Vertical swipe and fling distance: " + this.mDy + ", "
- + this.mVy * mVelocityToDistanceMultiplier);
- logDebug("Threshold: " + mVerticalFlingThresholdPx);
- return Math.abs(dY) >= mVerticalFlingThresholdPx;
+ @Override
+ public String toString() {
+ return String.format((Locale) null, "{dx=%f, vx=%f, dy=%f, vy=%f}", mDx, mVx, mDy, mVy);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
index bf397518de46..7555051fac48 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
@@ -19,7 +19,7 @@ package com.android.systemui.classifier.brightline;
import android.view.MotionEvent;
import com.android.systemui.classifier.Classifier;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
import java.util.List;
@@ -117,6 +117,14 @@ abstract class FalsingClassifier {
*/
abstract boolean isFalseTouch();
+ /**
+ * Give the classifier a chance to log more details about why it triggered.
+ *
+ * This should only be called after a call to {@link #isFalseTouch()}, and only if
+ * {@link #isFalseTouch()} returns true;
+ */
+ abstract String getReason();
+
static void logDebug(String msg) {
BrightLineFalsingManager.logDebug(msg);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
index 40e141fbf988..85a4d234b5df 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java
@@ -18,6 +18,8 @@ package com.android.systemui.classifier.brightline;
import android.view.MotionEvent;
+import java.util.Locale;
+
/**
* False touch if more than one finger touches the screen.
*
@@ -50,4 +52,13 @@ class PointerCountClassifier extends FalsingClassifier {
public boolean isFalseTouch() {
return mMaxPointerCount > MAX_ALLOWED_POINTERS;
}
+
+ @Override
+ String getReason() {
+ return String.format(
+ (Locale) null,
+ "{pointersObserved=%d, threshold=%d}",
+ mMaxPointerCount,
+ MAX_ALLOWED_POINTERS);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
index eeca409866a8..749914e1b625 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java
@@ -23,7 +23,9 @@ import android.provider.DeviceConfig;
import android.view.MotionEvent;
import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
+
+import java.util.Locale;
/**
@@ -121,6 +123,16 @@ class ProximityClassifier extends FalsingClassifier {
return false;
}
+ @Override
+ String getReason() {
+ return String.format(
+ (Locale) null,
+ "{percentInProximity=%f, threshold=%f, distanceClassifier=%s}",
+ mPercentNear,
+ mPercentCoveredThreshold,
+ mDistanceClassifier.getReason());
+ }
+
/**
* @param near is the sensor showing the near state right now
* @param timeStampNs time of this event in nanoseconds
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java
index b6ceab559301..5f1b37ae06d9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java
@@ -58,4 +58,9 @@ public class TypeClassifier extends FalsingClassifier {
return true;
}
}
+
+ @Override
+ String getReason() {
+ return String.format("{vertical=%s, up=%s, right=%s}", isVertical(), isUp(), isRight());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
index a0da988b27b5..957ea8d127d2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
@@ -29,6 +29,7 @@ import com.android.systemui.util.DeviceConfigProxy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* Penalizes gestures that change direction in either the x or y too much.
@@ -49,6 +50,10 @@ class ZigZagClassifier extends FalsingClassifier {
private final float mMaxYPrimaryDeviance;
private final float mMaxXSecondaryDeviance;
private final float mMaxYSecondaryDeviance;
+ private float mLastDevianceX;
+ private float mLastDevianceY;
+ private float mLastMaxXDeviance;
+ private float mLastMaxYDeviance;
ZigZagClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) {
super(dataProvider);
@@ -139,11 +144,25 @@ class ZigZagClassifier extends FalsingClassifier {
maxYDeviance = mMaxYPrimaryDeviance * totalDistanceIn * getYdpi();
}
+ // These values are saved for logging reasons. {@see #getReason()}
+ mLastDevianceX = devianceX;
+ mLastDevianceY = devianceY;
+ mLastMaxXDeviance = maxXDeviance;
+ mLastMaxYDeviance = maxYDeviance;
+
logDebug("Straightness Deviance: (" + devianceX + "," + devianceY + ") vs "
+ "(" + maxXDeviance + "," + maxYDeviance + ")");
return devianceX > maxXDeviance || devianceY > maxYDeviance;
}
+ @Override
+ String getReason() {
+ return String.format(
+ (Locale) null,
+ "{devianceX=%f, maxDevianceX=%s, devianceY=%s, maxDevianceY=%s}",
+ mLastDevianceX, mLastMaxXDeviance, mLastDevianceY, mLastMaxYDeviance);
+ }
+
private float getAtan2LastPoint() {
MotionEvent firstEvent = getFirstMotionEvent();
MotionEvent lastEvent = getLastMotionEvent();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index c3672868ba32..bb8c7f1dabf8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -33,7 +33,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -45,7 +45,7 @@ public class DozeFactory {
/** Creates a DozeMachine with its parts for {@code dozeService}. */
public DozeMachine assembleMachine(DozeService dozeService, FalsingManager falsingManager) {
Context context = dozeService;
- SensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
+ AsyncSensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
DockManager dockManager = Dependency.get(DockManager.class);
WakefulnessLifecycle wakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
@@ -91,7 +91,7 @@ public class DozeFactory {
params.getPolicy());
}
- private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager,
+ private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager,
DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine,
DockManager dockManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 026a62528c8d..67eefc5588e3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -26,7 +26,6 @@ import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
@@ -43,11 +42,12 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.systemui.R;
+import com.android.systemui.Dependency;
import com.android.systemui.plugins.SensorManagerPlugin;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.AlarmTimeout;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
@@ -62,7 +62,7 @@ public class DozeSensors {
private final Context mContext;
private final AlarmManager mAlarmManager;
- private final SensorManager mSensorManager;
+ private final AsyncSensorManager mSensorManager;
private final ContentResolver mResolver;
private final TriggerSensor mPickupSensor;
private final DozeParameters mDozeParameters;
@@ -74,13 +74,13 @@ public class DozeSensors {
protected TriggerSensor[] mSensors;
private final Handler mHandler = new Handler();
- private final ProxSensor mProxSensor;
+ private final ProximitySensor mProximitySensor;
private long mDebounceFrom;
private boolean mSettingRegistered;
private boolean mListening;
private boolean mPaused;
- public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
+ public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
mContext = context;
@@ -91,6 +91,7 @@ public class DozeSensors {
mWakeLock = wakeLock;
mProxCallback = proxCallback;
mResolver = mContext.getContentResolver();
+ mCallback = callback;
boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
mSensors = new TriggerSensor[] {
@@ -146,8 +147,11 @@ public class DozeSensors {
false /* touchscreen */, mConfig.getWakeLockScreenDebounce()),
};
- mProxSensor = new ProxSensor(policy);
- mCallback = callback;
+ mProximitySensor = new ProximitySensor(
+ context, sensorManager, Dependency.get(PluginManager.class));
+
+ mProximitySensor.register(
+ proximityEvent -> mProxCallback.accept(!proximityEvent.getNear()));
}
/**
@@ -236,7 +240,15 @@ public class DozeSensors {
}
public void setProxListening(boolean listen) {
- mProxSensor.setRequested(listen);
+ if (mProximitySensor.isRegistered() && listen) {
+ mProximitySensor.alertListeners();
+ } else {
+ if (listen) {
+ mProximitySensor.resume();
+ } else {
+ mProximitySensor.pause();
+ }
+ }
}
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@@ -267,115 +279,16 @@ public class DozeSensors {
/** Dump current state */
public void dump(PrintWriter pw) {
for (TriggerSensor s : mSensors) {
- pw.print(" Sensor: "); pw.println(s.toString());
+ pw.println(" Sensor: " + s.toString());
}
- pw.print(" ProxSensor: "); pw.println(mProxSensor.toString());
+ pw.println(" ProxSensor: " + mProximitySensor.toString());
}
/**
- * @return true if prox is currently far, false if near or null if unknown.
+ * @return true if prox is currently near, false if far or null if unknown.
*/
- public Boolean isProximityCurrentlyFar() {
- return mProxSensor.mCurrentlyFar;
- }
-
- private class ProxSensor implements SensorEventListener {
-
- boolean mRequested;
- boolean mRegistered;
- Boolean mCurrentlyFar;
- long mLastNear;
- final AlarmTimeout mCooldownTimer;
- final AlwaysOnDisplayPolicy mPolicy;
- final Sensor mSensor;
- final boolean mUsingBrightnessSensor;
-
- public ProxSensor(AlwaysOnDisplayPolicy policy) {
- mPolicy = policy;
- mCooldownTimer = new AlarmTimeout(mAlarmManager, this::updateRegistered,
- "prox_cooldown", mHandler);
-
- // The default prox sensor can be noisy, so let's use a prox gated brightness sensor
- // if available.
- Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
- mContext.getString(R.string.doze_brightness_sensor_type));
- mUsingBrightnessSensor = sensor != null;
- if (sensor == null) {
- sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- }
- mSensor = sensor;
- }
-
- void setRequested(boolean requested) {
- if (mRequested == requested) {
- // Send an update even if we don't re-register.
- mHandler.post(() -> {
- if (mCurrentlyFar != null) {
- mProxCallback.accept(mCurrentlyFar);
- }
- });
- return;
- }
- mRequested = requested;
- updateRegistered();
- }
-
- private void updateRegistered() {
- setRegistered(mRequested && !mCooldownTimer.isScheduled());
- }
-
- private void setRegistered(boolean register) {
- if (mRegistered == register) {
- return;
- }
- if (register) {
- mRegistered = mSensorManager.registerListener(this, mSensor,
- SensorManager.SENSOR_DELAY_NORMAL, mHandler);
- } else {
- mSensorManager.unregisterListener(this);
- mRegistered = false;
- mCurrentlyFar = null;
- }
- }
-
- @Override
- public void onSensorChanged(android.hardware.SensorEvent event) {
- if (DEBUG) Log.d(TAG, "onSensorChanged " + event);
-
- if (mUsingBrightnessSensor) {
- // The custom brightness sensor is gated by the proximity sensor and will return 0
- // whenever prox is covered.
- mCurrentlyFar = event.values[0] > 0;
- } else {
- mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
- }
- mProxCallback.accept(mCurrentlyFar);
-
- long now = SystemClock.elapsedRealtime();
- if (mCurrentlyFar == null) {
- // Sensor has been unregistered by the proxCallback. Do nothing.
- } else if (!mCurrentlyFar) {
- mLastNear = now;
- } else if (mCurrentlyFar && now - mLastNear < mPolicy.proxCooldownTriggerMs) {
- // If the last near was very recent, we might be using more power for prox
- // wakeups than we're saving from turning of the screen. Instead, turn it off
- // for a while.
- mCooldownTimer.schedule(mPolicy.proxCooldownPeriodMs,
- AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
- updateRegistered();
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- @Override
- public String toString() {
- return String.format("{registered=%s, requested=%s, coolingDown=%s, currentlyFar=%s,"
- + " sensor=%s}", mRegistered, mRequested, mCooldownTimer.isScheduled(),
- mCurrentlyFar, mSensor);
- }
+ public Boolean isProximityCurrentlyNear() {
+ return mProximitySensor.isNear();
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index bab64db4519c..80d4b631314a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -24,10 +24,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.metrics.LogMaker;
import android.os.Handler;
@@ -39,16 +35,16 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.Preconditions;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
-import java.util.function.IntConsumer;
+import java.util.function.Consumer;
/**
* Handles triggers for ambient state changes.
@@ -67,13 +63,15 @@ public class DozeTriggers implements DozeMachine.Part {
*/
private static boolean sWakeDisplaySensorState = true;
+ private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
+
private final Context mContext;
private final DozeMachine mMachine;
private final DozeSensors mDozeSensors;
private final DozeHost mDozeHost;
private final AmbientDisplayConfiguration mConfig;
private final DozeParameters mDozeParameters;
- private final SensorManager mSensorManager;
+ private final AsyncSensorManager mSensorManager;
private final Handler mHandler;
private final WakeLock mWakeLock;
private final boolean mAllowPulseTriggers;
@@ -81,6 +79,7 @@ public class DozeTriggers implements DozeMachine.Part {
private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
private final DockEventListener mDockEventListener = new DockEventListener();
private final DockManager mDockManager;
+ private final ProximitySensor.ProximityCheck mProxCheck;
private long mNotificationPulseTime;
private boolean mPulsePending;
@@ -89,7 +88,7 @@ public class DozeTriggers implements DozeMachine.Part {
public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
AlarmManager alarmManager, AmbientDisplayConfiguration config,
- DozeParameters dozeParameters, SensorManager sensorManager, Handler handler,
+ DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler,
WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager) {
mContext = context;
mMachine = machine;
@@ -105,6 +104,9 @@ public class DozeTriggers implements DozeMachine.Part {
dozeParameters.getPolicy());
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
+ mProxCheck = new ProximitySensor.ProximityCheck(
+ new ProximitySensor(mContext, mSensorManager, null),
+ mHandler);
}
private void onNotification(Runnable onPulseSuppressedListener) {
@@ -134,25 +136,27 @@ public class DozeTriggers implements DozeMachine.Part {
}
}
- private void proximityCheckThenCall(IntConsumer callback,
+ private void proximityCheckThenCall(Consumer<Boolean> callback,
boolean alreadyPerformedProxCheck,
int reason) {
- Boolean cachedProxFar = mDozeSensors.isProximityCurrentlyFar();
+ Boolean cachedProxNear = mDozeSensors.isProximityCurrentlyNear();
if (alreadyPerformedProxCheck) {
- callback.accept(ProximityCheck.RESULT_NOT_CHECKED);
- } else if (cachedProxFar != null) {
- callback.accept(cachedProxFar ? ProximityCheck.RESULT_FAR : ProximityCheck.RESULT_NEAR);
+ callback.accept(null);
+ } else if (cachedProxNear != null) {
+ callback.accept(cachedProxNear);
} else {
final long start = SystemClock.uptimeMillis();
- new ProximityCheck() {
- @Override
- public void onProximityResult(int result) {
- final long end = SystemClock.uptimeMillis();
- DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
- end - start, reason);
- callback.accept(result);
- }
- }.check();
+ mProxCheck.check(PROXIMITY_TIMEOUT_DELAY_MS, near -> {
+ final long end = SystemClock.uptimeMillis();
+ DozeLog.traceProximityResult(
+ mContext,
+ near == null ? false : near,
+ end - start,
+ reason);
+ callback.accept(near);
+ mWakeLock.release(TAG);
+ });
+ mWakeLock.acquire(TAG);
}
}
@@ -178,7 +182,7 @@ public class DozeTriggers implements DozeMachine.Part {
}
} else {
proximityCheckThenCall((result) -> {
- if (result == ProximityCheck.RESULT_NEAR) {
+ if (result) {
// In pocket, drop event.
return;
}
@@ -267,7 +271,7 @@ public class DozeTriggers implements DozeMachine.Part {
if (wake) {
proximityCheckThenCall((result) -> {
- if (result == ProximityCheck.RESULT_NEAR) {
+ if (result) {
// In pocket, drop event.
return;
}
@@ -376,7 +380,7 @@ public class DozeTriggers implements DozeMachine.Part {
mPulsePending = true;
proximityCheckThenCall((result) -> {
- if (result == ProximityCheck.RESULT_NEAR) {
+ if (result) {
// in pocket, abort pulse
DozeLog.tracePulseDropped(mContext, "inPocket");
mPulsePending = false;
@@ -412,104 +416,11 @@ public class DozeTriggers implements DozeMachine.Part {
pw.print(" notificationPulseTime=");
pw.println(Formatter.formatShortElapsedTime(mContext, mNotificationPulseTime));
- pw.print(" pulsePending="); pw.println(mPulsePending);
+ pw.println(" pulsePending=" + mPulsePending);
pw.println("DozeSensors:");
mDozeSensors.dump(pw);
}
- /**
- * @see DozeSensors.ProxSensor
- */
- private abstract class ProximityCheck implements SensorEventListener, Runnable {
- private static final int TIMEOUT_DELAY_MS = 500;
-
- protected static final int RESULT_UNKNOWN = 0;
- protected static final int RESULT_NEAR = 1;
- protected static final int RESULT_FAR = 2;
- protected static final int RESULT_NOT_CHECKED = 3;
-
- private boolean mRegistered;
- private boolean mFinished;
- private float mMaxRange;
- private boolean mUsingBrightnessSensor;
-
- protected abstract void onProximityResult(int result);
-
- public void check() {
- Preconditions.checkState(!mFinished && !mRegistered);
- Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
- mContext.getString(R.string.doze_brightness_sensor_type));
- mUsingBrightnessSensor = sensor != null;
- if (sensor == null) {
- sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- }
- if (sensor == null) {
- if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No sensor found");
- finishWithResult(RESULT_UNKNOWN);
- return;
- }
- mDozeSensors.setDisableSensorsInterferingWithProximity(true);
-
- mMaxRange = sensor.getMaximumRange();
- mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, 0,
- mHandler);
- mHandler.postDelayed(this, TIMEOUT_DELAY_MS);
- mWakeLock.acquire(TAG);
- mRegistered = true;
- }
-
- /**
- * @see DozeSensors.ProxSensor#onSensorChanged(SensorEvent)
- */
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (event.values.length == 0) {
- if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: Event has no values!");
- finishWithResult(RESULT_UNKNOWN);
- } else {
- if (DozeMachine.DEBUG) {
- Log.d(TAG, "ProxCheck: Event: value=" + event.values[0] + " max=" + mMaxRange);
- }
- final boolean isNear;
- if (mUsingBrightnessSensor) {
- // The custom brightness sensor is gated by the proximity sensor and will
- // return 0 whenever prox is covered.
- isNear = event.values[0] == 0;
- } else {
- isNear = event.values[0] < mMaxRange;
- }
- finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR);
- }
- }
-
- @Override
- public void run() {
- if (DozeMachine.DEBUG) Log.d(TAG, "ProxCheck: No event received before timeout");
- finishWithResult(RESULT_UNKNOWN);
- }
-
- private void finishWithResult(int result) {
- if (mFinished) return;
- boolean wasRegistered = mRegistered;
- if (mRegistered) {
- mHandler.removeCallbacks(this);
- mSensorManager.unregisterListener(this);
- mDozeSensors.setDisableSensorsInterferingWithProximity(false);
- mRegistered = false;
- }
- onProximityResult(result);
- if (wasRegistered) {
- mWakeLock.release(TAG);
- }
- mFinished = true;
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- // noop
- }
- }
-
private class TriggerReceiver extends BroadcastReceiver {
private boolean mRegistered;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 30be7754cffc..6795bff6409a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -18,6 +18,7 @@ package com.android.systemui.pip;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
@@ -46,9 +47,6 @@ public class PipBoundsHandler {
private static final String TAG = PipBoundsHandler.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
- // System.identityHashCode guarantees zero for null object.
- private static final int INVALID_SYSTEM_IDENTITY_TOKEN = 0;
-
private final Context mContext;
private final IWindowManager mWindowManager;
private final PipSnapAlgorithm mSnapAlgorithm;
@@ -58,7 +56,7 @@ public class PipBoundsHandler {
private final Point mTmpDisplaySize = new Point();
private IPinnedStackController mPinnedStackController;
- private int mLastPipToken;
+ private ComponentName mLastPipComponentName;
private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
private float mDefaultAspectRatio;
@@ -80,8 +78,11 @@ public class PipBoundsHandler {
mContext = context;
mSnapAlgorithm = new PipSnapAlgorithm(context);
mWindowManager = WindowManagerGlobal.getWindowManagerService();
- mAspectRatio = mDefaultAspectRatio;
reloadResources();
+ // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
+ // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
+ // triggers a configuration change and the resources to be reloaded.
+ mAspectRatio = mDefaultAspectRatio;
}
/**
@@ -161,27 +162,27 @@ public class PipBoundsHandler {
}
/**
- * Responds to IPinnedStackListener on saving reentry snap fraction for a given token.
- * Token should be generated via {@link System#identityHashCode(Object)}
+ * Responds to IPinnedStackListener on saving reentry snap fraction
+ * for a given {@link ComponentName}.
*/
- public void onSaveReentrySnapFraction(int token, Rect stackBounds) {
- mReentrySnapFraction = getSnapFraction(stackBounds);
- mLastPipToken = token;
+ public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {
+ mReentrySnapFraction = getSnapFraction(bounds);
+ mLastPipComponentName = componentName;
}
/**
- * Responds to IPinnedStackListener on resetting reentry snap fraction for a given token.
- * Token should be generated via {@link System#identityHashCode(Object)}
+ * Responds to IPinnedStackListener on resetting reentry snap fraction
+ * for a given {@link ComponentName}.
*/
- public void onResetReentrySnapFraction(int token) {
- if (mLastPipToken == token) {
+ public void onResetReentrySnapFraction(ComponentName componentName) {
+ if (componentName.equals(mLastPipComponentName)) {
onResetReentrySnapFractionUnchecked();
}
}
private void onResetReentrySnapFractionUnchecked() {
mReentrySnapFraction = INVALID_SNAP_FRACTION;
- mLastPipToken = INVALID_SYSTEM_IDENTITY_TOKEN;
+ mLastPipComponentName = null;
}
/**
@@ -212,24 +213,28 @@ public class PipBoundsHandler {
/**
* Responds to IPinnedStackListener on preparing the pinned stack animation.
*/
- public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
- final Rect targetStackBounds;
- if (stackBounds == null) {
- targetStackBounds = getDefaultBounds(mReentrySnapFraction);
+ public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
+ final Rect destinationBounds;
+ if (bounds == null) {
+ destinationBounds = getDefaultBounds(mReentrySnapFraction);
} else {
- targetStackBounds = new Rect();
- targetStackBounds.set(stackBounds);
+ destinationBounds = new Rect(bounds);
}
if (isValidPictureInPictureAspectRatio(aspectRatio)) {
- transformBoundsToAspectRatio(targetStackBounds, aspectRatio,
- true /* useCurrentMinEdgeSize */);
+ transformBoundsToAspectRatio(destinationBounds, aspectRatio,
+ false /* useCurrentMinEdgeSize */);
}
- if (targetStackBounds.equals(stackBounds)) {
+ if (destinationBounds.equals(bounds)) {
return;
}
mAspectRatio = aspectRatio;
onResetReentrySnapFractionUnchecked();
- // TODO: callback Window Manager on starting animation with calculated bounds
+ try {
+ mPinnedStackController.startAnimation(destinationBounds, sourceRectHint,
+ -1 /* animationDuration */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to start PiP animation from SysUI", e);
+ }
}
/**
@@ -358,6 +363,7 @@ public class PipBoundsHandler {
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
+ pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction);
pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio);
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 3be3422a36ad..8dfae32a1939 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -32,14 +32,16 @@ import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
+import android.view.DisplayInfo;
import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
import com.android.systemui.Dependency;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -58,8 +60,12 @@ public class PipManager implements BasePipManager {
private IActivityTaskManager mActivityTaskManager;
private Handler mHandler = new Handler();
- private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
+ private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener();
+ private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
+ private final Rect mTmpInsetBounds = new Rect();
+ private final Rect mTmpNormalBounds = new Rect();
+ private PipBoundsHandler mPipBoundsHandler;
private InputConsumerController mInputConsumerController;
private PipMenuActivityController mMenuController;
private PipMediaController mMediaController;
@@ -119,11 +125,11 @@ public class PipManager implements BasePipManager {
/**
* Handler for messages from the PIP controller.
*/
- private class PinnedStackListener extends IPinnedStackListener.Stub {
-
+ private class PipManagerPinnedStackListener extends PinnedStackListener {
@Override
public void onListenerRegistered(IPinnedStackController controller) {
mHandler.post(() -> {
+ mPipBoundsHandler.setPinnedStackController(controller);
mTouchHandler.setPinnedStackController(controller);
});
}
@@ -131,6 +137,7 @@ public class PipManager implements BasePipManager {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
mHandler.post(() -> {
+ mPipBoundsHandler.onImeVisibilityChanged(imeVisible, imeHeight);
mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
});
}
@@ -138,31 +145,66 @@ public class PipManager implements BasePipManager {
@Override
public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
mHandler.post(() -> {
- mTouchHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight);
+ mPipBoundsHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight);
+ mTouchHandler.onShelfVisibilityChanged(shelfVisible, shelfHeight);
});
}
@Override
public void onMinimizedStateChanged(boolean isMinimized) {
mHandler.post(() -> {
+ mPipBoundsHandler.onMinimizedStateChanged(isMinimized);
mTouchHandler.setMinimizedState(isMinimized, true /* fromController */);
});
}
@Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
- int displayRotation) {
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment) {
mHandler.post(() -> {
- mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, animatingBounds,
- fromImeAdjustment, fromShelfAdjustment, displayRotation);
+ // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
+ mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
+ animatingBounds, mTmpDisplayInfo);
+ mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
+ animatingBounds, fromImeAdjustment, fromShelfAdjustment,
+ mTmpDisplayInfo.rotation);
});
}
@Override
public void onActionsChanged(ParceledListSlice actions) {
+ mHandler.post(() -> mMenuController.setAppActions(actions));
+ }
+
+ @Override
+ public void onSaveReentrySnapFraction(ComponentName componentName, Rect bounds) {
+ mHandler.post(() -> mPipBoundsHandler.onSaveReentrySnapFraction(componentName, bounds));
+ }
+
+ @Override
+ public void onResetReentrySnapFraction(ComponentName componentName) {
+ mHandler.post(() -> mPipBoundsHandler.onResetReentrySnapFraction(componentName));
+ }
+
+ @Override
+ public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+ mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo));
+ }
+
+ @Override
+ public void onConfigurationChanged() {
+ mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged());
+ }
+
+ @Override
+ public void onAspectRatioChanged(float aspectRatio) {
+ mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio));
+ }
+
+ @Override
+ public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {
mHandler.post(() -> {
- mMenuController.setAppActions(actions);
+ mPipBoundsHandler.onPrepareAnimation(sourceRectHint, aspectRatio, bounds);
});
}
}
@@ -184,12 +226,13 @@ public class PipManager implements BasePipManager {
}
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ mPipBoundsHandler = new PipBoundsHandler(context);
mInputConsumerController = InputConsumerController.getPipInputConsumer();
mMediaController = new PipMediaController(context, mActivityManager);
mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
mInputConsumerController);
mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager,
- mMenuController, mInputConsumerController);
+ mMenuController, mInputConsumerController, mPipBoundsHandler);
mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
mTouchHandler.getMotionHelper());
@@ -252,5 +295,6 @@ public class PipManager implements BasePipManager {
mInputConsumerController.dump(pw, innerPrefix);
mMenuController.dump(pw, innerPrefix);
mTouchHandler.dump(pw, innerPrefix);
+ mPipBoundsHandler.dump(pw, innerPrefix);
}
}
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 30cf412671bc..1f36d97ce308 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -48,6 +48,7 @@ import android.view.accessibility.AccessibilityWindowInfo;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.policy.PipSnapAlgorithm;
import com.android.systemui.R;
+import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -75,6 +76,7 @@ public class PipTouchHandler {
private final IActivityTaskManager mActivityTaskManager;
private final ViewConfiguration mViewConfig;
private final PipMenuListener mMenuListener = new PipMenuListener();
+ private final PipBoundsHandler mPipBoundsHandler;
private IPinnedStackController mPinnedStackController;
private final PipMenuActivityController mMenuController;
@@ -178,7 +180,8 @@ public class PipTouchHandler {
public PipTouchHandler(Context context, IActivityManager activityManager,
IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
- InputConsumerController inputConsumerController) {
+ InputConsumerController inputConsumerController,
+ PipBoundsHandler pipBoundsHandler) {
// Initialize the Pip input consumer
mContext = context;
@@ -211,6 +214,8 @@ public class PipTouchHandler {
inputConsumerController.setInputListener(this::handleTouchEvent);
inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
onRegistrationChanged(inputConsumerController.isRegistered());
+
+ mPipBoundsHandler = pipBoundsHandler;
}
public void setTouchEnabled(boolean enabled) {
@@ -787,14 +792,8 @@ public class PipTouchHandler {
mMovementBounds = isMenuExpanded
? mExpandedMovementBounds
: mNormalMovementBounds;
- try {
- if (mPinnedStackController != null) {
- mPinnedStackController.setMinEdgeSize(
- isMenuExpanded ? mExpandedShortestEdgeSize : 0);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Could not set minimized state", e);
- }
+ mPipBoundsHandler.setMinEdgeSize(
+ isMenuExpanded ? mExpandedShortestEdgeSize : 0);
}
/**
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 918af4f0cd4c..81d6973efb2a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -19,13 +19,10 @@ package com.android.systemui.pip.tv;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.view.Display.DEFAULT_DISPLAY;
-import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -46,16 +43,15 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
-import android.view.IWindowManager;
-import android.view.WindowManagerGlobal;
+import android.view.DisplayInfo;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.pip.BasePipManager;
+import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -111,9 +107,8 @@ public class PipManager implements BasePipManager {
private int mSuspendPipResizingReason;
private Context mContext;
- private IActivityManager mActivityManager;
+ private PipBoundsHandler mPipBoundsHandler;
private IActivityTaskManager mActivityTaskManager;
- private IWindowManager mWindowManager;
private MediaSessionManager mMediaSessionManager;
private int mState = STATE_NO_PIP;
private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP;
@@ -135,11 +130,16 @@ public class PipManager implements BasePipManager {
private PipNotification mPipNotification;
private ParceledListSlice mCustomActions;
+ // Used to calculate the movement bounds
+ private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
+ private final Rect mTmpInsetBounds = new Rect();
+ private final Rect mTmpNormalBounds = new Rect();
+
// Keeps track of the IME visibility to adjust the PiP when the IME is visible
private boolean mImeVisible;
private int mImeHeightAdjustment;
- private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
+ private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener();
private final Runnable mResizePinnedStackRunnable = new Runnable() {
@Override
@@ -181,11 +181,7 @@ public class PipManager implements BasePipManager {
/**
* Handler for messages from the PIP controller.
*/
- private class PinnedStackListener extends IPinnedStackListener.Stub {
-
- @Override
- public void onListenerRegistered(IPinnedStackController controller) {}
-
+ private class PipManagerPinnedStackListener extends PinnedStackListener {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
if (mState == STATE_PIP) {
@@ -205,17 +201,13 @@ public class PipManager implements BasePipManager {
}
@Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {}
-
- @Override
- public void onMinimizedStateChanged(boolean isMinimized) {}
-
- @Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
- int displayRotation) {
+ public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment,
+ boolean fromShelfAdjustment) {
mHandler.post(() -> {
- mDefaultPipBounds.set(normalBounds);
+ // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
+ mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
+ animatingBounds, mTmpDisplayInfo);
+ mDefaultPipBounds.set(animatingBounds);
});
}
@@ -241,10 +233,8 @@ public class PipManager implements BasePipManager {
}
mInitialized = true;
mContext = context;
-
- mActivityManager = ActivityManager.getService();
+ mPipBoundsHandler = new PipBoundsHandler(context);
mActivityTaskManager = ActivityTaskManager.getService();
- mWindowManager = WindowManagerGlobal.getWindowManagerService();
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
@@ -291,7 +281,7 @@ public class PipManager implements BasePipManager {
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
try {
- mWindowManager.registerPinnedStackListener(DEFAULT_DISPLAY, mPinnedStackListener);
+ WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
} catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 61d7498ced94..1e763cf79240 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -222,7 +222,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (!TILES_SETTING.equals(key)) {
return;
}
- if (DEBUG) Log.d(TAG, "Recreating tiles");
+ Log.d(TAG, "Recreating tiles");
if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
}
@@ -231,7 +231,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
tile -> {
- if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
+ Log.d(TAG, "Destroying tile: " + tile.getKey());
tile.getValue().destroy();
});
final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
@@ -248,9 +248,10 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
newTiles.put(tileSpec, tile);
} else {
tile.destroy();
+ Log.d(TAG, "Destroying not available tile: " + tileSpec);
}
} else {
- if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
+ Log.d(TAG, "Creating tile: " + tileSpec);
try {
tile = createTile(tileSpec);
if (tile != null) {
@@ -259,6 +260,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
newTiles.put(tileSpec, tile);
} else {
tile.destroy();
+ Log.d(TAG, "Destroying not available tile: " + tileSpec);
}
}
} catch (Throwable t) {
@@ -274,7 +276,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mTiles.putAll(newTiles);
if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
// If we didn't manage to create any tiles, set it to empty (default)
- if (DEBUG) Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
+ Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
} else {
for (int i = 0; i < mCallbacks.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
index 752b6a80f93d..926ae71194d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
@@ -18,12 +18,19 @@ package com.android.systemui.statusbar;
import android.content.Context;
import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
import android.service.notification.StatusBarNotification;
import android.util.FeatureFlagUtils;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.internal.R;
import com.android.settingslib.media.MediaOutputSliceConstants;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.ActivityStarter;
@@ -51,7 +58,8 @@ public class MediaTransferManager {
}
ViewParent parent = view.getParent();
- StatusBarNotification statusBarNotification = getNotificationForParent(parent);
+ StatusBarNotification statusBarNotification =
+ getRowForParent(parent).getStatusBarNotification();
final Intent intent = new Intent()
.setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
.putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
@@ -60,16 +68,6 @@ public class MediaTransferManager {
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
return true;
}
-
- private StatusBarNotification getNotificationForParent(ViewParent parent) {
- while (parent != null) {
- if (parent instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) parent).getStatusBarNotification();
- }
- parent = parent.getParent();
- }
- return null;
- }
};
public MediaTransferManager(Context context) {
@@ -77,6 +75,16 @@ public class MediaTransferManager {
mActivityStarter = Dependency.get(ActivityStarter.class);
}
+ private ExpandableNotificationRow getRowForParent(ViewParent parent) {
+ while (parent != null) {
+ if (parent instanceof ExpandableNotificationRow) {
+ return ((ExpandableNotificationRow) parent);
+ }
+ parent = parent.getParent();
+ }
+ return null;
+ }
+
/**
* apply the action button for MediaTransfer
*
@@ -95,5 +103,23 @@ public class MediaTransferManager {
view.setVisibility(View.VISIBLE);
view.setOnClickListener(mOnClickHandler);
+
+ ExpandableNotificationRow enr = getRowForParent(view.getParent());
+ int color = enr.getNotificationHeader().getOriginalIconColor();
+ ColorStateList tintList = ColorStateList.valueOf(color);
+
+ // Update the outline color
+ LinearLayout viewLayout = (LinearLayout) view;
+ RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground();
+ GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0);
+ rect.setStroke(2, color);
+
+ // Update the image color
+ ImageView image = view.findViewById(R.id.media_seamless_image);
+ image.setImageTintList(tintList);
+
+ // Update the text color
+ TextView text = view.findViewById(R.id.media_seamless_text);
+ text.setTextColor(tintList);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 276afa7a3a94..a70dc7c0ec5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -33,6 +33,7 @@ import com.android.systemui.Gefingerpoken
import com.android.systemui.Interpolators
import com.android.systemui.R
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -56,7 +57,8 @@ constructor(
private val wakeUpCoordinator: NotificationWakeUpCoordinator,
private val bypassController: KeyguardBypassController,
private val headsUpManager: HeadsUpManagerPhone,
- private val roundnessManager: NotificationRoundnessManager
+ private val roundnessManager: NotificationRoundnessManager,
+ private val statusBarStateController: StatusBarStateController
) : Gefingerpoken {
companion object {
private val RUBBERBAND_FACTOR_STATIC = 0.25f
@@ -188,7 +190,8 @@ constructor(
MotionEvent.ACTION_MOVE -> updateExpansionHeight(moveDistance)
MotionEvent.ACTION_UP -> {
velocityTracker!!.computeCurrentVelocity(1000 /* units */)
- val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000
+ val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000 &&
+ statusBarStateController.state != StatusBarState.SHADE
if (!mFalsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) {
finishExpansion()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 4422a81874c6..8b9268e1888a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -20,6 +20,7 @@ import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.text.format.DateFormat;
import android.util.FloatProperty;
+import android.util.Log;
import android.view.View;
import android.view.animation.Interpolator;
@@ -137,6 +138,11 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
// Record the to-be mState and mLastState
recordHistoricalState(state, mState);
+ // b/139259891
+ if (mState == StatusBarState.SHADE && state == StatusBarState.SHADE_LOCKED) {
+ Log.e(TAG, "Invalid state transition: SHADE -> SHADE_LOCKED", new Throwable());
+ }
+
synchronized (mListeners) {
String tag = getClass().getSimpleName() + "#setState(" + state + ")";
DejankUtils.startDetectingBlockingIpcs(tag);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 91d47077fc31..13c6f2730d08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -34,7 +34,6 @@ import android.view.View;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Interpolators;
-import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -145,6 +144,7 @@ public class ActivityLaunchAnimator {
@Override
public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
+ RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
mSourceNotification.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index a33d23c0b5d5..c3211e307845 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -141,6 +141,12 @@ public final class NotificationEntry {
private boolean hasSentReply;
/**
+ * Whether this notification has changed in visual appearance since the previous post.
+ * New notifications are interruptive by default.
+ */
+ public boolean isVisuallyInterruptive;
+
+ /**
* Whether this notification is shown to the user as a high priority notification: visible on
* the lock screen/status bar and in the top section in the shade.
*/
@@ -205,6 +211,7 @@ public final class NotificationEntry {
+ " doesn't match existing key " + key);
}
mRanking = ranking;
+ isVisuallyInterruptive = ranking.visuallyInterruptive();
}
public NotificationChannel getChannel() {
@@ -244,6 +251,7 @@ public final class NotificationEntry {
return mRanking.canBubble();
}
+
public @NonNull List<Notification.Action> getSmartActions() {
return mRanking.getSmartActions();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 48a82957bf1e..a612a1721c41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -264,7 +264,7 @@ public class NotificationContentInflater {
mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
packageContext);
result = inflateSmartReplyViews(result, reInflateFlags, mRow.getEntry(),
- mRow.getContext(), mRow.getHeadsUpManager(),
+ mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
mRow.getExistingSmartRepliesAndActions());
apply(
inflateSynchronously,
@@ -311,20 +311,21 @@ public class NotificationContentInflater {
private static InflationProgress inflateSmartReplyViews(InflationProgress result,
@InflationFlag int reInflateFlags, NotificationEntry entry, Context context,
- HeadsUpManager headsUpManager, SmartRepliesAndActions previousSmartRepliesAndActions) {
+ Context packageContext, HeadsUpManager headsUpManager,
+ SmartRepliesAndActions previousSmartRepliesAndActions) {
SmartReplyConstants smartReplyConstants = Dependency.get(SmartReplyConstants.class);
SmartReplyController smartReplyController = Dependency.get(SmartReplyController.class);
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) {
result.expandedInflatedSmartReplies =
InflatedSmartReplies.inflate(
- context, entry, smartReplyConstants, smartReplyController,
- headsUpManager, previousSmartRepliesAndActions);
+ context, packageContext, entry, smartReplyConstants,
+ smartReplyController, headsUpManager, previousSmartRepliesAndActions);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) {
result.headsUpInflatedSmartReplies =
InflatedSmartReplies.inflate(
- context, entry, smartReplyConstants, smartReplyController,
- headsUpManager, previousSmartRepliesAndActions);
+ context, packageContext, entry, smartReplyConstants,
+ smartReplyController, headsUpManager, previousSmartRepliesAndActions);
}
return result;
}
@@ -817,7 +818,7 @@ public class NotificationContentInflater {
recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
mUsesIncreasedHeadsUpHeight, packageContext);
return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
- mRow.getContext(), mRow.getHeadsUpManager(),
+ mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
mRow.getExistingSmartRepliesAndActions());
} catch (Exception e) {
mError = e;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index cc2078bd158b..f30a8b12ab39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1139,19 +1139,21 @@ public class NotificationContentView extends FrameLayout {
}
private void applyMediaTransfer(final NotificationEntry entry) {
- View bigContentView = mExpandedChild;
- if (bigContentView == null || !entry.isMediaNotification()) {
+ if (!entry.isMediaNotification()) {
return;
}
- View mediaActionContainer = bigContentView.findViewById(
- com.android.internal.R.id.media_actions);
- if (!(mediaActionContainer instanceof LinearLayout)) {
- return;
+ View bigContentView = mExpandedChild;
+ if (bigContentView != null && (bigContentView instanceof ViewGroup)) {
+ mMediaTransferManager.applyMediaTransferView((ViewGroup) bigContentView,
+ entry);
}
- mMediaTransferManager.applyMediaTransferView((ViewGroup) mediaActionContainer,
- entry);
+ View smallContentView = mContractedChild;
+ if (smallContentView != null && (smallContentView instanceof ViewGroup)) {
+ mMediaTransferManager.applyMediaTransferView((ViewGroup) smallContentView,
+ entry);
+ }
}
private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 41c6a7ba7848..2d012c93f42b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -448,9 +448,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
} else if (!unlockingAllowed) {
return bypass ? MODE_SHOW_BOUNCER : MODE_NONE;
} else if (mDozeScrimController.isPulsing()) {
- // Let's not wake-up to lock screen when not bypassing, otherwise the notification
- // would move as the user tried to tap it.
- return bypass ? MODE_WAKE_AND_UNLOCK_PULSING : MODE_NONE;
+ return bypass ? MODE_WAKE_AND_UNLOCK_PULSING : MODE_ONLY_WAKE;
} else {
if (bypass) {
// Wake-up fading out nicely
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 575b5597b657..4cd3ad27ab34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone;
import static android.view.Display.INVALID_DISPLAY;
import android.content.Context;
-import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -36,8 +35,6 @@ import android.util.Log;
import android.util.MathUtils;
import android.util.StatsLog;
import android.view.Gravity;
-import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
import android.view.ISystemGestureExclusionListener;
import android.view.InputChannel;
import android.view.InputDevice;
@@ -57,6 +54,7 @@ import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -72,35 +70,13 @@ public class EdgeBackGestureHandler implements DisplayListener {
private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
"gestures.back_timeout", 250);
- private final IPinnedStackListener.Stub mImeChangedListener = new IPinnedStackListener.Stub() {
- @Override
- public void onListenerRegistered(IPinnedStackController controller) {
- }
-
+ private final PinnedStackListener mImeChangedListener = new PinnedStackListener() {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
// No need to thread jump, assignments are atomic
mImeHeight = imeVisible ? imeHeight : 0;
// TODO: Probably cancel any existing gesture
}
-
- @Override
- public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
- }
-
- @Override
- public void onMinimizedStateChanged(boolean isMinimized) {
- }
-
- @Override
- public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment,
- int displayRotation) {
- }
-
- @Override
- public void onActionsChanged(ParceledListSlice actions) {
- }
};
private ISystemGestureExclusionListener mGestureExclusionListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index f7bb97b38dd5..00b764bfbe9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -24,7 +24,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dependency
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.util.Assert
-import com.android.systemui.util.AsyncSensorManager
+import com.android.systemui.util.sensors.AsyncSensorManager
class KeyguardLiftController constructor(
private val statusBarStateController: StatusBarStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index ba3406999388..1a3560ece1d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -537,11 +537,7 @@ public class NotificationIconAreaController implements DarkReceiver,
if (dozeParameters.shouldControlScreenOff()) {
mAodIcons.setTranslationY(-mAodIconAppearTranslation);
mAodIcons.setAlpha(0);
- mAodIcons.animate()
- .setInterpolator(Interpolators.DECELERATE_QUINT)
- .translationY(0)
- .setDuration(AOD_ICONS_APPEAR_DURATION)
- .start();
+ animateInAodIconTranslation();
mAodIcons.animate()
.alpha(1)
.setInterpolator(Interpolators.LINEAR)
@@ -550,6 +546,14 @@ public class NotificationIconAreaController implements DarkReceiver,
}
}
+ private void animateInAodIconTranslation() {
+ mAodIcons.animate()
+ .setInterpolator(Interpolators.DECELERATE_QUINT)
+ .translationY(0)
+ .setDuration(AOD_ICONS_APPEAR_DURATION)
+ .start();
+ }
+
private void reloadAodColor() {
mAodIconTint = Utils.getColorAttrDefaultColor(mContext,
R.attr.wallpaperTextColor);
@@ -606,14 +610,19 @@ public class NotificationIconAreaController implements DarkReceiver,
mAodIcons.setAlpha(1.0f);
appearAodIcons();
} else {
+ // Let's make sure the icon are translated to 0, since we cancelled it above
+ animateInAodIconTranslation();
// We were fading out, let's fade in instead
CrossFadeHelper.fadeIn(mAodIcons);
}
} else {
+ // Let's make sure the icon are translated to 0, since we cancelled it above
+ animateInAodIconTranslation();
CrossFadeHelper.fadeOut(mAodIcons);
}
} else {
mAodIcons.setAlpha(1.0f);
+ mAodIcons.setTranslationY(0);
mAodIcons.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
}
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 f853b638db46..8c95b844ae2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -503,8 +503,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo
* device is dozing when the light sensor is on.
*/
public void setAodFrontScrimAlpha(float alpha) {
- if (((mState == ScrimState.AOD && mDozeParameters.getAlwaysOn())
- || mState == ScrimState.PULSING) && mInFrontAlpha != alpha) {
+ if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()
+ && mInFrontAlpha != alpha) {
mInFrontAlpha = alpha;
updateScrims();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 40c3d9d19c16..0a2fb2e783a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.policy;
import static com.android.systemui.Dependency.BG_LOOPER_NAME;
+import static com.android.systemui.Dependency.MAIN_LOOPER_NAME;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -67,16 +68,18 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
private boolean mEnabled;
private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
- private final H mHandler = new H(Looper.getMainLooper());
+ private final H mHandler;
private int mState;
/**
*/
@Inject
public BluetoothControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper,
+ @Named(MAIN_LOOPER_NAME) Looper mainLooper,
@Nullable LocalBluetoothManager localBluetoothManager) {
mLocalBluetoothManager = localBluetoothManager;
mBgHandler = new Handler(bgLooper);
+ mHandler = new H(mainLooper);
if (mLocalBluetoothManager != null) {
mLocalBluetoothManager.getEventManager().registerCallback(this);
mLocalBluetoothManager.getProfileManager().addServiceListener(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
index 25a32b330f7d..be27741b4dc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java
@@ -80,6 +80,7 @@ public class InflatedSmartReplies {
*/
public static InflatedSmartReplies inflate(
Context context,
+ Context packageContext,
NotificationEntry entry,
SmartReplyConstants smartReplyConstants,
SmartReplyController smartReplyController,
@@ -108,9 +109,9 @@ public class InflatedSmartReplies {
}
if (newSmartRepliesAndActions.smartActions != null) {
suggestionButtons.addAll(
- smartReplyView.inflateSmartActions(newSmartRepliesAndActions.smartActions,
- smartReplyController, entry, headsUpManager,
- delayOnClickListener));
+ smartReplyView.inflateSmartActions(packageContext,
+ newSmartRepliesAndActions.smartActions, smartReplyController, entry,
+ headsUpManager, delayOnClickListener));
}
return new InflatedSmartReplies(smartReplyView, suggestionButtons,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index dbfb09f7fc41..5da726749580 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -294,7 +294,8 @@ public class MobileSignalController extends SignalController<
}
boolean dataDisabled = mCurrentState.userSetup
&& (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
- || mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA);
+ || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA
+ && mCurrentState.defaultDataOff));
boolean noInternet = mCurrentState.inetCondition == 0;
boolean cutOut = dataDisabled || noInternet;
return SignalDrawable.getState(level, getNumLevels(), cutOut);
@@ -320,7 +321,7 @@ public class MobileSignalController extends SignalController<
dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
}
final boolean dataDisabled = (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
- || mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA)
+ || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA))
&& mCurrentState.userSetup;
// Show icon in QS when we are connected or data is disabled.
@@ -484,6 +485,7 @@ public class MobileSignalController extends SignalController<
Log.d(mTag, "updateTelephonySignalStrength: hasService=" +
Utils.isInService(mServiceState) + " ss=" + mSignalStrength);
}
+ checkDefaultData();
mCurrentState.connected = Utils.isInService(mServiceState)
&& mSignalStrength != null;
if (mCurrentState.connected) {
@@ -541,6 +543,23 @@ public class MobileSignalController extends SignalController<
notifyListenersIfNecessary();
}
+ /**
+ * If we are controlling the NOT_DEFAULT_DATA icon, check the status of the other one
+ */
+ private void checkDefaultData() {
+ if (mCurrentState.iconGroup != TelephonyIcons.NOT_DEFAULT_DATA) {
+ mCurrentState.defaultDataOff = false;
+ return;
+ }
+
+ mCurrentState.defaultDataOff = mNetworkController.isDataControllerDisabled();
+ }
+
+ void onMobileDataChanged() {
+ checkDefaultData();
+ notifyListenersIfNecessary();
+ }
+
private MobileIconGroup getNr5GIconGroup() {
if (mServiceState == null) return null;
@@ -617,7 +636,7 @@ public class MobileSignalController extends SignalController<
return candidateIconGroup;
}
- private boolean isDataDisabled() {
+ boolean isDataDisabled() {
return !mPhone.isDataCapable();
}
@@ -750,6 +769,7 @@ public class MobileSignalController extends SignalController<
boolean isDefault;
boolean userSetup;
boolean roaming;
+ boolean defaultDataOff; // Tracks the on/off state of the defaultDataSubscription
@Override
public void copyFrom(State s) {
@@ -765,6 +785,7 @@ public class MobileSignalController extends SignalController<
carrierNetworkChangeMode = state.carrierNetworkChangeMode;
userSetup = state.userSetup;
roaming = state.roaming;
+ defaultDataOff = state.defaultDataOff;
}
@Override
@@ -781,7 +802,8 @@ public class MobileSignalController extends SignalController<
builder.append("airplaneMode=").append(airplaneMode).append(',');
builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode)
.append(',');
- builder.append("userSetup=").append(userSetup);
+ builder.append("userSetup=").append(userSetup).append(',');
+ builder.append("defaultDataOff=").append(defaultDataOff);
}
@Override
@@ -796,7 +818,8 @@ public class MobileSignalController extends SignalController<
&& ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
&& ((MobileState) o).userSetup == userSetup
&& ((MobileState) o).isDefault == isDefault
- && ((MobileState) o).roaming == roaming;
+ && ((MobileState) o).roaming == roaming
+ && ((MobileState) o).defaultDataOff == defaultDataOff;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index e1b3816cf759..7b5d48947498 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -220,6 +220,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
@Override
public void onMobileDataEnabled(boolean enabled) {
mCallbackHandler.setMobileDataEnabled(enabled);
+ notifyControllersMobileDataChanged();
}
});
mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
@@ -386,6 +387,22 @@ public class NetworkControllerImpl extends BroadcastReceiver
return mMobileSignalControllers.size();
}
+ boolean isDataControllerDisabled() {
+ MobileSignalController dataController = getDataController();
+ if (dataController == null) {
+ return false;
+ }
+
+ return dataController.isDataDisabled();
+ }
+
+ private void notifyControllersMobileDataChanged() {
+ for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+ MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
+ mobileSignalController.onMobileDataChanged();
+ }
+ }
+
public boolean isEmergencyOnly() {
if (mMobileSignalControllers.size() == 0) {
// When there are no active subscriptions, determine emengency state from last
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 4fdaa6300065..b5f660a84043 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -235,17 +235,17 @@ public class SmartReplyView extends ViewGroup {
* Add smart actions to be shown next to smart replies. Only the actions that fit into the
* notification are shown.
*/
- public List<Button> inflateSmartActions(@NonNull SmartActions smartActions,
- SmartReplyController smartReplyController, NotificationEntry entry,
- HeadsUpManager headsUpManager, boolean delayOnClickListener) {
+ public List<Button> inflateSmartActions(Context packageContext,
+ @NonNull SmartActions smartActions, SmartReplyController smartReplyController,
+ NotificationEntry entry, HeadsUpManager headsUpManager, boolean delayOnClickListener) {
List<Button> buttons = new ArrayList<>();
int numSmartActions = smartActions.actions.size();
for (int n = 0; n < numSmartActions; n++) {
Notification.Action action = smartActions.actions.get(n);
if (action.actionIntent != null) {
buttons.add(inflateActionButton(
- this, getContext(), n, smartActions, smartReplyController, entry,
- headsUpManager, delayOnClickListener));
+ this, getContext(), packageContext, n, smartActions, smartReplyController,
+ entry, headsUpManager, delayOnClickListener));
}
}
return buttons;
@@ -327,7 +327,7 @@ public class SmartReplyView extends ViewGroup {
@VisibleForTesting
static Button inflateActionButton(SmartReplyView smartReplyView, Context context,
- int actionIndex, SmartActions smartActions,
+ Context packageContext, int actionIndex, SmartActions smartActions,
SmartReplyController smartReplyController, NotificationEntry entry,
HeadsUpManager headsUpManager, boolean useDelayedOnClickListener) {
Notification.Action action = smartActions.actions.get(actionIndex);
@@ -335,7 +335,9 @@ public class SmartReplyView extends ViewGroup {
R.layout.smart_action_button, smartReplyView, false);
button.setText(action.title);
- Drawable iconDrawable = action.getIcon().loadDrawable(context);
+ // We received the Icon from the application - so use the Context of the application to
+ // reference icon resources.
+ Drawable iconDrawable = action.getIcon().loadDrawable(packageContext);
// Add the action icon to the Smart Action button.
int newIconSize = context.getResources().getDimensionPixelSize(
R.dimen.smart_action_button_icon_size);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
index 97507900e269..453c2f7da71f 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -31,15 +31,21 @@ import androidx.preference.PreferenceScreen;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.SystemUIFactory;
import com.android.systemui.fragments.FragmentService;
+import javax.inject.Inject;
+
public class TunerActivity extends Activity implements
PreferenceFragment.OnPreferenceStartFragmentCallback,
PreferenceFragment.OnPreferenceStartScreenCallback {
private static final String TAG_TUNER = "tuner";
+ @Inject
+ TunerActivity() {
+ super();
+ }
+
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -51,8 +57,6 @@ public class TunerActivity extends Activity implements
setActionBar(toolbar);
}
- Dependency.initDependencies(SystemUIFactory.getInstance().getRootComponent());
-
if (getFragmentManager().findFragmentByTag(TAG_TUNER) == null) {
final String action = getIntent().getAction();
boolean showDemoMode = action != null && action.equals(
@@ -68,7 +72,6 @@ public class TunerActivity extends Activity implements
protected void onDestroy() {
super.onDestroy();
Dependency.destroy(FragmentService.class, s -> s.destroyAll());
- Dependency.clearDependencies();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java
deleted file mode 100644
index a905eba1f0ed..000000000000
--- a/packages/SystemUI/src/com/android/systemui/util/ProximitySensor.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.util;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.util.Log;
-
-import com.android.systemui.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Simple wrapper around SensorManager customized for the Proximity sensor.
- */
-public class ProximitySensor {
- private static final String TAG = "ProxSensor";
- private static final boolean DEBUG = false;
-
- private final Sensor mSensor;
- private final AsyncSensorManager mSensorManager;
- private final boolean mUsingBrightnessSensor;
- private final float mMaxRange;
-
- private SensorEventListener mSensorEventListener = new SensorEventListener() {
- @Override
- public synchronized void onSensorChanged(SensorEvent event) {
- onSensorEvent(event);
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
- };
- private boolean mNear;
- private List<ProximitySensorListener> mListeners = new ArrayList<>();
- private String mTag = null;
-
- @Inject
- public ProximitySensor(Context context, AsyncSensorManager sensorManager) {
- mSensorManager = sensorManager;
- Sensor sensor = findBrightnessSensor(context, sensorManager);
-
- if (sensor == null) {
- mUsingBrightnessSensor = false;
- sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- } else {
- mUsingBrightnessSensor = true;
- }
- mSensor = sensor;
- if (mSensor != null) {
- mMaxRange = mSensor.getMaximumRange();
- } else {
- mMaxRange = 0;
- }
- }
-
- public void setTag(String tag) {
- mTag = tag;
- }
-
- private Sensor findBrightnessSensor(Context context, SensorManager sensorManager) {
- String sensorType = context.getString(R.string.doze_brightness_sensor_type);
- List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
- Sensor sensor = null;
- for (Sensor s : sensorList) {
- if (sensorType.equals(s.getStringType())) {
- sensor = s;
- break;
- }
- }
-
- return sensor;
- }
-
- /**
- * Returns {@code false} if a Proximity sensor is not available.
- */
- public boolean getSensorAvailable() {
- return mSensor != null;
- }
-
- /**
- * Add a listener.
- *
- * Registers itself with the {@link SensorManager} if this is the first listener
- * added.
- */
- public boolean register(ProximitySensorListener listener) {
- if (!getSensorAvailable()) {
- return false;
- }
-
- logDebug("using brightness sensor? " + mUsingBrightnessSensor);
- mListeners.add(listener);
- if (mListeners.size() == 1) {
- logDebug("registering sensor listener");
- mSensorManager.registerListener(
- mSensorEventListener, mSensor, SensorManager.SENSOR_DELAY_GAME);
- }
-
- return true;
- }
-
- /**
- * Remove a listener.
- *
- * If all listeners are removed from an instance of this class,
- * it will unregister itself with the SensorManager.
- */
- public void unregister(ProximitySensorListener listener) {
- mListeners.remove(listener);
- if (mListeners.size() == 0) {
- logDebug("unregistering sensor listener");
- mSensorManager.unregisterListener(mSensorEventListener);
- }
- }
-
- public boolean isNear() {
- return getSensorAvailable() && mNear;
- }
-
- private void onSensorEvent(SensorEvent event) {
- boolean near = event.values[0] < mMaxRange;
- if (mUsingBrightnessSensor) {
- near = event.values[0] == 0;
- }
- mNear = near;
- mListeners.forEach(proximitySensorListener ->
- proximitySensorListener.onProximitySensorEvent(
- new ProximityEvent(mNear, event.timestamp)));
- }
-
- /** Implement to be notified of ProximityEvents. */
- public interface ProximitySensorListener {
- /** Called when the ProximitySensor changes. */
- void onProximitySensorEvent(ProximityEvent proximityEvent);
- }
-
- /**
- * Returned when the near/far state of a {@link ProximitySensor} changes.
- */
- public static class ProximityEvent {
- private final boolean mNear;
- private final long mTimestampNs;
-
- public ProximityEvent(boolean near, long timestampNs) {
- mNear = near;
- mTimestampNs = timestampNs;
- }
-
- public boolean getNear() {
- return mNear;
- }
-
- public long getTimestampNs() {
- return mTimestampNs;
- }
- }
-
- private void logDebug(String msg) {
- if (DEBUG) {
- Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
index b9c5ee5a7a7e..dcd0c58a5310 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.util;
+package com.android.systemui.util.sensors;
import android.content.Context;
import android.hardware.HardwareBuffer;
@@ -56,23 +56,31 @@ public class AsyncSensorManager extends SensorManager
private final SensorManager mInner;
private final List<Sensor> mSensorCache;
- private final HandlerThread mHandlerThread = new HandlerThread("async_sensor");
- @VisibleForTesting final Handler mHandler;
+ private final Handler mHandler;
private final List<SensorManagerPlugin> mPlugins;
@Inject
public AsyncSensorManager(Context context, PluginManager pluginManager) {
- this(context.getSystemService(SensorManager.class), pluginManager);
+ this(context.getSystemService(SensorManager.class), pluginManager, null);
}
@VisibleForTesting
- AsyncSensorManager(SensorManager sensorManager, PluginManager pluginManager) {
+ public AsyncSensorManager(
+ SensorManager sensorManager, PluginManager pluginManager, Handler handler) {
mInner = sensorManager;
- mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
+ if (handler == null) {
+ HandlerThread handlerThread = new HandlerThread("async_sensor");
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper());
+ } else {
+ mHandler = handler;
+ }
mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL);
mPlugins = new ArrayList<>();
- pluginManager.addPluginListener(this, SensorManagerPlugin.class, true /* allowMultiple */);
+ if (pluginManager != null) {
+ pluginManager.addPluginListener(this, SensorManagerPlugin.class,
+ true /* allowMultiple */);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
new file mode 100644
index 000000000000..c48bdde6adef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.shared.plugins.PluginManager;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+
+/**
+ * Simple wrapper around SensorManager customized for the Proximity sensor.
+ */
+public class ProximitySensor {
+ private static final String TAG = "ProxSensor";
+ private static final boolean DEBUG = false;
+
+ private final Sensor mSensor;
+ private final AsyncSensorManager mSensorManager;
+ private final boolean mUsingBrightnessSensor;
+ private final float mMaxRange;
+ private List<ProximitySensorListener> mListeners = new ArrayList<>();
+ private String mTag = null;
+ private ProximityEvent mLastEvent;
+ private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
+ private boolean mPaused;
+ private boolean mRegistered;
+
+ private SensorEventListener mSensorEventListener = new SensorEventListener() {
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ onSensorEvent(event);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ };
+
+ @Inject
+ public ProximitySensor(
+ Context context, AsyncSensorManager sensorManager, PluginManager pluginManager) {
+ mSensorManager = sensorManager;
+ Sensor sensor = findBrightnessSensor(context);
+
+ if (sensor == null) {
+ mUsingBrightnessSensor = false;
+ sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ } else {
+ mUsingBrightnessSensor = true;
+ }
+ mSensor = sensor;
+ if (mSensor != null) {
+ mMaxRange = mSensor.getMaximumRange();
+ } else {
+ mMaxRange = 0;
+ }
+ }
+
+ public void setTag(String tag) {
+ mTag = tag;
+ }
+
+ public void setSensorDelay(int sensorDelay) {
+ mSensorDelay = sensorDelay;
+ }
+
+ /**
+ * Unregister with the {@link SensorManager} without unsetting listeners on this object.
+ */
+ public void pause() {
+ mPaused = true;
+ unregisterInternal();
+ }
+
+ /**
+ * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
+ */
+ public void resume() {
+ mPaused = false;
+ registerInternal();
+ }
+
+ private Sensor findBrightnessSensor(Context context) {
+ String sensorType = context.getString(R.string.doze_brightness_sensor_type);
+ List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ Sensor sensor = null;
+ for (Sensor s : sensorList) {
+ if (sensorType.equals(s.getStringType())) {
+ sensor = s;
+ break;
+ }
+ }
+
+ return sensor;
+ }
+
+ /**
+ * Returns true if we are registered with the SensorManager.
+ */
+ public boolean isRegistered() {
+ return mRegistered;
+ }
+
+ /**
+ * Returns {@code false} if a Proximity sensor is not available.
+ */
+ public boolean getSensorAvailable() {
+ return mSensor != null;
+ }
+
+ /**
+ * Add a listener.
+ *
+ * Registers itself with the {@link SensorManager} if this is the first listener
+ * added. If a cool down is currently running, the sensor will be registered when it is over.
+ */
+ public boolean register(ProximitySensorListener listener) {
+ if (!getSensorAvailable()) {
+ return false;
+ }
+
+ logDebug("Using brightness sensor? " + mUsingBrightnessSensor);
+ mListeners.add(listener);
+ registerInternal();
+
+ return true;
+ }
+
+ private void registerInternal() {
+ if (mRegistered || mPaused || mListeners.isEmpty()) {
+ return;
+ }
+ logDebug("Registering sensor listener");
+ mRegistered = true;
+ mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
+ }
+
+ /**
+ * Remove a listener.
+ *
+ * If all listeners are removed from an instance of this class,
+ * it will unregister itself with the SensorManager.
+ */
+ public void unregister(ProximitySensorListener listener) {
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ unregisterInternal();
+ }
+ }
+
+ private void unregisterInternal() {
+ if (!mRegistered) {
+ return;
+ }
+ logDebug("unregistering sensor listener");
+ mSensorManager.unregisterListener(mSensorEventListener);
+ mRegistered = false;
+ }
+
+ public Boolean isNear() {
+ return getSensorAvailable() && mLastEvent != null ? mLastEvent.getNear() : null;
+ }
+
+ /** Update all listeners with the last value this class received from the sensor. */
+ public void alertListeners() {
+ mListeners.forEach(proximitySensorListener ->
+ proximitySensorListener.onSensorEvent(mLastEvent));
+ }
+
+ private void onSensorEvent(SensorEvent event) {
+ boolean near = event.values[0] < mMaxRange;
+ if (mUsingBrightnessSensor) {
+ near = event.values[0] == 0;
+ }
+ mLastEvent = new ProximityEvent(near, event.timestamp);
+ alertListeners();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{registered=%s, paused=%s, near=%s, sensor=%s}",
+ isRegistered(), mPaused, isNear(), mSensor);
+ }
+
+ /**
+ * Convenience class allowing for briefly checking the proximity sensor.
+ */
+ public static class ProximityCheck implements Runnable {
+
+ private final ProximitySensor mSensor;
+ private final Handler mHandler;
+ private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
+
+ @Inject
+ public ProximityCheck(ProximitySensor sensor, Handler handler) {
+ mSensor = sensor;
+ mSensor.setTag("prox_check");
+ mHandler = handler;
+ mSensor.pause();
+ ProximitySensorListener listener = proximityEvent -> {
+ mCallbacks.forEach(
+ booleanConsumer ->
+ booleanConsumer.accept(
+ proximityEvent == null ? null : proximityEvent.getNear()));
+ mCallbacks.clear();
+ mSensor.pause();
+ };
+ mSensor.register(listener);
+ }
+
+ /** Set a descriptive tag for the sensors registration. */
+ public void setTag(String tag) {
+ mSensor.setTag(tag);
+ }
+
+ @Override
+ public void run() {
+ mSensor.pause();
+ mSensor.alertListeners();
+ }
+
+ /**
+ * Query the proximity sensor, timing out if no result.
+ */
+ public void check(long timeoutMs, Consumer<Boolean> callback) {
+ if (!mSensor.getSensorAvailable()) {
+ callback.accept(null);
+ }
+ mCallbacks.add(callback);
+ if (!mSensor.isRegistered()) {
+ mSensor.resume();
+ mHandler.postDelayed(this, timeoutMs);
+ }
+ }
+ }
+
+ /** Implement to be notified of ProximityEvents. */
+ public interface ProximitySensorListener {
+ /** Called when the ProximitySensor changes. */
+ void onSensorEvent(ProximityEvent proximityEvent);
+ }
+
+ /**
+ * Returned when the near/far state of a {@link ProximitySensor} changes.
+ */
+ public static class ProximityEvent {
+ private final boolean mNear;
+ private final long mTimestampNs;
+
+ public ProximityEvent(boolean near, long timestampNs) {
+ mNear = near;
+ mTimestampNs = timestampNs;
+ }
+
+ public boolean getNear() {
+ return mNear;
+ }
+
+ public long getTimestampNs() {
+ return mTimestampNs;
+ }
+
+ public long getTimestampMs() {
+ return mTimestampNs / 1000000;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mNear, mTimestampNs);
+ }
+
+ }
+
+ private void logDebug(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index b9d09ce91c1a..939df10724ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -74,6 +74,9 @@ public class DependencyTest extends SysuiTestCase {
@Test
public void testInitDependency() {
Dependency.clearDependencies();
- Dependency.initDependencies(SystemUIFactory.getInstance().getRootComponent());
+ Dependency dependency = new Dependency();
+ SystemUIFactory
+ .getInstance().getRootComponent().createDependency().createSystemUI(dependency);
+ dependency.start();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java
index c53289c62d94..fe131275afd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/AssistHandleLikeHomeBehaviorTest.java
@@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
import org.junit.Before;
@@ -47,6 +48,7 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
private AssistHandleLikeHomeBehavior mAssistHandleLikeHomeBehavior;
+ @Mock private StatusBarStateController mMockStatusBarStateController;
@Mock private WakefulnessLifecycle mMockWakefulnessLifecycle;
@Mock private SysUiState mMockSysUiState;
@Mock private AssistHandleCallbacks mMockAssistHandleCallbacks;
@@ -55,7 +57,9 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
public void setup() {
MockitoAnnotations.initMocks(this);
mAssistHandleLikeHomeBehavior = new AssistHandleLikeHomeBehavior(
- () -> mMockWakefulnessLifecycle, () -> mMockSysUiState);
+ () -> mMockStatusBarStateController,
+ () -> mMockWakefulnessLifecycle,
+ () -> mMockSysUiState);
}
@Test
@@ -66,6 +70,9 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
// Assert
+ verify(mMockStatusBarStateController).isDozing();
+ verify(mMockStatusBarStateController).addCallback(
+ any(StatusBarStateController.StateListener.class));
verify(mMockWakefulnessLifecycle).getWakefulness();
verify(mMockWakefulnessLifecycle).addObserver(any(WakefulnessLifecycle.Observer.class));
verify(mMockSysUiState).addCallback(any(SysUiState.SysUiStateCallback.class));
@@ -73,8 +80,9 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
}
@Test
- public void onModeActivated_showsHandlesWhenAwake() {
+ public void onModeActivated_showsHandlesWhenFullyAwake() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(false);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
@@ -89,6 +97,7 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
@Test
public void onModeActivated_hidesHandlesWhenNotAwake() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(true);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP);
@@ -101,72 +110,139 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
}
@Test
+ public void onModeActivated_hidesHandlesWhenDozing() {
+ // Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(true);
+ when(mMockWakefulnessLifecycle.getWakefulness())
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+
+ // Act
+ mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).hide();
+ verifyNoMoreInteractions(mMockAssistHandleCallbacks);
+ }
+
+ @Test
public void onModeDeactivated_stopsObserving() {
// Arrange
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ ArgumentCaptor<StatusBarStateController.StateListener> stateListener =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
ArgumentCaptor<WakefulnessLifecycle.Observer> observer =
ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
ArgumentCaptor<SysUiState.SysUiStateCallback> sysUiStateCallback =
ArgumentCaptor.forClass(SysUiState.SysUiStateCallback.class);
+ verify(mMockStatusBarStateController).addCallback(stateListener.capture());
verify(mMockWakefulnessLifecycle).addObserver(observer.capture());
verify(mMockSysUiState).addCallback(sysUiStateCallback.capture());
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
mAssistHandleLikeHomeBehavior.onModeDeactivated();
// Assert
+ verify(mMockStatusBarStateController).removeCallback(eq(stateListener.getValue()));
verify(mMockWakefulnessLifecycle).removeObserver(eq(observer.getValue()));
verify(mMockSysUiState).removeCallback(eq(sysUiStateCallback.getValue()));
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
public void onAssistantGesturePerformed_doesNothing() {
// Arrange
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
mAssistHandleLikeHomeBehavior.onAssistantGesturePerformed();
// Assert
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
public void onAssistHandlesRequested_doesNothing() {
// Arrange
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
mAssistHandleLikeHomeBehavior.onAssistHandlesRequested();
// Assert
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
- public void onWake_handlesShow() {
+ public void onBothAwakeAndUnDoze_handlesShow() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(true);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP);
+ ArgumentCaptor<StatusBarStateController.StateListener> stateListener =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
ArgumentCaptor<WakefulnessLifecycle.Observer> observer =
ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ verify(mMockStatusBarStateController).addCallback(stateListener.capture());
verify(mMockWakefulnessLifecycle).addObserver(observer.capture());
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
- observer.getValue().onStartedWakingUp();
+ observer.getValue().onFinishedWakingUp();
// Assert
+ verify(mMockAssistHandleCallbacks).hide();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
+
+ // Arrange
+ observer.getValue().onFinishedGoingToSleep();
+ reset(mMockAssistHandleCallbacks);
+
+ // Act
+ stateListener.getValue().onDozingChanged(false);
+
+ // Assert
+ verify(mMockAssistHandleCallbacks).hide();
+ verifyNoMoreInteractions(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
observer.getValue().onFinishedWakingUp();
@@ -174,19 +250,30 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
// Assert
verify(mMockAssistHandleCallbacks).showAndStay();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
- public void onSleep_handlesHide() {
+ public void onSleepOrDoze_handlesHide() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(false);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+ ArgumentCaptor<StatusBarStateController.StateListener> stateListener =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
ArgumentCaptor<WakefulnessLifecycle.Observer> observer =
ArgumentCaptor.forClass(WakefulnessLifecycle.Observer.class);
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
+ verify(mMockStatusBarStateController).addCallback(stateListener.capture());
verify(mMockWakefulnessLifecycle).addObserver(observer.capture());
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
observer.getValue().onStartedGoingToSleep();
@@ -194,26 +281,42 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
// Assert
verify(mMockAssistHandleCallbacks).hide();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
+
+ // Arrange
+ observer.getValue().onFinishedWakingUp();
+ reset(mMockAssistHandleCallbacks);
// Act
- observer.getValue().onFinishedGoingToSleep();
+ stateListener.getValue().onDozingChanged(true);
// Assert
+ verify(mMockAssistHandleCallbacks).hide();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
public void onHomeHandleHide_handlesHide() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(false);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
ArgumentCaptor<SysUiState.SysUiStateCallback> sysUiStateCallback =
ArgumentCaptor.forClass(SysUiState.SysUiStateCallback.class);
mAssistHandleLikeHomeBehavior.onModeActivated(mContext, mMockAssistHandleCallbacks);
verify(mMockSysUiState).addCallback(sysUiStateCallback.capture());
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
sysUiStateCallback.getValue().onSystemUiStateChanged(
@@ -222,12 +325,16 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
// Assert
verify(mMockAssistHandleCallbacks).hide();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
@Test
public void onHomeHandleUnhide_handlesShow() {
// Arrange
+ when(mMockStatusBarStateController.isDozing()).thenReturn(false);
when(mMockWakefulnessLifecycle.getWakefulness())
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
ArgumentCaptor<SysUiState.SysUiStateCallback> sysUiStateCallback =
@@ -236,7 +343,11 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
verify(mMockSysUiState).addCallback(sysUiStateCallback.capture());
sysUiStateCallback.getValue().onSystemUiStateChanged(
QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN);
- reset(mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ reset(
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
// Act
sysUiStateCallback.getValue().onSystemUiStateChanged(
@@ -245,6 +356,9 @@ public class AssistHandleLikeHomeBehaviorTest extends SysuiTestCase {
// Assert
verify(mMockAssistHandleCallbacks).showAndStay();
verifyNoMoreInteractions(
- mMockWakefulnessLifecycle, mMockSysUiState, mMockAssistHandleCallbacks);
+ mMockStatusBarStateController,
+ mMockWakefulnessLifecycle,
+ mMockSysUiState,
+ mMockAssistHandleCallbacks);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
index b9793562d8d8..3561e3465898 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -34,7 +34,7 @@ import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
import org.junit.After;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
index 3fc5d7202d0a..35d59c1c9726 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
@@ -30,7 +30,7 @@ import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
import com.android.systemui.util.DeviceConfigProxyFake;
-import com.android.systemui.util.ProximitySensor;
+import com.android.systemui.util.sensors.ProximitySensor;
import org.junit.After;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index 0c124fff53a3..752e145d79bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.utils.hardware.FakeSensorManager;
+import com.android.systemui.util.sensors.FakeSensorManager;
import org.mockito.Answers;
import org.mockito.MockSettings;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 392c677b9827..aa62e9aca4fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -40,7 +40,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.hardware.FakeSensorManager;
+import com.android.systemui.util.sensors.FakeSensorManager;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index cd6d1e069566..ddd1685bf8bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -44,7 +44,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.DozeSensors.TriggerSensor;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.AsyncSensorManager;
+import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.wakelock.WakeLock;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index e190f9923da8..f7cd69643fc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -32,6 +32,7 @@ import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
@@ -39,9 +40,10 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.FakeSensorManager;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
-import com.android.systemui.utils.hardware.FakeSensorManager;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -55,12 +57,8 @@ public class DozeTriggersTest extends SysuiTestCase {
private DozeTriggers mTriggers;
private DozeMachine mMachine;
private DozeHostFake mHost;
- private AmbientDisplayConfiguration mConfig;
- private DozeParameters mParameters;
private FakeSensorManager mSensors;
private Sensor mTapSensor;
- private WakeLock mWakeLock;
- private AlarmManager mAlarmManager;
private DockManager mDockManagerFake;
@BeforeClass
@@ -72,18 +70,21 @@ public class DozeTriggersTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
mMachine = mock(DozeMachine.class);
- mAlarmManager = mock(AlarmManager.class);
+ AlarmManager alarmManager = mock(AlarmManager.class);
mHost = spy(new DozeHostFake());
- mConfig = DozeConfigurationUtil.createMockConfig();
- mParameters = DozeConfigurationUtil.createMockParameters();
+ AmbientDisplayConfiguration config = DozeConfigurationUtil.createMockConfig();
+ DozeParameters parameters = DozeConfigurationUtil.createMockParameters();
mSensors = spy(new FakeSensorManager(mContext));
mTapSensor = mSensors.getFakeTapSensor().getSensor();
- mWakeLock = new WakeLockFake();
+ WakeLock wakeLock = new WakeLockFake();
mDockManagerFake = mock(DockManager.class);
+ AsyncSensorManager asyncSensorManager =
+ new AsyncSensorManager(mSensors, null, new Handler());
- mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters,
- mSensors, Handler.createAsync(Looper.myLooper()), mWakeLock, true,
+ mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters,
+ asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
mDockManagerFake);
+ waitForSensorManager();
}
@Test
@@ -95,13 +96,14 @@ public class DozeTriggersTest extends SysuiTestCase {
clearInvocations(mMachine);
mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
- mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */
+ mSensors.getFakeProximitySensor().sendProximityResult(false); /* Near */
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
- mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */
+ waitForSensorManager();
+ mSensors.getFakeProximitySensor().sendProximityResult(true); /* Far */
verify(mMachine).requestPulse(anyInt());
}
@@ -111,6 +113,7 @@ public class DozeTriggersTest extends SysuiTestCase {
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
+ waitForSensorManager();
verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor));
clearInvocations(mSensors);
@@ -118,10 +121,12 @@ public class DozeTriggersTest extends SysuiTestCase {
DozeMachine.State.DOZE_REQUEST_PULSE);
mTriggers.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING);
+ waitForSensorManager();
verify(mSensors).cancelTriggerSensor(any(), eq(mTapSensor));
clearInvocations(mSensors);
mTriggers.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.DOZE_PULSE_DONE);
+ waitForSensorManager();
verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor));
}
@@ -133,4 +138,8 @@ public class DozeTriggersTest extends SysuiTestCase {
mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH);
verify(mDockManagerFake).removeListener(any());
}
+
+ private void waitForSensorManager() {
+ TestableLooper.get(this).processAllMessages();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
index 05f179ed4620..820f4652e685 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -51,6 +51,7 @@ public class RankingBuilder {
private ArrayList<Notification.Action> mSmartActions = new ArrayList<>();
private ArrayList<CharSequence> mSmartReplies = new ArrayList<>();
private boolean mCanBubble = false;
+ private boolean mIsVisuallyInterruptive = false;
public RankingBuilder() {
}
@@ -98,7 +99,8 @@ public class RankingBuilder {
mNoisy,
mSmartActions,
mSmartReplies,
- mCanBubble);
+ mCanBubble,
+ mIsVisuallyInterruptive);
return ranking;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 53d6bffdc896..30e02e6b46d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -183,7 +183,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
0,
NotificationManager.IMPORTANCE_DEFAULT,
null, null,
- null, null, null, true, sentiment, false, -1, false, null, null, false);
+ null, null, null, true, sentiment, false, -1, false, null, null, false, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}
@@ -202,7 +202,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
null, null,
null, null, null, true,
NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
- false, smartActions, null, false);
+ false, smartActions, null, false, false);
return true;
}).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index ab7f960d6a15..657ec61dfd11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -653,6 +653,7 @@ public class NotificationDataTest extends SysuiTestCase {
public static final String OVERRIDE_SMART_ACTIONS = "sa";
public static final String OVERRIDE_SMART_REPLIES = "sr";
public static final String OVERRIDE_BUBBLE = "cb";
+ public static final String OVERRIDE_VISUALLY_INTERRUPTIVE = "vi";
public Map<String, Bundle> rankingOverrides = new HashMap<>();
@@ -713,7 +714,9 @@ public class NotificationDataTest extends SysuiTestCase {
overrides.containsKey(OVERRIDE_SMART_REPLIES)
? overrides.getCharSequenceArrayList(OVERRIDE_SMART_REPLIES)
: currentReplies,
- overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble()));
+ overrides.getBoolean(OVERRIDE_BUBBLE, outRanking.canBubble()),
+ overrides.getBoolean(OVERRIDE_VISUALLY_INTERRUPTIVE,
+ outRanking.visuallyInterruptive()));
} else {
outRanking.populate(
new RankingBuilder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 2f2449473e1b..219aef1ec685 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -131,7 +131,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
mKeyguardBypassController);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator,
mKeyguardBypassController, mHeadsUpManager,
- mock(NotificationRoundnessManager.class));
+ mock(NotificationRoundnessManager.class), mStatusBarStateController);
mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
mKeyguardBypassController);
mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 5d3cdc88aa99..2623b46ba50f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -243,7 +243,7 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
- public void transitionToPulsing_withFrontAlphaUpdates() {
+ public void transitionToPulsing() {
// Pre-condition
// Need to go to AoD first because PULSING doesn't change
// the back scrim opacity - otherwise it would hide AoD wallpapers.
@@ -267,22 +267,11 @@ public class ScrimControllerTest extends SysuiTestCase {
true /* behind */,
false /* bubble */);
- // ... and when ambient goes dark, front scrim should be semi-transparent
- mScrimController.setAodFrontScrimAlpha(0.5f);
- mScrimController.finishAnimationsImmediately();
- // Front scrim should be semi-transparent
- assertScrimAlpha(SEMI_TRANSPARENT /* front */,
- OPAQUE /* back */,
- TRANSPARENT /* bubble */);
-
mScrimController.setWakeLockScreenSensorActive(true);
mScrimController.finishAnimationsImmediately();
- assertScrimAlpha(SEMI_TRANSPARENT /* front */,
+ assertScrimAlpha(TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
-
- // Reset value since enums are static.
- mScrimController.setAodFrontScrimAlpha(0f);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 766ad978f475..e629a4f03586 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
-import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -78,6 +77,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
mBluetoothControllerImpl = new BluetoothControllerImpl(mContext,
mTestableLooper.getLooper(),
+ mTestableLooper.getLooper(),
mMockBluetoothManager);
}
@@ -109,18 +109,13 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
BluetoothController.Callback callback = mock(BluetoothController.Callback.class);
mBluetoothControllerImpl.addCallback(callback);
- // Grab the main looper, we'll need it later.
- TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper());
-
// Trigger the state getting.
assertEquals(BluetoothDevice.BOND_NONE, mBluetoothControllerImpl.getBondState(device));
- mTestableLooper.processMessages(1);
- mainLooper.processAllMessages();
+ mTestableLooper.processAllMessages();
assertEquals(BluetoothDevice.BOND_BONDED, mBluetoothControllerImpl.getBondState(device));
verify(callback).onBluetoothDevicesChanged();
- mainLooper.destroy();
}
@Test
@@ -130,20 +125,15 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
BluetoothController.Callback callback = mock(BluetoothController.Callback.class);
mBluetoothControllerImpl.addCallback(callback);
- // Grab the main looper, we'll need it later.
- TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper());
-
// Trigger the state getting.
assertEquals(BluetoothProfile.STATE_DISCONNECTED,
mBluetoothControllerImpl.getMaxConnectionState(device));
- mTestableLooper.processMessages(1);
- mainLooper.processAllMessages();
+ mTestableLooper.processAllMessages();
assertEquals(BluetoothProfile.STATE_CONNECTED,
mBluetoothControllerImpl.getMaxConnectionState(device));
verify(callback).onBluetoothDevicesChanged();
- mainLooper.destroy();
}
@Test
@@ -153,19 +143,11 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
BluetoothController.Callback callback = mock(BluetoothController.Callback.class);
mBluetoothControllerImpl.addCallback(callback);
- // Grab the main looper, we'll need it later.
- TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper());
-
- try {
- // Trigger the state getting.
- assertEquals(BluetoothProfile.STATE_DISCONNECTED,
- mBluetoothControllerImpl.getMaxConnectionState(null));
+ // Trigger the state getting.
+ assertEquals(BluetoothProfile.STATE_DISCONNECTED,
+ mBluetoothControllerImpl.getMaxConnectionState(null));
- mTestableLooper.processMessages(1);
- mainLooper.processAllMessages();
- } finally {
- mainLooper.destroy();
- }
+ mTestableLooper.processAllMessages();
}
@Test
@@ -217,15 +199,8 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
mBluetoothControllerImpl.onAclConnectionStateChanged(device,
BluetoothProfile.STATE_CONNECTED);
- // Grab the main looper, we'll need it later.
- TestableLooper mainLooper = new TestableLooper(Looper.getMainLooper());
+ mTestableLooper.processAllMessages();
- try {
- mTestableLooper.processAllMessages();
- mainLooper.processAllMessages();
- } finally {
- mainLooper.destroy();
- }
assertTrue(mBluetoothControllerImpl.isBluetoothConnected());
verify(callback, atLeastOnce()).onBluetoothStateChange(anyBoolean());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 3ddfbdac6db8..aa4723acba62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -147,7 +147,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
}
@Test
- public void testNoInternetIcon_withoutDefaultSub() {
+ public void testNonDefaultSIM_showsFullSignal_connected() {
setupNetworkController();
when(mMockTm.isDataCapable()).thenReturn(false);
setupDefaultSignal();
@@ -158,11 +158,11 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
// Verify that a SignalDrawable with a cut out is used to display data disabled.
verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
- false, true, NOT_DEFAULT_DATA_STRING);
+ false, false, NOT_DEFAULT_DATA_STRING);
}
@Test
- public void testDataDisabledIcon_withoutDefaultSub() {
+ public void testNonDefaultSIM_showsFullSignal_disconnected() {
setupNetworkController();
when(mMockTm.isDataCapable()).thenReturn(false);
setupDefaultSignal();
@@ -173,7 +173,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest {
// Verify that a SignalDrawable with a cut out is used to display data disabled.
verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, 0,
true, DEFAULT_QS_SIGNAL_STRENGTH, 0, false,
- false, true, NOT_DEFAULT_DATA_STRING);
+ false, false, NOT_DEFAULT_DATA_STRING);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 0c6f25786190..0d56cbe84eb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -500,6 +500,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
private void setSmartActions(String[] actionTitles, boolean useDelayedOnClickListener) {
mView.resetSmartSuggestions(mContainer);
List<Button> actions = mView.inflateSmartActions(
+ getContext(),
new SmartReplyView.SmartActions(createActions(actionTitles), false),
mLogger,
mEntry,
@@ -520,6 +521,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
List<Button> smartSuggestions = inflateSmartReplies(choices, fromAssistant,
useDelayedOnClickListener);
smartSuggestions.addAll(mView.inflateSmartActions(
+ getContext(),
new SmartReplyView.SmartActions(createActions(actionTitles), fromAssistant),
mLogger,
mEntry,
@@ -866,7 +868,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
}
private Button inflateActionButton(Notification.Action action) {
- return SmartReplyView.inflateActionButton(mView, getContext(), 0,
+ return SmartReplyView.inflateActionButton(mView, getContext(), getContext(), 0,
new SmartReplyView.SmartActions(Collections.singletonList(action), false),
mLogger, mEntry, mHeadsUpManager, true /* useDelayedOnClickListener */);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
index 4a9b1b3c4006..9149599f2c7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,8 @@
* limitations under the License.
*/
-package com.android.systemui.util;
+package com.android.systemui.util.sensors;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -24,15 +23,15 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
+import android.os.Handler;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.utils.hardware.FakeSensorManager;
import org.junit.Before;
import org.junit.Test;
@@ -40,20 +39,21 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
public class AsyncSensorManagerTest extends SysuiTestCase {
- private TestableAsyncSensorManager mAsyncSensorManager;
- private FakeSensorManager mFakeSensorManager;
+ private AsyncSensorManager mAsyncSensorManager;
private SensorEventListener mListener;
- private FakeSensorManager.MockProximitySensor mSensor;
+ private FakeSensorManager.FakeProximitySensor mSensor;
private PluginManager mPluginManager;
@Before
public void setUp() throws Exception {
mPluginManager = mock(PluginManager.class);
- mFakeSensorManager = new FakeSensorManager(mContext);
- mAsyncSensorManager = new TestableAsyncSensorManager(mFakeSensorManager);
- mSensor = mFakeSensorManager.getMockProximitySensor();
+ FakeSensorManager fakeSensorManager = new FakeSensorManager(mContext);
+ mAsyncSensorManager = new AsyncSensorManager(
+ fakeSensorManager, mPluginManager, new Handler());
+ mSensor = fakeSensorManager.getFakeProximitySensor();
mListener = mock(SensorEventListener.class);
}
@@ -61,7 +61,7 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
public void registerListenerImpl() throws Exception {
mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
- mAsyncSensorManager.waitUntilRequestsCompleted();
+ waitUntilRequestsCompleted();
// Verify listener was registered.
mSensor.sendProximityResult(true);
@@ -73,7 +73,7 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
mAsyncSensorManager.unregisterListener(mListener);
- mAsyncSensorManager.waitUntilRequestsCompleted();
+ waitUntilRequestsCompleted();
// Verify listener was unregistered.
mSensor.sendProximityResult(true);
@@ -85,7 +85,7 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
mAsyncSensorManager.unregisterListener(mListener, mSensor.getSensor());
- mAsyncSensorManager.waitUntilRequestsCompleted();
+ waitUntilRequestsCompleted();
// Verify listener was unregistered.
mSensor.sendProximityResult(true);
@@ -98,13 +98,7 @@ public class AsyncSensorManagerTest extends SysuiTestCase {
eq(SensorManagerPlugin.class), eq(true) /* allowMultiple */);
}
- private class TestableAsyncSensorManager extends AsyncSensorManager {
- public TestableAsyncSensorManager(SensorManager sensorManager) {
- super(sensorManager, mPluginManager);
- }
-
- public void waitUntilRequestsCompleted() {
- assertTrue(mHandler.runWithScissors(() -> {}, 0));
- }
+ public void waitUntilRequestsCompleted() {
+ TestableLooper.get(this).processAllMessages();
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
index 29b8ab600caf..1deb495b5250 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.utils.hardware;
+package com.android.systemui.util.sensors;
import android.content.Context;
import android.hardware.HardwareBuffer;
@@ -54,7 +54,7 @@ public class FakeSensorManager extends SensorManager {
public static final String TAP_SENSOR_TYPE = "tapSensorType";
- private final MockProximitySensor mMockProximitySensor;
+ private final FakeProximitySensor mFakeProximitySensor;
private final FakeGenericSensor mFakeLightSensor;
private final FakeGenericSensor mFakeTapSensor;
private final FakeGenericSensor[] mSensors;
@@ -68,14 +68,14 @@ public class FakeSensorManager extends SensorManager {
}
mSensors = new FakeGenericSensor[]{
- mMockProximitySensor = new MockProximitySensor(proxSensor),
+ mFakeProximitySensor = new FakeProximitySensor(proxSensor),
mFakeLightSensor = new FakeGenericSensor(createSensor(Sensor.TYPE_LIGHT, null)),
mFakeTapSensor = new FakeGenericSensor(createSensor(99, TAP_SENSOR_TYPE))
};
}
- public MockProximitySensor getMockProximitySensor() {
- return mMockProximitySensor;
+ public FakeProximitySensor getFakeProximitySensor() {
+ return mFakeProximitySensor;
}
public FakeGenericSensor getFakeLightSensor() {
@@ -231,9 +231,9 @@ public class FakeSensorManager extends SensorManager {
setter.invoke(sensor, type);
}
- public class MockProximitySensor extends FakeGenericSensor {
+ public class FakeProximitySensor extends FakeGenericSensor {
- private MockProximitySensor(Sensor sensor) {
+ private FakeProximitySensor(Sensor sensor) {
super(sensor);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
new file mode 100644
index 000000000000..6d13408058cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ProximitySensorTest extends SysuiTestCase {
+
+ private ProximitySensor mProximitySensor;
+ private FakeSensorManager.FakeProximitySensor mFakeProximitySensor;
+
+ @Before
+ public void setUp() throws Exception {
+ FakeSensorManager sensorManager = new FakeSensorManager(getContext());
+ AsyncSensorManager asyncSensorManager = new AsyncSensorManager(
+ sensorManager, null, new Handler());
+ mFakeProximitySensor = sensorManager.getFakeProximitySensor();
+ mProximitySensor = new ProximitySensor(getContext(), asyncSensorManager, null);
+ }
+
+ @Test
+ public void testSingleListener() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 2);
+
+ mProximitySensor.unregister(listener);
+ waitForSensorManager();
+ }
+
+ @Test
+ public void testMultiListener() {
+ TestableListener listenerA = new TestableListener();
+ TestableListener listenerB = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+
+ mProximitySensor.register(listenerA);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ mProximitySensor.register(listenerB);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listenerA.mLastEvent);
+ assertNull(listenerB.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listenerA.mLastEvent.getNear());
+ assertFalse(listenerB.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 1);
+ assertEquals(listenerB.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listenerA.mLastEvent.getNear());
+ assertTrue(listenerB.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 2);
+ assertEquals(listenerB.mCallCount, 2);
+
+ mProximitySensor.unregister(listenerA);
+ mProximitySensor.unregister(listenerB);
+ waitForSensorManager();
+ }
+
+ @Test
+ public void testUnregister() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.unregister(listener);
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+ }
+
+ @Test
+ public void testPauseAndResume() {
+ TestableListener listener = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+ mProximitySensor.register(listener);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listener.mLastEvent);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.pause();
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+
+ // More events do nothing when paused.
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+ mFakeProximitySensor.sendProximityResult(false);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mProximitySensor.resume();
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ // Still matches our previous call
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 1);
+
+ mFakeProximitySensor.sendProximityResult(true);
+ assertFalse(listener.mLastEvent.getNear());
+ assertEquals(listener.mCallCount, 2);
+
+ mProximitySensor.unregister(listener);
+ waitForSensorManager();
+ assertFalse(mProximitySensor.isRegistered());
+ }
+
+ @Test
+ public void testAlertListeners() {
+ TestableListener listenerA = new TestableListener();
+ TestableListener listenerB = new TestableListener();
+
+ assertFalse(mProximitySensor.isRegistered());
+
+ mProximitySensor.register(listenerA);
+ mProximitySensor.register(listenerB);
+ waitForSensorManager();
+ assertTrue(mProximitySensor.isRegistered());
+ assertNull(listenerA.mLastEvent);
+ assertNull(listenerB.mLastEvent);
+
+ mProximitySensor.alertListeners();
+ assertNull(listenerA.mLastEvent);
+ assertEquals(listenerA.mCallCount, 1);
+ assertNull(listenerB.mLastEvent);
+ assertEquals(listenerB.mCallCount, 1);
+
+ mFakeProximitySensor.sendProximityResult(false);
+ assertTrue(listenerA.mLastEvent.getNear());
+ assertEquals(listenerA.mCallCount, 2);
+ assertTrue(listenerB.mLastEvent.getNear());
+ assertEquals(listenerB.mCallCount, 2);
+
+ mProximitySensor.unregister(listenerA);
+ mProximitySensor.unregister(listenerB);
+ waitForSensorManager();
+ }
+
+ class TestableListener implements ProximitySensor.ProximitySensorListener {
+ ProximitySensor.ProximityEvent mLastEvent;
+ int mCallCount = 0;
+
+ @Override
+ public void onSensorEvent(ProximitySensor.ProximityEvent proximityEvent) {
+ mLastEvent = proximityEvent;
+ mCallCount++;
+ }
+
+ void reset() {
+ mLastEvent = null;
+ mCallCount = 0;
+ }
+ };
+
+ private void waitForSensorManager() {
+ TestableLooper.get(this).processAllMessages();
+ }
+
+}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index f40a1eea255b..5b826b1c551b 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7422,6 +7422,9 @@ message MetricsEvent {
// OS: Q - QPR1
ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET = 1749;
+ // OPEN: Settings > System > Aware > Aware Display
+ // OS: Q
+ SETTINGS_AWARE_DISPLAY = 1750;
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 8ad24894a1b9..fbf6ca52f11c 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1792,6 +1792,25 @@ message ExperimentValues {
// Indicates if we are logging LinkSpeedCount in metrics
optional bool link_speed_counts_logging_enabled = 4;
+
+ // Duration for evaluating Wifi condition to trigger a data stall
+ // measured in milliseconds
+ optional int32 data_stall_duration_ms = 5;
+
+ // Threshold of Tx throughput below which to trigger a data stall
+ // measured in Kbps
+ optional int32 data_stall_tx_tput_thr_kbps = 6;
+
+ // Threshold of Rx throughput below which to trigger a data stall
+ // measured in Kbps
+ optional int32 data_stall_rx_tput_thr_kbps = 7;
+
+ // Threshold of Tx packet error rate above which to trigger a data stall
+ // in percentage
+ optional int32 data_stall_tx_per_thr = 8;
+
+ // Threshold of CCA level above which to trigger a data stall in percentage
+ optional int32 data_stall_cca_level_thr = 9;
}
message WifiIsUnusableEvent {
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index cdb062d1963c..4f021ad3cee0 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -23,7 +23,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCE
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
@@ -1173,9 +1173,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- public void notifyGesture(AccessibilityGestureInfo gestureInfo) {
+ public void notifyGesture(AccessibilityGestureEvent gestureEvent) {
mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
- gestureInfo).sendToTarget();
+ gestureEvent).sendToTarget();
}
public void notifyClearAccessibilityNodeInfoCache() {
@@ -1264,7 +1264,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
}
- private void notifyGestureInternal(AccessibilityGestureInfo gestureInfo) {
+ private void notifyGestureInternal(AccessibilityGestureEvent gestureInfo) {
final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
if (listener != null) {
try {
@@ -1469,7 +1469,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
final int type = message.what;
switch (type) {
case MSG_ON_GESTURE: {
- notifyGestureInternal((AccessibilityGestureInfo) message.obj);
+ notifyGestureInternal((AccessibilityGestureEvent) message.obj);
} break;
case MSG_CLEAR_ACCESSIBILITY_CACHE: {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index feb7329bdda6..1f11059392a1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -27,7 +27,7 @@ import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.Manifest;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
@@ -828,12 +828,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
-
- public boolean onGesture(AccessibilityGestureInfo gestureInfo) {
+ /**
+ * Called when a gesture is detected on a display.
+ *
+ * @param gestureEvent the detail of the gesture.
+ * @return true if the event is handled.
+ */
+ public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
synchronized (mLock) {
- boolean handled = notifyGestureLocked(gestureInfo, false);
+ boolean handled = notifyGestureLocked(gestureEvent, false);
if (!handled) {
- handled = notifyGestureLocked(gestureInfo, true);
+ handled = notifyGestureLocked(gestureEvent, true);
}
return handled;
}
@@ -1028,7 +1033,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- private boolean notifyGestureLocked(AccessibilityGestureInfo gestureInfo, boolean isDefault) {
+ private boolean notifyGestureLocked(AccessibilityGestureEvent gestureEvent, boolean isDefault) {
// TODO: Now we are giving the gestures to the last enabled
// service that can handle them which is the last one
// in our list since we write the last enabled as the
@@ -1042,7 +1047,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
- service.notifyGesture(gestureInfo);
+ service.notifyGesture(gestureEvent);
return true;
}
}
@@ -2200,6 +2205,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
synchronized(mLock) {
final UserState userState = getUserStateLocked(mCurrentUserId);
+ if (userState.mServiceToEnableWithShortcut == null) {
+ return null;
+ }
return userState.mServiceToEnableWithShortcut.flattenToString();
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
index 9101a01bc8fe..7e8fb295d036 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/AccessibilityGestureDetector.java
@@ -16,7 +16,7 @@
package com.android.server.accessibility.gestures;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.accessibilityservice.AccessibilityService;
import android.content.Context;
import android.gesture.GesturePoint;
@@ -119,11 +119,11 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen
/**
* Called when an event stream is recognized as a gesture.
*
- * @param gestureInfo Information about the gesture.
+ * @param gestureEvent Information about the gesture.
*
* @return true if the event is consumed, else false
*/
- boolean onGestureCompleted(AccessibilityGestureInfo gestureInfo);
+ boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent);
/**
* Called when the system has decided an event stream doesn't match any
@@ -567,19 +567,19 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen
switch (direction) {
case LEFT:
return mListener.onGestureCompleted(
- new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_LEFT,
+ new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_LEFT,
displayId));
case RIGHT:
return mListener.onGestureCompleted(
- new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_RIGHT,
+ new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_RIGHT,
displayId));
case UP:
return mListener.onGestureCompleted(
- new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_UP,
+ new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_UP,
displayId));
case DOWN:
return mListener.onGestureCompleted(
- new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_DOWN,
+ new AccessibilityGestureEvent(AccessibilityService.GESTURE_SWIPE_DOWN,
displayId));
default:
// Do nothing.
@@ -600,7 +600,7 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen
int segmentDirection1 = toDirection(dX1, dY1);
int gestureId = DIRECTIONS_TO_GESTURE_ID[segmentDirection0][segmentDirection1];
return mListener.onGestureCompleted(
- new AccessibilityGestureInfo(gestureId, displayId));
+ new AccessibilityGestureEvent(gestureId, displayId));
}
// else if (path.size() < 2 || 3 < path.size()) then no gesture recognized.
return mListener.onGestureCancelled(event, policyFlags);
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
new file mode 100644
index 000000000000..dc7a9aaf966d
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accessibility.gestures;
+
+import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
+import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_ID_BITS;
+import static com.android.server.accessibility.gestures.TouchState.MAX_POINTER_COUNT;
+
+import android.content.Context;
+import android.util.Slog;
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.server.accessibility.AccessibilityManagerService;
+import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.policy.WindowManagerPolicy;
+
+/**
+ * This class dispatches motion events and accessibility events relating to touch exploration and
+ * gesture dispatch. TouchExplorer is responsible for insuring that the receiver of motion events is
+ * set correctly so that events go to the right place.
+ */
+class EventDispatcher {
+ private static final String LOG_TAG = "EventDispatcher";
+
+ private final AccessibilityManagerService mAms;
+ private Context mContext;
+ // The receiver of motion events.
+ private EventStreamTransformation mReceiver;
+ // Keep track of which pointers sent to the system are down.
+ private int mInjectedPointersDown;
+
+ // The time of the last injected down.
+ private long mLastInjectedDownEventTime;
+
+ // The last injected hover event.
+ private MotionEvent mLastInjectedHoverEvent;
+ private TouchState mState;
+
+ EventDispatcher(
+ Context context,
+ AccessibilityManagerService ams,
+ EventStreamTransformation receiver,
+ TouchState state) {
+ mContext = context;
+ mAms = ams;
+ mReceiver = receiver;
+ mState = state;
+ }
+
+ public void setReceiver(EventStreamTransformation receiver) {
+ mReceiver = receiver;
+ }
+
+ /**
+ * Sends an event.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param action The action of the event.
+ * @param pointerIdBits The bits of the pointers to send.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits, int policyFlags) {
+ prototype.setAction(action);
+
+ MotionEvent event = null;
+ if (pointerIdBits == ALL_POINTER_ID_BITS) {
+ event = prototype;
+ } else {
+ try {
+ event = prototype.split(pointerIdBits);
+ } catch (IllegalArgumentException e) {
+ Slog.e(LOG_TAG, "sendMotionEvent: Failed to split motion event: " + e);
+ return;
+ }
+ }
+ if (action == MotionEvent.ACTION_DOWN) {
+ event.setDownTime(event.getEventTime());
+ } else {
+ event.setDownTime(getLastInjectedDownEventTime());
+ }
+ if (DEBUG) {
+ Slog.d(
+ LOG_TAG,
+ "Injecting event: "
+ + event
+ + ", policyFlags=0x"
+ + Integer.toHexString(policyFlags));
+ }
+
+ // Make sure that the user will see the event.
+ policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
+ // TODO: For now pass null for the raw event since the touch
+ // explorer is the last event transformation and it does
+ // not care about the raw event.
+ if (mReceiver != null) {
+ mReceiver.onMotionEvent(event, null, policyFlags);
+ } else {
+ Slog.e(LOG_TAG, "Error sending event: no receiver specified.");
+ }
+ updateState(event);
+
+ if (event != prototype) {
+ event.recycle();
+ }
+ }
+
+ /**
+ * Sends an accessibility event of the given type.
+ *
+ * @param type The event type.
+ */
+ void sendAccessibilityEvent(int type) {
+ AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
+ if (accessibilityManager.isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(type);
+ event.setWindowId(mAms.getActiveWindowId());
+ accessibilityManager.sendAccessibilityEvent(event);
+ if (DEBUG) {
+ Slog.d(
+ LOG_TAG,
+ "Sending accessibility event" + AccessibilityEvent.eventTypeToString(type));
+ }
+ }
+ // Todo: get rid of this and have TouchState control the sending of events rather than react
+ // to it.
+ mState.onInjectedAccessibilityEvent(type);
+ }
+
+ /**
+ * Processes an injected {@link MotionEvent} event.
+ *
+ * @param event The event to process.
+ */
+ void updateState(MotionEvent event) {
+ final int action = event.getActionMasked();
+ final int pointerId = event.getPointerId(event.getActionIndex());
+ final int pointerFlag = (1 << pointerId);
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ mInjectedPointersDown |= pointerFlag;
+ mLastInjectedDownEventTime = event.getDownTime();
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ mInjectedPointersDown &= ~pointerFlag;
+ if (mInjectedPointersDown == 0) {
+ mLastInjectedDownEventTime = 0;
+ }
+ break;
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ case MotionEvent.ACTION_HOVER_EXIT:
+ if (mLastInjectedHoverEvent != null) {
+ mLastInjectedHoverEvent.recycle();
+ }
+ mLastInjectedHoverEvent = MotionEvent.obtain(event);
+ break;
+ }
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Injected pointer:\n" + toString());
+ }
+ }
+
+ /** Clears the internals state. */
+ public void clear() {
+ mInjectedPointersDown = 0;
+ }
+
+ /** @return The time of the last injected down event. */
+ public long getLastInjectedDownEventTime() {
+ return mLastInjectedDownEventTime;
+ }
+
+ /** @return The number of down pointers injected to the view hierarchy. */
+ public int getInjectedPointerDownCount() {
+ return Integer.bitCount(mInjectedPointersDown);
+ }
+
+ /** @return The bits of the injected pointers that are down. */
+ public int getInjectedPointersDown() {
+ return mInjectedPointersDown;
+ }
+
+ /**
+ * Whether an injected pointer is down.
+ *
+ * @param pointerId The unique pointer id.
+ * @return True if the pointer is down.
+ */
+ public boolean isInjectedPointerDown(int pointerId) {
+ final int pointerFlag = (1 << pointerId);
+ return (mInjectedPointersDown & pointerFlag) != 0;
+ }
+
+ /** @return The the last injected hover event. */
+ public MotionEvent getLastInjectedHoverEvent() {
+ return mLastInjectedHoverEvent;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("=========================");
+ builder.append("\nDown pointers #");
+ builder.append(Integer.bitCount(mInjectedPointersDown));
+ builder.append(" [ ");
+ for (int i = 0; i < MAX_POINTER_COUNT; i++) {
+ if ((mInjectedPointersDown & i) != 0) {
+ builder.append(i);
+ builder.append(" ");
+ }
+ }
+ builder.append("]");
+ builder.append("\n=========================");
+ return builder.toString();
+ }
+
+ /**
+ * Computes the action for an injected event based on a masked action and a pointer index.
+ *
+ * @param actionMasked The masked action.
+ * @param pointerIndex The index of the pointer which has changed.
+ * @return The action to be used for injection.
+ */
+ private int computeInjectionAction(int actionMasked, int pointerIndex) {
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ // Compute the action based on how many down pointers are injected.
+ if (getInjectedPointerDownCount() == 0) {
+ return MotionEvent.ACTION_DOWN;
+ } else {
+ return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+ | MotionEvent.ACTION_POINTER_DOWN;
+ }
+ case MotionEvent.ACTION_POINTER_UP:
+ // Compute the action based on how many down pointers are injected.
+ if (getInjectedPointerDownCount() == 1) {
+ return MotionEvent.ACTION_UP;
+ } else {
+ return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+ | MotionEvent.ACTION_POINTER_UP;
+ }
+ default:
+ return actionMasked;
+ }
+ }
+ /**
+ * Sends down events to the view hierarchy for all pointers which are not already being
+ * delivered i.e. pointers that are not yet injected.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) {
+
+ // Inject the injected pointers.
+ int pointerIdBits = 0;
+ final int pointerCount = prototype.getPointerCount();
+ for (int i = 0; i < pointerCount; i++) {
+ final int pointerId = prototype.getPointerId(i);
+ // Do not send event for already delivered pointers.
+ if (!isInjectedPointerDown(pointerId)) {
+ pointerIdBits |= (1 << pointerId);
+ final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
+ sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
+ }
+ }
+ }
+
+ /**
+ * Sends up events to the view hierarchy for all pointers which are already being delivered i.e.
+ * pointers that are injected.
+ *
+ * @param prototype The prototype from which to create the injected events.
+ * @param policyFlags The policy flags associated with the event.
+ */
+ void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
+ int pointerIdBits = 0;
+ final int pointerCount = prototype.getPointerCount();
+ for (int i = 0; i < pointerCount; i++) {
+ final int pointerId = prototype.getPointerId(i);
+ // Skip non injected down pointers.
+ if (!isInjectedPointerDown(pointerId)) {
+ continue;
+ }
+ pointerIdBits |= (1 << pointerId);
+ final int action = computeInjectionAction(MotionEvent.ACTION_UP, i);
+ sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
+ }
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index a338b901d24c..f4ac82157d04 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -20,7 +20,7 @@ import static android.view.MotionEvent.INVALID_POINTER_ID;
import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_ID_BITS;
-import android.accessibilityservice.AccessibilityGestureInfo;
+import android.accessibilityservice.AccessibilityGestureEvent;
import android.content.Context;
import android.graphics.Point;
import android.os.Handler;
@@ -29,11 +29,11 @@ import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.BaseEventStreamTransformation;
+import com.android.server.accessibility.EventStreamTransformation;
import com.android.server.policy.WindowManagerPolicy;
import java.util.ArrayList;
@@ -61,7 +61,7 @@ import java.util.List;
public class TouchExplorer extends BaseEventStreamTransformation
implements AccessibilityGestureDetector.Listener {
- private static final boolean DEBUG = false;
+ static final boolean DEBUG = false;
// Tag for logging received events.
private static final String LOG_TAG = "TouchExplorer";
@@ -109,8 +109,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Helper class to track received pointers.
private final TouchState.ReceivedPointerTracker mReceivedPointerTracker;
- // Helper class to track injected pointers.
- private final TouchState.InjectedPointerTracker mInjectedPointerTracker;
+ private final EventDispatcher mDispatcher;
// Handle to the accessibility manager service.
private final AccessibilityManagerService mAms;
@@ -148,7 +147,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
mAms = service;
mState = new TouchState();
mReceivedPointerTracker = mState.getReceivedPointerTracker();
- mInjectedPointerTracker = mState.getInjectedPointerTracker();
+ mDispatcher = new EventDispatcher(context, mAms, super.getNext(), mState);
mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
mHandler = new Handler(context.getMainLooper());
@@ -197,10 +196,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
} else if (mState.isDragging()) {
mDraggingPointerId = INVALID_POINTER_ID;
// Send exit to all pointers that we have delivered.
- sendUpForInjectedDownPointers(event, policyFlags);
+ mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
} else if (mState.isDelegating()) {
// Send exit to all pointers that we have delivered.
- sendUpForInjectedDownPointers(event, policyFlags);
+ mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
} else if (mState.isGestureDetecting()) {
// No state specific cleanup required.
}
@@ -271,7 +270,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
if (mSendTouchExplorationEndDelayed.isPending()
&& eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
mSendTouchExplorationEndDelayed.cancel();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ mDispatcher.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
}
// The event for touch interaction end should be strictly after the
@@ -279,7 +279,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
if (mSendTouchInteractionEndDelayed.isPending()
&& eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
mSendTouchInteractionEndDelayed.cancel();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
}
super.onAccessibilityEvent(event);
}
@@ -318,7 +318,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
// Announce the end of a new touch interaction.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
// Try to use the standard accessibility API to click
if (!mAms.performActionOnAccessibilityFocusedItem(
@@ -338,19 +338,19 @@ public class TouchExplorer extends BaseEventStreamTransformation
mExitGestureDetectionModeDelayed.post();
// Send accessibility event to announce the start
// of gesture recognition.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
return false;
}
@Override
- public boolean onGestureCompleted(AccessibilityGestureInfo gestureInfo) {
+ public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) {
if (!mState.isGestureDetecting()) {
return false;
}
endGestureDetection(true);
- mAms.onGesture(gestureInfo);
+ mAms.onGesture(gestureEvent);
return true;
}
@@ -371,7 +371,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
mSendHoverEnterAndMoveDelayed.addEvent(event);
mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
mSendHoverExitDelayed.cancel();
- sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
return true;
}
}
@@ -417,7 +418,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
if (!mGestureDetector.firstTapDetected() && mState.isClear()) {
mSendTouchExplorationEndDelayed.forceSendAndRemove();
mSendTouchInteractionEndDelayed.forceSendAndRemove();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
} else {
// Let gesture to handle to avoid duplicated TYPE_TOUCH_INTERACTION_END event.
mSendTouchInteractionEndDelayed.cancel();
@@ -536,18 +537,19 @@ public class TouchExplorer extends BaseEventStreamTransformation
mState.startDragging();
mDraggingPointerId = pointerId;
event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
- sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
} else {
// Two pointers moving arbitrary are delegated to the view hierarchy.
mState.startDelegating();
- sendDownForAllNotInjectedPointers(event, policyFlags);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
}
break;
default:
// More than two pointers are delegated to the view hierarchy.
mState.startDelegating();
event = MotionEvent.obtainNoHistory(event);
- sendDownForAllNotInjectedPointers(event, policyFlags);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
break;
}
}
@@ -585,7 +587,8 @@ public class TouchExplorer extends BaseEventStreamTransformation
case 1:
// Touch exploration.
sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
- sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
break;
case 2:
if (mSendHoverEnterAndMoveDelayed.isPending()) {
@@ -658,9 +661,10 @@ public class TouchExplorer extends BaseEventStreamTransformation
// goes down => delegate the three pointers to the view hierarchy
mState.startDelegating();
if (mDraggingPointerId != INVALID_POINTER_ID) {
- sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
- sendDownForAllNotInjectedPointers(event, policyFlags);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
} break;
case MotionEvent.ACTION_MOVE: {
if (mDraggingPointerId == INVALID_POINTER_ID) {
@@ -672,21 +676,12 @@ public class TouchExplorer extends BaseEventStreamTransformation
} break;
case 2: {
if (isDraggingGesture(event)) {
- // Adjust event location to the middle location of the two pointers.
- final float firstPtrX = event.getX(0);
- final float firstPtrY = event.getY(0);
- final float secondPtrX = event.getX(1);
- final float secondPtrY = event.getY(1);
- final int pointerIndex = event.findPointerIndex(mDraggingPointerId);
- final float deltaX =
- (pointerIndex == 0) ? (secondPtrX - firstPtrX)
- : (firstPtrX - secondPtrX);
- final float deltaY =
- (pointerIndex == 0) ? (secondPtrY - firstPtrY)
- : (firstPtrY - secondPtrY);
- event.offsetLocation(deltaX / 2, deltaY / 2);
// If still dragging send a drag event.
- sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits,
+ adjustEventLocationForDrag(event);
+ mDispatcher.sendMotionEvent(
+ event,
+ MotionEvent.ACTION_MOVE,
+ pointerIdBits,
policyFlags);
} else {
// The two pointers are moving either in different directions or
@@ -695,20 +690,20 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Remove move history before send injected non-move events
event = MotionEvent.obtainNoHistory(event);
// Send an event to the end of the drag gesture.
- sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
+ mDispatcher.sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
policyFlags);
// Deliver all pointers to the view hierarchy.
- sendDownForAllNotInjectedPointers(event, policyFlags);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
}
} break;
default: {
mState.startDelegating();
event = MotionEvent.obtainNoHistory(event);
// Send an event to the end of the drag gesture.
- sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
+ mDispatcher.sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
policyFlags);
// Deliver all pointers to the view hierarchy.
- sendDownForAllNotInjectedPointers(event, policyFlags);
+ mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
}
}
} break;
@@ -716,20 +711,22 @@ public class TouchExplorer extends BaseEventStreamTransformation
final int pointerId = event.getPointerId(event.getActionIndex());
if (pointerId == mDraggingPointerId) {
mDraggingPointerId = INVALID_POINTER_ID;
- // Send an event to the end of the drag gesture.
- sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ // Send an event to the end of the drag gesture.
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
} break;
case MotionEvent.ACTION_UP: {
mAms.onTouchInteractionEnd();
// Announce the end of a new touch interaction.
- sendAccessibilityEvent(
+ mDispatcher.sendAccessibilityEvent(
AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
final int pointerId = event.getPointerId(event.getActionIndex());
if (pointerId == mDraggingPointerId) {
mDraggingPointerId = INVALID_POINTER_ID;
// Send an event to the end of the drag gesture.
- sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
} break;
}
@@ -751,16 +748,18 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
case MotionEvent.ACTION_UP: {
// Deliver the event.
- sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
// Announce the end of a the touch interaction.
mAms.onTouchInteractionEnd();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
} break;
default: {
- // Deliver the event.
- sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
+ // Deliver the event.
+ mDispatcher.sendMotionEvent(
+ event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
}
}
}
@@ -769,57 +768,15 @@ public class TouchExplorer extends BaseEventStreamTransformation
mAms.onTouchInteractionEnd();
// Announce the end of the gesture recognition.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
// Don't announce the end of a the touch interaction if users didn't lift their fingers.
if (interactionEnd) {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
}
mExitGestureDetectionModeDelayed.cancel();
}
- /**
- * Sends an accessibility event of the given type.
- *
- * @param type The event type.
- */
- private void sendAccessibilityEvent(int type) {
- AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
- if (accessibilityManager.isEnabled()) {
- AccessibilityEvent event = AccessibilityEvent.obtain(type);
- event.setWindowId(mAms.getActiveWindowId());
- accessibilityManager.sendAccessibilityEvent(event);
- if (DEBUG) {
- Slog.d(
- LOG_TAG,
- "Sending accessibility event" + AccessibilityEvent.eventTypeToString(type));
- }
- }
- mState.onInjectedAccessibilityEvent(type);
- }
-
- /**
- * Sends down events to the view hierarchy for all pointers which are
- * not already being delivered i.e. pointers that are not yet injected.
- *
- * @param prototype The prototype from which to create the injected events.
- * @param policyFlags The policy flags associated with the event.
- */
- private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) {
-
- // Inject the injected pointers.
- int pointerIdBits = 0;
- final int pointerCount = prototype.getPointerCount();
- for (int i = 0; i < pointerCount; i++) {
- final int pointerId = prototype.getPointerId(i);
- // Do not send event for already delivered pointers.
- if (!mInjectedPointerTracker.isInjectedPointerDown(pointerId)) {
- pointerIdBits |= (1 << pointerId);
- final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
- sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
- }
- }
- }
/**
* Sends the exit events if needed. Such events are hover exit and touch explore
@@ -828,13 +785,14 @@ public class TouchExplorer extends BaseEventStreamTransformation
* @param policyFlags The policy flags associated with the event.
*/
private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
- MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
+ MotionEvent event = mDispatcher.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
if (!mSendTouchExplorationEndDelayed.isPending()) {
mSendTouchExplorationEndDelayed.post();
}
- sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
}
}
@@ -845,115 +803,14 @@ public class TouchExplorer extends BaseEventStreamTransformation
* @param policyFlags The policy flags associated with the event.
*/
private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
- MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
+ MotionEvent event = mDispatcher.getLastInjectedHoverEvent();
if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
final int pointerIdBits = event.getPointerIdBits();
- sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
+ mDispatcher.sendMotionEvent(
+ event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
}
}
- /**
- * Sends up events to the view hierarchy for all pointers which are
- * already being delivered i.e. pointers that are injected.
- *
- * @param prototype The prototype from which to create the injected events.
- * @param policyFlags The policy flags associated with the event.
- */
- private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
- int pointerIdBits = 0;
- final int pointerCount = prototype.getPointerCount();
- for (int i = 0; i < pointerCount; i++) {
- final int pointerId = prototype.getPointerId(i);
- // Skip non injected down pointers.
- if (!mInjectedPointerTracker.isInjectedPointerDown(pointerId)) {
- continue;
- }
- pointerIdBits |= (1 << pointerId);
- final int action = computeInjectionAction(MotionEvent.ACTION_UP, i);
- sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
- }
- }
-
- /**
- * Sends an event.
- *
- * @param prototype The prototype from which to create the injected events.
- * @param action The action of the event.
- * @param pointerIdBits The bits of the pointers to send.
- * @param policyFlags The policy flags associated with the event.
- */
- private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits,
- int policyFlags) {
- prototype.setAction(action);
-
- MotionEvent event = null;
- if (pointerIdBits == ALL_POINTER_ID_BITS) {
- event = prototype;
- } else {
- try {
- event = prototype.split(pointerIdBits);
- } catch (IllegalArgumentException e) {
- Slog.e(LOG_TAG, "sendMotionEvent: Failed to split motion event: " + e);
- return;
- }
- }
- if (action == MotionEvent.ACTION_DOWN) {
- event.setDownTime(event.getEventTime());
- } else {
- event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
- }
- if (DEBUG) {
- Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x"
- + Integer.toHexString(policyFlags));
- }
-
- // Make sure that the user will see the event.
- policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
- // TODO: For now pass null for the raw event since the touch
- // explorer is the last event transformation and it does
- // not care about the raw event.
- super.onMotionEvent(event, null, policyFlags);
-
- mInjectedPointerTracker.onMotionEvent(event);
-
- if (event != prototype) {
- event.recycle();
- }
- }
-
- /**
- * Computes the action for an injected event based on a masked action
- * and a pointer index.
- *
- * @param actionMasked The masked action.
- * @param pointerIndex The index of the pointer which has changed.
- * @return The action to be used for injection.
- */
- private int computeInjectionAction(int actionMasked, int pointerIndex) {
- switch (actionMasked) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN: {
- // Compute the action based on how many down pointers are injected.
- if (mInjectedPointerTracker.getInjectedPointerDownCount() == 0) {
- return MotionEvent.ACTION_DOWN;
- } else {
- return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
- | MotionEvent.ACTION_POINTER_DOWN;
- }
- }
- case MotionEvent.ACTION_POINTER_UP: {
- // Compute the action based on how many down pointers are injected.
- if (mInjectedPointerTracker.getInjectedPointerDownCount() == 1) {
- return MotionEvent.ACTION_UP;
- } else {
- return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
- | MotionEvent.ACTION_POINTER_UP;
- }
- }
- default:
- return actionMasked;
- }
- }
/**
* Determines whether a two pointer gesture is a dragging one.
@@ -978,10 +835,34 @@ public class TouchExplorer extends BaseEventStreamTransformation
MAX_DRAGGING_ANGLE_COS);
}
+ /**
+ * Adjust the location of an injected event when performing a drag The new location will be in
+ * between the two fingers touching the screen.
+ */
+ private void adjustEventLocationForDrag(MotionEvent event) {
+
+ final float firstPtrX = event.getX(0);
+ final float firstPtrY = event.getY(0);
+ final float secondPtrX = event.getX(1);
+ final float secondPtrY = event.getY(1);
+ final int pointerIndex = event.findPointerIndex(mDraggingPointerId);
+ final float deltaX =
+ (pointerIndex == 0) ? (secondPtrX - firstPtrX) : (firstPtrX - secondPtrX);
+ final float deltaY =
+ (pointerIndex == 0) ? (secondPtrY - firstPtrY) : (firstPtrY - secondPtrY);
+ event.offsetLocation(deltaX / 2, deltaY / 2);
+ }
+
public TouchState getState() {
return mState;
}
+ @Override
+ public void setNext(EventStreamTransformation next) {
+ mDispatcher.setReceiver(next);
+ super.setNext(next);
+ }
+
/**
* Class for delayed exiting from gesture detecting mode.
*/
@@ -998,7 +879,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void run() {
// Announce the end of gesture recognition.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
+ mDispatcher.sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
clear();
}
}
@@ -1055,11 +936,12 @@ public class TouchExplorer extends BaseEventStreamTransformation
public void run() {
// Send an accessibility event to announce the touch exploration start.
- sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
+ mDispatcher.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
if (!mEvents.isEmpty()) {
// Deliver a down event.
- sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER,
+ mDispatcher.sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER,
mPointerIdBits, mPolicyFlags);
if (DEBUG) {
Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
@@ -1069,7 +951,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
// Deliver move events.
final int eventCount = mEvents.size();
for (int i = 1; i < eventCount; i++) {
- sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE,
+ mDispatcher.sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE,
mPointerIdBits, mPolicyFlags);
if (DEBUG) {
Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
@@ -1129,7 +1011,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:"
+ " ACTION_HOVER_EXIT");
}
- sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT,
+ mDispatcher.sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT,
mPointerIdBits, mPolicyFlags);
if (!mSendTouchExplorationEndDelayed.isPending()) {
mSendTouchExplorationEndDelayed.cancel();
@@ -1173,7 +1055,7 @@ public class TouchExplorer extends BaseEventStreamTransformation
@Override
public void run() {
- sendAccessibilityEvent(mEventType);
+ mDispatcher.sendAccessibilityEvent(mEventType);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index 17e969a1aa49..49938fa4c6b9 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -18,6 +18,8 @@ package com.android.server.accessibility.gestures;
import static android.view.MotionEvent.INVALID_POINTER_ID;
+import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
+
import android.annotation.IntDef;
import android.util.Slog;
import android.view.MotionEvent;
@@ -29,14 +31,12 @@ import android.view.accessibility.AccessibilityEvent;
* dispatch.
*/
public class TouchState {
-
- private static final boolean DEBUG = false;
private static final String LOG_TAG = "TouchState";
// Pointer-related constants
// This constant captures the current implementation detail that
// pointer IDs are between 0 and 31 inclusive (subject to change).
// (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
- private static final int MAX_POINTER_COUNT = 32;
+ static final int MAX_POINTER_COUNT = 32;
// Constant referring to the ids bits of all pointers.
public static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
@@ -71,13 +71,9 @@ public class TouchState {
// Helper class to track received pointers.
// Todo: collapse or hide this class so multiple classes don't modify it.
private final ReceivedPointerTracker mReceivedPointerTracker;
- // Helper class to track injected pointers.
- // Todo: collapse or hide this class so multiple classes don't modify it.
- private final InjectedPointerTracker mInjectedPointerTracker;
public TouchState() {
mReceivedPointerTracker = new ReceivedPointerTracker();
- mInjectedPointerTracker = new InjectedPointerTracker();
}
/** Clears the internal shared state. */
@@ -85,16 +81,6 @@ public class TouchState {
setState(STATE_CLEAR);
// Reset the pointer trackers.
mReceivedPointerTracker.clear();
- mInjectedPointerTracker.clear();
- }
-
- /**
- * Updates the state in response to a hover event dispatched by TouchExplorer.
- *
- * @param event The event sent from TouchExplorer.
- */
- public void onInjectedMotionEvent(MotionEvent event) {
- mInjectedPointerTracker.onMotionEvent(event);
}
/**
@@ -226,117 +212,10 @@ public class TouchState {
}
}
- public InjectedPointerTracker getInjectedPointerTracker() {
- return mInjectedPointerTracker;
- }
-
public ReceivedPointerTracker getReceivedPointerTracker() {
return mReceivedPointerTracker;
}
- /** This class tracks the up/down state of each pointer. It does not track movement. */
- class InjectedPointerTracker {
- private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker";
-
- // Keep track of which pointers sent to the system are down.
- private int mInjectedPointersDown;
-
- // The time of the last injected down.
- private long mLastInjectedDownEventTime;
-
- // The last injected hover event.
- private MotionEvent mLastInjectedHoverEvent;
-
- /**
- * Processes an injected {@link MotionEvent} event.
- *
- * @param event The event to process.
- */
- public void onMotionEvent(MotionEvent event) {
- final int action = event.getActionMasked();
- final int pointerId = event.getPointerId(event.getActionIndex());
- final int pointerFlag = (1 << pointerId);
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_POINTER_DOWN:
- mInjectedPointersDown |= pointerFlag;
- mLastInjectedDownEventTime = event.getDownTime();
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP:
- mInjectedPointersDown &= ~pointerFlag;
- if (mInjectedPointersDown == 0) {
- mLastInjectedDownEventTime = 0;
- }
- break;
- case MotionEvent.ACTION_HOVER_ENTER:
- case MotionEvent.ACTION_HOVER_MOVE:
- case MotionEvent.ACTION_HOVER_EXIT:
- if (mLastInjectedHoverEvent != null) {
- mLastInjectedHoverEvent.recycle();
- }
- mLastInjectedHoverEvent = MotionEvent.obtain(event);
- break;
- }
- if (DEBUG) {
- Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString());
- }
- }
-
- /** Clears the internals state. */
- public void clear() {
- mInjectedPointersDown = 0;
- }
-
- /** @return The time of the last injected down event. */
- public long getLastInjectedDownEventTime() {
- return mLastInjectedDownEventTime;
- }
-
- /** @return The number of down pointers injected to the view hierarchy. */
- public int getInjectedPointerDownCount() {
- return Integer.bitCount(mInjectedPointersDown);
- }
-
- /** @return The bits of the injected pointers that are down. */
- public int getInjectedPointersDown() {
- return mInjectedPointersDown;
- }
-
- /**
- * Whether an injected pointer is down.
- *
- * @param pointerId The unique pointer id.
- * @return True if the pointer is down.
- */
- public boolean isInjectedPointerDown(int pointerId) {
- final int pointerFlag = (1 << pointerId);
- return (mInjectedPointersDown & pointerFlag) != 0;
- }
-
- /** @return The the last injected hover event. */
- public MotionEvent getLastInjectedHoverEvent() {
- return mLastInjectedHoverEvent;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append("=========================");
- builder.append("\nDown pointers #");
- builder.append(Integer.bitCount(mInjectedPointersDown));
- builder.append(" [ ");
- for (int i = 0; i < MAX_POINTER_COUNT; i++) {
- if ((mInjectedPointersDown & i) != 0) {
- builder.append(i);
- builder.append(" ");
- }
- }
- builder.append("]");
- builder.append("\n=========================");
- return builder.toString();
- }
- }
/** This class tracks where and when a pointer went down. It does not track its movement. */
class ReceivedPointerTracker {
private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
index 1891ba9b1742..6bc1a570b7c0 100644
--- a/services/core/java/com/android/server/MountServiceIdler.java
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -27,6 +27,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
import android.util.Slog;
+import java.util.concurrent.TimeUnit;
public class MountServiceIdler extends JobService {
private static final String TAG = "MountServiceIdler";
@@ -48,7 +49,7 @@ public class MountServiceIdler extends JobService {
mStarted = false;
}
}
- // ... and try again tomorrow
+ // ... and try again right away or tomorrow
scheduleIdlePass(MountServiceIdler.this);
}
};
@@ -98,24 +99,32 @@ public class MountServiceIdler extends JobService {
public static void scheduleIdlePass(Context context) {
JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- Calendar calendar = tomorrowMidnight();
- final long timeToMidnight = calendar.getTimeInMillis() - System.currentTimeMillis();
+ final long today3AM = offsetFromTodayMidnight(0, 3).getTimeInMillis();
+ final long today4AM = offsetFromTodayMidnight(0, 4).getTimeInMillis();
+ final long tomorrow3AM = offsetFromTodayMidnight(1, 3).getTimeInMillis();
+
+ long nextScheduleTime;
+ if (System.currentTimeMillis() > today3AM && System.currentTimeMillis() < today4AM) {
+ nextScheduleTime = TimeUnit.SECONDS.toMillis(10);
+ } else {
+ nextScheduleTime = tomorrow3AM - System.currentTimeMillis(); // 3AM tomorrow
+ }
JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService);
builder.setRequiresDeviceIdle(true);
- builder.setRequiresCharging(true);
- builder.setMinimumLatency(timeToMidnight);
+ builder.setRequiresBatteryNotLow(true);
+ builder.setMinimumLatency(nextScheduleTime);
tm.schedule(builder.build());
}
- private static Calendar tomorrowMidnight() {
+ private static Calendar offsetFromTodayMidnight(int nDays, int nHours) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
- calendar.set(Calendar.HOUR_OF_DAY, 3);
+ calendar.set(Calendar.HOUR_OF_DAY, nHours);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
- calendar.add(Calendar.DAY_OF_MONTH, 1);
+ calendar.add(Calendar.DAY_OF_MONTH, nDays);
return calendar;
}
}
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 8c60f1aa439b..bc509561163a 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -812,7 +812,6 @@ public class PackageWatchdog {
*/
private static class ObserverInternal {
public final String name;
- //TODO(b/120598832): Add getter for mPackages
@GuardedBy("mLock")
public final ArrayMap<String, MonitoredPackage> packages = new ArrayMap<>();
@Nullable
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1675b94292a7..e7569bee239e 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1748,8 +1748,8 @@ public final class ActiveServices {
s.instanceName, s.processName);
// Once the apps have become associated, if one of them is caller is ephemeral
// the target app should now be able to see the calling app
- mAm.grantEphemeralAccessLocked(callerApp.userId, service,
- UserHandle.getAppId(s.appInfo.uid), UserHandle.getAppId(callerApp.uid));
+ mAm.grantImplicitAccess(callerApp.userId, service,
+ UserHandle.getAppId(callerApp.uid), UserHandle.getAppId(s.appInfo.uid));
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
@@ -2802,8 +2802,9 @@ public final class ActiveServices {
mAm.mUgmInternal.grantUriPermissionUncheckedFromIntent(si.neededGrants,
si.getUriPermissionsLocked());
}
- mAm.grantEphemeralAccessLocked(r.userId, si.intent, UserHandle.getAppId(r.appInfo.uid),
- UserHandle.getAppId(si.callingId));
+ mAm.grantImplicitAccess(r.userId, si.intent, UserHandle.getAppId(si.callingId),
+ UserHandle.getAppId(r.appInfo.uid)
+ );
bumpServiceExecutingLocked(r, execInFg, "start");
if (!oomAdjusted) {
oomAdjusted = true;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6c42c7f2583c..3c7cb88174e1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5265,11 +5265,14 @@ public class ActivityManagerService extends IActivityManager.Stub
// Inform checkpointing systems of success
try {
+ // This line is needed to CTS test for the correct exception handling
+ // See b/138952436#comment36 for context
+ Slog.i(TAG, "About to commit checkpoint");
IStorageManager storageManager = PackageHelper.getStorageManager();
storageManager.commitChanges();
} catch (Exception e) {
PowerManager pm = (PowerManager)
- mContext.getSystemService(Context.POWER_SERVICE);
+ mInjector.getContext().getSystemService(Context.POWER_SERVICE);
pm.reboot("Checkpoint commit failed");
}
@@ -6115,10 +6118,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@VisibleForTesting
- public void grantEphemeralAccessLocked(int userId, Intent intent,
- int targetAppId, int ephemeralAppId) {
+ public void grantImplicitAccess(int userId, Intent intent, int callingAppId, int targetAppId) {
getPackageManagerInternalLocked().
- grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
+ grantImplicitAccess(userId, intent, callingAppId, targetAppId);
}
/**
@@ -7088,9 +7090,10 @@ public class ActivityManagerService extends IActivityManager.Stub
}
checkTime(startTime, "getContentProviderImpl: done!");
- grantEphemeralAccessLocked(userId, null /*intent*/,
- UserHandle.getAppId(cpi.applicationInfo.uid),
- UserHandle.getAppId(Binder.getCallingUid()));
+ grantImplicitAccess(userId, null /*intent*/,
+ UserHandle.getAppId(Binder.getCallingUid()),
+ UserHandle.getAppId(cpi.applicationInfo.uid)
+ );
}
// Wait for the provider to be published...
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index e73abd17cc07..fd64df9e86e0 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -63,8 +63,6 @@ public final class MemoryStatUtil {
private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)");
private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)");
- private static final Pattern RSS_HIGH_WATERMARK_IN_KILOBYTES =
- Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
private static final Pattern PROCFS_RSS_IN_KILOBYTES =
Pattern.compile("VmRSS:\\s*(\\d+)\\s*kB");
private static final Pattern PROCFS_ANON_RSS_IN_KILOBYTES =
@@ -113,15 +111,6 @@ public final class MemoryStatUtil {
}
/**
- * Reads RSS high-water mark of a process from procfs. Returns value of the VmHWM field in
- * /proc/PID/status in kilobytes or 0 if not available.
- */
- public static int readRssHighWaterMarkFromProcfs(int pid) {
- final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid);
- return parseVmHWMFromProcfs(readFileContents(statusPath));
- }
-
- /**
* Reads cmdline of a process from procfs.
*
* Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
@@ -204,19 +193,6 @@ public final class MemoryStatUtil {
}
/**
- * Parses RSS high-water mark out from the contents of the /proc/pid/status file in procfs. The
- * returned value is in kilobytes.
- */
- @VisibleForTesting
- static int parseVmHWMFromProcfs(String procStatusContents) {
- if (procStatusContents == null || procStatusContents.isEmpty()) {
- return 0;
- }
- return (int) tryParseLong(RSS_HIGH_WATERMARK_IN_KILOBYTES, procStatusContents);
- }
-
-
- /**
* Parses cmdline out of the contents of the /proc/pid/cmdline file in procfs.
*
* Parsing is required to strip anything after first null byte.
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 0509f9f0ec11..2f9a5c952659 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -12,6 +12,7 @@ suprabh@google.com
varunshah@google.com
kwekua@google.com
bookatz@google.com
+jji@google.com
# Windows & Activities
ogunwale@google.com
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 146be5aa7044..159e5b87e5a8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -608,6 +608,10 @@ public class AppOpsService extends IAppOpsService.Stub {
private void updateProxyState(long key, int proxyUid,
@Nullable String proxyPackageName) {
+ if (proxyUid == Process.INVALID_UID) {
+ return;
+ }
+
if (mProxyUids == null) {
mProxyUids = new LongSparseLongArray();
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 066e765e6e30..77b3feec700e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -155,7 +155,7 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * The implementation of the volume manager service.
+ * The implementation of the audio service for volume, audio focus, device management...
* <p>
* This implementation focuses on delivering a responsive UI. Most methods are
* asynchronous to external calls. For example, the task of setting a volume
diff --git a/services/core/java/com/android/server/audio/TEST_MAPPING b/services/core/java/com/android/server/audio/TEST_MAPPING
new file mode 100644
index 000000000000..0d34c5372914
--- /dev/null
+++ b/services/core/java/com/android/server/audio/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsMediaTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.cts.AudioManagerTest"
+ },
+ {
+ "include-filter": "android.media.cts.AudioFocusTest"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 4af3627427d2..7302b985181b 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -981,10 +981,10 @@ public class BiometricService extends SystemService {
mStatusBarService = mInjector.getStatusBarService();
// Cache the authenticators
- for (int i = 0; i < FEATURE_ID.length; i++) {
- if (hasFeature(FEATURE_ID[i])) {
+ for (int featureId : FEATURE_ID) {
+ if (hasFeature(featureId)) {
Authenticator authenticator =
- new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i]));
+ new Authenticator(featureId, getAuthenticator(featureId));
mAuthenticators.add(authenticator);
}
}
@@ -1005,8 +1005,6 @@ public class BiometricService extends SystemService {
* and the error containing one of the {@link BiometricConstants} errors.
*/
private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) {
- int modality = TYPE_NONE;
-
// No biometric features, send error
if (mAuthenticators.isEmpty()) {
return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
@@ -1022,10 +1020,11 @@ public class BiometricService extends SystemService {
boolean hasTemplatesEnrolled = false;
boolean enabledForApps = false;
+ int modality = TYPE_NONE;
int firstHwAvailable = TYPE_NONE;
- for (int i = 0; i < mAuthenticators.size(); i++) {
- modality = mAuthenticators.get(i).getType();
- BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
+ for (Authenticator authenticatorWrapper : mAuthenticators) {
+ modality = authenticatorWrapper.getType();
+ BiometricAuthenticator authenticator = authenticatorWrapper.getAuthenticator();
if (authenticator.isHardwareDetected()) {
isHardwareDetected = true;
if (firstHwAvailable == TYPE_NONE) {
@@ -1036,9 +1035,6 @@ public class BiometricService extends SystemService {
if (authenticator.hasEnrolledTemplates(userId)) {
hasTemplatesEnrolled = true;
if (isEnabledForApp(modality, userId)) {
- // TODO(b/110907543): When face settings (and other settings) have both a
- // user toggle as well as a work profile settings page, this needs to be
- // updated to reflect the correct setting.
enabledForApps = true;
break;
}
@@ -1555,7 +1551,7 @@ public class BiometricService extends SystemService {
}
/**
- * authenticate() (above) which is called from BiometricPrompt determines which
+ * handleAuthenticate() (above) which is called from BiometricPrompt determines which
* modality/modalities to start authenticating with. authenticateInternal() should only be
* used for:
* 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 09f52860e069..96af74a60b1d 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -79,7 +79,7 @@ public class CameraServiceProxy extends SystemService
private static final int MSG_SWITCH_USER = 1;
private static final int RETRY_DELAY_TIME = 20; //ms
- private static final int RETRY_TIMES = 30;
+ private static final int RETRY_TIMES = 60;
// Maximum entries to keep in usage history before dumping out
private static final int MAX_USAGE_HISTORY = 100;
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index fc38735509f0..81e507cd24cf 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -19,7 +19,9 @@ package com.android.server.compat;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.util.Slog;
+import android.util.StatsLog;
+import com.android.internal.compat.ChangeReporter;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.util.DumpUtils;
@@ -34,23 +36,27 @@ public class PlatformCompat extends IPlatformCompat.Stub {
private static final String TAG = "Compatibility";
private final Context mContext;
+ private final ChangeReporter mChangeReporter;
public PlatformCompat(Context context) {
mContext = context;
+ mChangeReporter = new ChangeReporter();
}
@Override
public void reportChange(long changeId, ApplicationInfo appInfo) {
- Slog.d(TAG, "Compat change reported: " + changeId + "; UID " + appInfo.uid);
- // TODO log via StatsLog
+ reportChange(changeId, appInfo, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
}
@Override
public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) {
- reportChange(changeId, appInfo);
+ reportChange(changeId, appInfo,
+ StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
return true;
}
+ reportChange(changeId, appInfo,
+ StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__DISABLED);
return false;
}
@@ -59,4 +65,13 @@ public class PlatformCompat extends IPlatformCompat.Stub {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
CompatConfig.get().dumpConfig(pw);
}
+
+ private void reportChange(long changeId, ApplicationInfo appInfo, int state) {
+ int uid = appInfo.uid;
+ //TODO(b/138374585): Implement rate limiting for the logs.
+ Slog.d(TAG, ChangeReporter.createLogString(uid, changeId, state));
+ mChangeReporter.reportChange(uid, changeId,
+ state, /* source */
+ StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__SOURCE__SYSTEM_SERVER);
+ }
}
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 7fb5b191a9b0..0bf43b6d1b9c 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -609,6 +609,9 @@ public final class ColorDisplayService extends SystemService {
@Override
public void onAnimationEnd(Animator animator) {
+ Slog.d(TAG, tintController.getClass().getSimpleName()
+ + " Animation cancelled: " + mIsCancelled
+ + " to matrix: " + TintController.matrixToString(to, 16));
if (!mIsCancelled) {
// Ensure final color matrix is set at the end of the animation. If the
// animation is cancelled then don't set the final color matrix so the new
@@ -1314,8 +1317,10 @@ public final class ColorDisplayService extends SystemService {
* Reset the CCT value for the display white balance transform to its default value.
*/
public boolean resetDisplayWhiteBalanceColorTemperature() {
- return setDisplayWhiteBalanceColorTemperature(getContext().getResources()
- .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault));
+ int temperatureDefault = getContext().getResources()
+ .getInteger(R.integer.config_displayWhiteBalanceColorTemperatureDefault);
+ Slog.d(TAG, "resetDisplayWhiteBalanceColorTemperature: " + temperatureDefault);
+ return setDisplayWhiteBalanceColorTemperature(temperatureDefault);
}
/**
diff --git a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
index d2c6cd9f1007..3f1c222ab520 100644
--- a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
+++ b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
@@ -149,8 +149,6 @@ final class DisplayWhiteBalanceTintController extends TintController {
cct = mTemperatureMax;
}
- Slog.d(ColorDisplayService.TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
-
synchronized (mLock) {
mCurrentColorTemperature = cct;
@@ -185,6 +183,9 @@ final class DisplayWhiteBalanceTintController extends TintController {
java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
}
+
+ Slog.d(ColorDisplayService.TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct
+ + " matrix = " + matrixToString(mMatrixDisplayWhiteBalance, 16));
}
@Override
@@ -225,28 +226,6 @@ final class DisplayWhiteBalanceTintController extends TintController {
}
}
- /**
- * Format a given matrix into a string.
- *
- * @param matrix the matrix to format
- * @param columns number of columns in the matrix
- */
- private String matrixToString(float[] matrix, int columns) {
- if (matrix == null || columns <= 0) {
- Slog.e(ColorDisplayService.TAG, "Invalid arguments when formatting matrix to string");
- return "";
- }
-
- final StringBuilder sb = new StringBuilder("");
- for (int i = 0; i < matrix.length; i++) {
- if (i % columns == 0) {
- sb.append("\n ");
- }
- sb.append(String.format("%9.6f", matrix[i]));
- }
- return sb.toString();
- }
-
private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
return new ColorSpace.Rgb(
"Display Color Space",
diff --git a/services/core/java/com/android/server/display/color/TintController.java b/services/core/java/com/android/server/display/color/TintController.java
index b291c645027a..8d8b9b2af04e 100644
--- a/services/core/java/com/android/server/display/color/TintController.java
+++ b/services/core/java/com/android/server/display/color/TintController.java
@@ -18,6 +18,7 @@ package com.android.server.display.color;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.util.Slog;
import java.io.PrintWriter;
@@ -95,4 +96,29 @@ abstract class TintController {
* Returns whether or not this transform type is available on this device.
*/
public abstract boolean isAvailable(Context context);
+
+ /**
+ * Format a given matrix into a string.
+ *
+ * @param matrix the matrix to format
+ * @param columns number of columns in the matrix
+ */
+ static String matrixToString(float[] matrix, int columns) {
+ if (matrix == null || columns <= 0) {
+ Slog.e(ColorDisplayService.TAG, "Invalid arguments when formatting matrix to string,"
+ + " matrix is null: " + (matrix == null)
+ + " columns: " + columns);
+ return "";
+ }
+
+ final StringBuilder sb = new StringBuilder("");
+ for (int i = 0; i < matrix.length; i++) {
+ if (i % columns == 0) {
+ sb.append("\n ");
+ }
+ sb.append(String.format("%9.6f", matrix[i]));
+ }
+ return sb.toString();
+ }
+
}
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 02ec10e2d49d..7b1f4c3222f3 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -95,6 +95,11 @@ public class DisplayWhiteBalanceController implements
// A piecewise linear relationship between high light brightness and high light bias.
private Spline.LinearSpline mHighLightAmbientBrightnessToBiasSpline;
+ private float mLatestAmbientColorTemperature;
+ private float mLatestAmbientBrightness;
+ private float mLatestLowLightBias;
+ private float mLatestHighLightBias;
+
/**
* @param brightnessSensor
* The sensor used to detect changes in the ambient brightness.
@@ -348,6 +353,7 @@ public class DisplayWhiteBalanceController implements
public void updateAmbientColorTemperature() {
final long time = System.currentTimeMillis();
float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
+ mLatestAmbientColorTemperature = ambientColorTemperature;
if (mAmbientToDisplayColorTemperatureSpline != null && ambientColorTemperature != -1.0f) {
ambientColorTemperature =
@@ -355,6 +361,7 @@ public class DisplayWhiteBalanceController implements
}
float ambientBrightness = mBrightnessFilter.getEstimate(time);
+ mLatestAmbientBrightness = ambientBrightness;
if (ambientColorTemperature != -1.0f &&
mLowLightAmbientBrightnessToBiasSpline != null) {
@@ -362,6 +369,7 @@ public class DisplayWhiteBalanceController implements
ambientColorTemperature =
bias * ambientColorTemperature + (1.0f - bias)
* mLowLightAmbientColorTemperature;
+ mLatestLowLightBias = bias;
}
if (ambientColorTemperature != -1.0f &&
mHighLightAmbientBrightnessToBiasSpline != null) {
@@ -369,6 +377,7 @@ public class DisplayWhiteBalanceController implements
ambientColorTemperature =
(1.0f - bias) * ambientColorTemperature + bias
* mHighLightAmbientColorTemperature;
+ mLatestHighLightBias = bias;
}
if (mAmbientColorTemperatureOverride != -1.0f) {
@@ -426,6 +435,11 @@ public class DisplayWhiteBalanceController implements
}
mPendingAmbientColorTemperature = -1.0f;
mAmbientColorTemperatureHistory.add(mAmbientColorTemperature);
+ Slog.d(TAG, "Display cct: " + mAmbientColorTemperature
+ + " Latest ambient cct: " + mLatestAmbientColorTemperature
+ + " Latest ambient lux: " + mLatestAmbientBrightness
+ + " Latest low light bias: " + mLatestLowLightBias
+ + " Latest high light bias: " + mLatestHighLightBias);
mColorDisplayServiceInternal.setDisplayWhiteBalanceColorTemperature(
(int) mAmbientColorTemperature);
mLastAmbientColorTemperature = mAmbientColorTemperature;
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index b05742af04ee..ccfc98e2291b 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -18,6 +18,7 @@ package com.android.server.location;
import android.content.Context;
import android.location.Location;
+import android.os.Binder;
import android.os.Bundle;
import android.os.WorkSource;
@@ -80,7 +81,12 @@ public abstract class AbstractLocationProvider {
* any thread.
*/
protected void setEnabled(boolean enabled) {
- mLocationProviderManager.onSetEnabled(enabled);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mLocationProviderManager.onSetEnabled(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
@@ -88,21 +94,36 @@ public abstract class AbstractLocationProvider {
* any thread.
*/
protected void setProperties(ProviderProperties properties) {
- mLocationProviderManager.onSetProperties(properties);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mLocationProviderManager.onSetProperties(properties);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
* Call this method to report a new location. May be called from any thread.
*/
protected void reportLocation(Location location) {
- mLocationProviderManager.onReportLocation(location);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mLocationProviderManager.onReportLocation(location);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
* Call this method to report a new location. May be called from any thread.
*/
protected void reportLocation(List<Location> locations) {
- mLocationProviderManager.onReportLocation(locations);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mLocationProviderManager.onReportLocation(locations);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 9b9f4de7a18f..bc051547a53f 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -129,6 +129,12 @@ public class NotificationComparator
return -1 * Integer.compare(leftPriority, rightPriority);
}
+ final boolean leftInterruptive = left.isInterruptive();
+ final boolean rightInterruptive = right.isInterruptive();
+ if (leftInterruptive != rightInterruptive) {
+ return -1 * Boolean.compare(leftInterruptive, rightInterruptive);
+ }
+
// then break ties by time, most recent first
return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2d4c6cf70847..d480cb6e9800 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5759,7 +5759,9 @@ public class NotificationManagerService extends SystemService {
notification.flags |=
old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
r.isUpdate = true;
- r.setTextChanged(isVisuallyInterruptive(old, r));
+ final boolean isInterruptive = isVisuallyInterruptive(old, r);
+ r.setTextChanged(isInterruptive);
+ r.setInterruptive(isInterruptive);
}
mNotificationsByKey.put(n.getKey(), r);
@@ -5858,7 +5860,6 @@ public class NotificationManagerService extends SystemService {
Notification oldN = old.sbn.getNotification();
Notification newN = r.sbn.getNotification();
-
if (oldN.extras == null || newN.extras == null) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
@@ -5890,6 +5891,7 @@ public class NotificationManagerService extends SystemService {
}
return true;
}
+
// Do not compare Spannables (will always return false); compare unstyled Strings
final String oldText = String.valueOf(oldN.extras.get(Notification.EXTRA_TEXT));
final String newText = String.valueOf(newN.extras.get(Notification.EXTRA_TEXT));
@@ -5904,6 +5906,7 @@ public class NotificationManagerService extends SystemService {
}
return true;
}
+
if (oldN.hasCompletedProgress() != newN.hasCompletedProgress()) {
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
@@ -5911,6 +5914,16 @@ public class NotificationManagerService extends SystemService {
}
return true;
}
+
+ // Fields below are invisible to bubbles.
+ if (r.canBubble()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Slog.v(TAG, "INTERRUPTIVENESS: "
+ + r.getKey() + " is not interruptive: bubble");
+ }
+ return false;
+ }
+
// Actions
if (Notification.areActionsVisiblyDifferent(oldN, newN)) {
if (DEBUG_INTERRUPTIVENESS) {
@@ -5944,7 +5957,6 @@ public class NotificationManagerService extends SystemService {
} catch (Exception e) {
Slog.w(TAG, "error recovering builder", e);
}
-
return false;
}
@@ -6139,12 +6151,17 @@ public class NotificationManagerService extends SystemService {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is not interruptive: summary");
}
+ } else if (record.canBubble()) {
+ if (DEBUG_INTERRUPTIVENESS) {
+ Slog.v(TAG, "INTERRUPTIVENESS: "
+ + record.getKey() + " is not interruptive: bubble");
+ }
} else {
+ record.setInterruptive(true);
if (DEBUG_INTERRUPTIVENESS) {
Slog.v(TAG, "INTERRUPTIVENESS: "
+ record.getKey() + " is interruptive: alerted");
}
- record.setInterruptive(true);
}
MetricsLogger.action(record.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_ALERT)
@@ -6503,15 +6520,21 @@ public class NotificationManagerService extends SystemService {
int indexBefore = findNotificationRecordIndexLocked(record);
boolean interceptBefore = record.isIntercepted();
int visibilityBefore = record.getPackageVisibilityOverride();
+ boolean interruptiveBefore = record.isInterruptive();
+
recon.applyChangesLocked(record);
applyZenModeLocked(record);
mRankingHelper.sort(mNotificationList);
- int indexAfter = findNotificationRecordIndexLocked(record);
- boolean interceptAfter = record.isIntercepted();
- int visibilityAfter = record.getPackageVisibilityOverride();
- changed = indexBefore != indexAfter || interceptBefore != interceptAfter
- || visibilityBefore != visibilityAfter;
- if (interceptBefore && !interceptAfter
+ boolean indexChanged = indexBefore != findNotificationRecordIndexLocked(record);
+ boolean interceptChanged = interceptBefore != record.isIntercepted();
+ boolean visibilityChanged = visibilityBefore != record.getPackageVisibilityOverride();
+
+ // Broadcast isInterruptive changes for bubbles.
+ boolean interruptiveChanged =
+ record.canBubble() && (interruptiveBefore != record.isInterruptive());
+
+ changed = indexChanged || interceptChanged || visibilityChanged || interruptiveChanged;
+ if (interceptBefore && !record.isIntercepted()
&& record.isNewEnoughForAlerting(System.currentTimeMillis())) {
buzzBeepBlinkLocked(record);
}
@@ -7661,7 +7684,8 @@ public class NotificationManagerService extends SystemService {
record.getSound() != null || record.getVibration() != null,
record.getSystemGeneratedSmartActions(),
record.getSmartReplies(),
- record.canBubble()
+ record.canBubble(),
+ record.isInterruptive()
);
rankings.add(ranking);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 965ddc9f2782..f8b3fb259089 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -744,7 +744,7 @@ public final class OverlayManagerService extends SystemService {
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
@NonNull final String[] args, @NonNull final ShellCallback callback,
@NonNull final ResultReceiver resultReceiver) {
- (new OverlayManagerShellCommand(this)).exec(
+ (new OverlayManagerShellCommand(getContext(), this)).exec(
this, in, out, err, args, callback, resultReceiver);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
index 99f583978535..eb432759cb02 100644
--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
+++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java
@@ -18,15 +18,23 @@ package com.android.server.om;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
+import android.util.TypedValue;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Implementation of 'cmd overlay' commands.
@@ -36,9 +44,11 @@ import java.util.Map;
* for a list of available commands.
*/
final class OverlayManagerShellCommand extends ShellCommand {
+ private final Context mContext;
private final IOverlayManager mInterface;
- OverlayManagerShellCommand(@NonNull final IOverlayManager iom) {
+ OverlayManagerShellCommand(@NonNull final Context ctx, @NonNull final IOverlayManager iom) {
+ mContext = ctx;
mInterface = iom;
}
@@ -60,6 +70,8 @@ final class OverlayManagerShellCommand extends ShellCommand {
return runEnableExclusive();
case "set-priority":
return runSetPriority();
+ case "lookup":
+ return runLookup();
default:
return handleDefaultCommands(cmd);
}
@@ -102,6 +114,10 @@ final class OverlayManagerShellCommand extends ShellCommand {
out.println(" 'lowest', change priority of PACKAGE to the lowest priority.");
out.println(" If PARENT is the special keyword 'highest', change priority of");
out.println(" PACKAGE to the highest priority.");
+ out.println(" lookup [--verbose] PACKAGE-TO-LOAD PACKAGE:TYPE/NAME");
+ out.println(" Load a package and print the value of a given resource");
+ out.println(" applying the current configuration and enabled overlays.");
+ out.println(" For a more fine-grained alernative, use 'idmap2 lookup'.");
}
private int runList() throws RemoteException {
@@ -253,4 +269,92 @@ final class OverlayManagerShellCommand extends ShellCommand {
return mInterface.setPriority(packageName, newParentPackageName, userId) ? 0 : 1;
}
}
+
+ private int runLookup() throws RemoteException {
+ final PrintWriter out = getOutPrintWriter();
+ final PrintWriter err = getErrPrintWriter();
+
+ final boolean verbose = "--verbose".equals(getNextOption());
+
+ final String packageToLoad = getNextArgRequired();
+
+ final String fullyQualifiedResourceName = getNextArgRequired(); // package:type/name
+ final Pattern regex = Pattern.compile("(.*?):(.*?)/(.*?)");
+ final Matcher matcher = regex.matcher(fullyQualifiedResourceName);
+ if (!matcher.matches()) {
+ err.println("Error: bad resource name, doesn't match package:type/name");
+ return 1;
+ }
+
+ final PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ err.println("Error: failed to get package manager");
+ return 1;
+ }
+
+ final Resources res;
+ try {
+ res = pm.getResourcesForApplication(packageToLoad);
+ } catch (PackageManager.NameNotFoundException e) {
+ err.println("Error: failed to get resources for package " + packageToLoad);
+ return 1;
+ }
+ final AssetManager assets = res.getAssets();
+ try {
+ assets.setResourceResolutionLoggingEnabled(true);
+
+ // first try as non-complex type ...
+ try {
+ final TypedValue value = new TypedValue();
+ res.getValue(fullyQualifiedResourceName, value, false /* resolveRefs */);
+ final CharSequence valueString = value.coerceToString();
+ final String resolution = assets.getLastResourceResolution();
+
+ res.getValue(fullyQualifiedResourceName, value, true /* resolveRefs */);
+ final CharSequence resolvedString = value.coerceToString();
+
+ if (verbose) {
+ out.println(resolution);
+ }
+
+ if (valueString.equals(resolvedString)) {
+ out.println(valueString);
+ } else {
+ out.println(valueString + " -> " + resolvedString);
+ }
+ return 0;
+ } catch (Resources.NotFoundException e) {
+ // this is ok, resource could still be a complex type
+ }
+
+ // ... then try as complex type
+ try {
+
+ final String pkg = matcher.group(1);
+ final String type = matcher.group(2);
+ final String name = matcher.group(3);
+ final int resid = res.getIdentifier(name, type, pkg);
+ if (resid == 0) {
+ throw new Resources.NotFoundException();
+ }
+ final TypedArray array = res.obtainTypedArray(resid);
+ if (verbose) {
+ out.println(assets.getLastResourceResolution());
+ }
+ TypedValue tv = new TypedValue();
+ for (int i = 0; i < array.length(); i++) {
+ array.getValue(i, tv);
+ out.println(tv.coerceToString());
+ }
+ array.recycle();
+ return 0;
+ } catch (Resources.NotFoundException e) {
+ // give up
+ err.println("Error: failed to get the resource " + fullyQualifiedResourceName);
+ return 1;
+ }
+ } finally {
+ assets.setResourceResolutionLoggingEnabled(false);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 48678bfac86e..61ea84f9dc7f 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -39,6 +39,7 @@ import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.FgThread;
import com.android.server.compat.PlatformCompat;
@@ -55,14 +56,11 @@ import java.util.Set;
* The entity responsible for filtering visibility between apps based on declarations in their
* manifests.
*/
-class AppsFilter {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class AppsFilter {
private static final String TAG = PackageManagerService.TAG;
- // Forces filtering logic to run for debug purposes.
- // STOPSHIP (b/136675067): should be false after development is complete
- private static final boolean DEBUG_RUN_WHEN_DISABLED = false;
-
// Logs all filtering instead of enforcing
private static final boolean DEBUG_ALLOW_ALL = false;
@@ -128,15 +126,13 @@ class AppsFilter {
/** @return true if the feature is enabled for the given package. */
boolean packageIsEnabled(PackageParser.Package pkg);
+
}
private static class FeatureConfigImpl implements FeatureConfig {
private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
-
- // STOPSHIP(patb): set this to true if we plan to launch this in R
- private static final boolean DEFAULT_ENABLED_STATE = false;
private final PackageManagerService.Injector mInjector;
- private volatile boolean mFeatureEnabled = DEFAULT_ENABLED_STATE;
+ private volatile boolean mFeatureEnabled = true;
private FeatureConfigImpl(PackageManagerService.Injector injector) {
mInjector = injector;
@@ -146,13 +142,13 @@ class AppsFilter {
public void onSystemReady() {
mFeatureEnabled = DeviceConfig.getBoolean(
NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME,
- DEFAULT_ENABLED_STATE);
+ true);
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
properties -> {
synchronized (FeatureConfigImpl.this) {
mFeatureEnabled = properties.getBoolean(
- FILTERING_ENABLED_NAME, DEFAULT_ENABLED_STATE);
+ FILTERING_ENABLED_NAME, true);
}
});
}
@@ -246,25 +242,25 @@ class AppsFilter {
}
/**
- * Marks that a package initiated an interaction with another package, granting visibility of
- * the prior from the former.
+ * Grants access based on an interaction between a calling and target package, granting
+ * visibility of the caller from the target.
*
- * @param initiatingPackage the package initiating the interaction
+ * @param callingPackage the package initiating the interaction
* @param targetPackage the package being interacted with and thus gaining visibility of the
* initiating package.
* @param userId the user in which this interaction was taking place
*/
- private void markAppInteraction(
- PackageSetting initiatingPackage, PackageSetting targetPackage, int userId) {
+ public void grantImplicitAccess(
+ String callingPackage, String targetPackage, int userId) {
HashMap<String, Set<String>> currentUser = mImplicitlyQueryable.get(userId);
if (currentUser == null) {
currentUser = new HashMap<>();
mImplicitlyQueryable.put(userId, currentUser);
}
- if (!currentUser.containsKey(targetPackage.pkg.packageName)) {
- currentUser.put(targetPackage.pkg.packageName, new HashSet<>());
+ if (!currentUser.containsKey(targetPackage)) {
+ currentUser.put(targetPackage, new HashSet<>());
}
- currentUser.get(targetPackage.pkg.packageName).add(initiatingPackage.pkg.packageName);
+ currentUser.get(targetPackage).add(callingPackage);
}
public void onSystemReady() {
@@ -353,7 +349,7 @@ class AppsFilter {
public boolean shouldFilterApplication(int callingUid, @Nullable SettingBase callingSetting,
PackageSetting targetPkgSetting, int userId) {
final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
- if (!featureEnabled && !DEBUG_RUN_WHEN_DISABLED) {
+ if (!featureEnabled) {
if (DEBUG_LOGGING) {
Slog.d(TAG, "filtering disabled; skipped");
}
@@ -374,8 +370,6 @@ class AppsFilter {
callingPkgSetting = (PackageSetting) callingSetting;
if (!shouldFilterApplicationInternal(callingPkgSetting, targetPkgSetting,
userId)) {
- // TODO: actually base this on a start / launch (not just a query)
- markAppInteraction(callingPkgSetting, targetPkgSetting, userId);
return false;
}
} else if (callingSetting instanceof SharedUserSetting) {
@@ -386,8 +380,6 @@ class AppsFilter {
final PackageSetting packageSetting = packageSettings.valueAt(i);
if (!shouldFilterApplicationInternal(packageSetting, targetPkgSetting,
userId)) {
- // TODO: actually base this on a start / launch (not just a query)
- markAppInteraction(packageSetting, targetPkgSetting, userId);
return false;
}
if (callingPkgSetting == null && packageSetting.pkg != null) {
@@ -403,21 +395,12 @@ class AppsFilter {
return true;
}
}
- if (!featureEnabled) {
- return false;
- }
- if (mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
- if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting,
- DEBUG_ALLOW_ALL ? "ALLOWED" : "BLOCKED");
- }
- return !DEBUG_ALLOW_ALL;
- } else {
- if (DEBUG_LOGGING) {
- log(callingPkgSetting, targetPkgSetting, "DISABLED");
- }
- return false;
+
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting,
+ DEBUG_ALLOW_ALL ? "ALLOWED" : "BLOCKED");
}
+ return !DEBUG_ALLOW_ALL;
}
private boolean shouldFilterApplicationInternal(
@@ -425,6 +408,12 @@ class AppsFilter {
final String callingName = callingPkgSetting.pkg.packageName;
final PackageParser.Package targetPkg = targetPkgSetting.pkg;
+ if (!mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
+ if (DEBUG_LOGGING) {
+ log(callingPkgSetting, targetPkgSetting, "DISABLED");
+ }
+ return false;
+ }
// This package isn't technically installed and won't be written to settings, so we can
// treat it as filtered until it's available again.
if (targetPkg == null) {
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 5eaddf9ffa05..9e04c4b69bd0 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -401,7 +401,7 @@ class InstantAppRegistry {
@GuardedBy("mService.mLock")
public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent,
- int targetAppId, int instantAppId) {
+ int instantAppId, int targetAppId) {
if (mInstalledInstantAppUids == null) {
return; // no instant apps installed; no need to grant
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 58596aa0aad2..587862ad1963 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17124,8 +17124,7 @@ public class PackageManagerService extends IPackageManager.Stub
if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) {
try {
VerityUtils.setUpFsverity(filePath, signaturePath);
- } catch (IOException | DigestException | NoSuchAlgorithmException
- | SecurityException e) {
+ } catch (IOException e) {
throw new PrepareFailure(PackageManager.INSTALL_FAILED_BAD_SIGNATURE,
"Failed to enable fs-verity: " + e);
}
@@ -19787,7 +19786,7 @@ public class PackageManagerService extends IPackageManager.Stub
public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
UserManagerService ums = UserManagerService.getInstance();
- if (ums != null) {
+ if (ums != null && !sessionInfo.isStaged()) {
final UserInfo parent = ums.getProfileParent(userId);
final int launcherUid = (parent != null) ? parent.id : userId;
final ComponentName launcherComponent = getDefaultHomeActivity(launcherUid);
@@ -23380,11 +23379,24 @@ public class PackageManagerService extends IPackageManager.Stub
}
@Override
- public void grantEphemeralAccess(int userId, Intent intent,
- int targetAppId, int ephemeralAppId) {
+ public void grantImplicitAccess(int userId, Intent intent,
+ int callingAppId, int targetAppId) {
synchronized (mLock) {
- mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
- targetAppId, ephemeralAppId);
+ final PackageParser.Package callingPackage = getPackage(
+ UserHandle.getUid(userId, callingAppId));
+ final PackageParser.Package targetPackage = getPackage(
+ UserHandle.getUid(userId, targetAppId));
+ if (callingPackage == null || targetPackage == null) {
+ return;
+ }
+
+ if (isInstantApp(callingPackage.packageName, userId)) {
+ mInstantAppRegistry.grantInstantAccessLPw(userId, intent,
+ callingAppId, targetAppId);
+ } else {
+ mAppsFilter.grantImplicitAccess(
+ callingPackage.packageName, targetPackage.packageName, userId);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 597246884ca5..1873a4ec98d9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -62,6 +62,7 @@ import android.os.PersistableBundle;
import android.os.Process;
import android.os.SELinux;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -94,6 +95,7 @@ import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.PermissionSettings;
import com.android.server.pm.permission.PermissionsState;
import com.android.server.pm.permission.PermissionsState.PermissionState;
+import com.android.server.utils.TimingsTraceAndSlog;
import libcore.io.IoUtils;
@@ -4012,8 +4014,11 @@ public final class Settings {
}
}
- void createNewUserLI(@NonNull PackageManagerService service, @NonNull Installer installer,
- int userHandle, String[] disallowedPackages) {
+ void createNewUserLI(@NonNull PackageManagerService service,
+ @NonNull Installer installer, int userHandle, String[] disallowedPackages) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
+ Trace.TRACE_TAG_PACKAGE_MANAGER);
+ t.traceBegin("createNewUser-" + userHandle);
String[] volumeUuids;
String[] names;
int[] appIds;
@@ -4051,6 +4056,7 @@ public final class Settings {
targetSdkVersions[i] = ps.pkg.applicationInfo.targetSdkVersion;
}
}
+ t.traceBegin("createAppData");
for (int i = 0; i < packagesCount; i++) {
if (names[i] == null) {
continue;
@@ -4064,9 +4070,11 @@ public final class Settings {
Slog.w(TAG, "Failed to prepare app data", e);
}
}
+ t.traceEnd(); // createAppData
synchronized (mLock) {
applyDefaultPreferredAppsLPw(userHandle);
}
+ t.traceEnd(); // createNewUser
}
void removeUserLPw(int userId) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 3482e92a6fbd..1cea4ca1b945 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -301,7 +301,7 @@ public class StagingManager {
// Greedily re-trigger the pre-reboot verification.
Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to be "
+ "verified, resuming pre-reboot verification");
- mPreRebootVerificationHandler.startPreRebootVerification(session);
+ mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
return;
}
if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
@@ -429,7 +429,7 @@ public class StagingManager {
return;
}
mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
- originalSession);
+ originalSession.sessionId);
});
apkSession.commit(receiver.getIntentSender(), false);
return;
@@ -526,7 +526,7 @@ public class StagingManager {
void commitSession(@NonNull PackageInstallerSession session) {
updateStoredSession(session);
- mPreRebootVerificationHandler.startPreRebootVerification(session);
+ mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
}
@Nullable
@@ -653,7 +653,7 @@ public class StagingManager {
if (!session.isStagedSessionReady()) {
// The framework got restarted before the pre-reboot verification could complete,
// restart the verification.
- mPreRebootVerificationHandler.startPreRebootVerification(session);
+ mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
} else {
// Session had already being marked ready. Start the checks to verify if there is any
// follow-up work.
@@ -737,7 +737,16 @@ public class StagingManager {
@Override
public void handleMessage(Message msg) {
- PackageInstallerSession session = (PackageInstallerSession) msg.obj;
+ final int sessionId = msg.arg1;
+ final PackageInstallerSession session;
+ synchronized (mStagedSessions) {
+ session = mStagedSessions.get(sessionId);
+ }
+ // Maybe session was aborted before pre-reboot verification was complete
+ if (session == null) {
+ Slog.d(TAG, "Stopping pre-reboot verification for sessionId: " + sessionId);
+ return;
+ }
switch (msg.what) {
case MSG_PRE_REBOOT_VERIFICATION_START:
handlePreRebootVerification_Start(session);
@@ -755,20 +764,20 @@ public class StagingManager {
}
// Method for starting the pre-reboot verification
- private void startPreRebootVerification(PackageInstallerSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, session).sendToTarget();
+ private void startPreRebootVerification(int sessionId) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
}
- private void notifyPreRebootVerification_Start_Complete(PackageInstallerSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, session).sendToTarget();
+ private void notifyPreRebootVerification_Start_Complete(int sessionId) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, sessionId, 0).sendToTarget();
}
- private void notifyPreRebootVerification_Apex_Complete(PackageInstallerSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session).sendToTarget();
+ private void notifyPreRebootVerification_Apex_Complete(int sessionId) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, sessionId, 0).sendToTarget();
}
- private void notifyPreRebootVerification_Apk_Complete(PackageInstallerSession session) {
- obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session).sendToTarget();
+ private void notifyPreRebootVerification_Apk_Complete(int sessionId) {
+ obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, sessionId, 0).sendToTarget();
}
/**
@@ -778,7 +787,7 @@ public class StagingManager {
*/
private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId);
- notifyPreRebootVerification_Start_Complete(session);
+ notifyPreRebootVerification_Start_Complete(session.sessionId);
}
/**
@@ -808,7 +817,7 @@ public class StagingManager {
}
}
- notifyPreRebootVerification_Apex_Complete(session);
+ notifyPreRebootVerification_Apex_Complete(session.sessionId);
}
/**
@@ -819,7 +828,7 @@ public class StagingManager {
*/
private void handlePreRebootVerification_Apk(@NonNull PackageInstallerSession session) {
if (!sessionContainsApk(session)) {
- notifyPreRebootVerification_Apk_Complete(session);
+ notifyPreRebootVerification_Apk_Complete(session.sessionId);
return;
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index dd1adb703e62..9371c4473bb3 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2713,14 +2713,25 @@ public class UserManagerService extends IUserManager.Stub {
return createUserInternalUnchecked(name, flags, parentId, disallowedPackages);
}
- private UserInfo createUserInternalUnchecked(String name, int flags, int parentId,
- String[] disallowedPackages) {
+ private UserInfo createUserInternalUnchecked(@Nullable String name, int flags,
+ int parentId, @Nullable String[] disallowedPackages) {
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("createUser");
+ UserInfo userInfo =
+ createUserInternalUncheckedNoTracing(name, flags, parentId, disallowedPackages, t);
+ t.traceEnd();
+ return userInfo;
+ }
+
+ private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name, int flags,
+ int parentId, @Nullable String[] disallowedPackages, @NonNull TimingsTraceAndSlog t) {
DeviceStorageMonitorInternal dsm = LocalServices
.getService(DeviceStorageMonitorInternal.class);
if (dsm.isMemoryLow()) {
Log.w(LOG_TAG, "Cannot add user. Not enough space on disk.");
return null;
}
+
final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0;
final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0;
@@ -2820,11 +2831,21 @@ public class UserManagerService extends IUserManager.Stub {
}
}
}
+
+ t.traceBegin("createUserKey");
final StorageManager storage = mContext.getSystemService(StorageManager.class);
storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
+ t.traceEnd();
+
+ t.traceBegin("prepareUserData");
mUserDataPreparer.prepareUserData(userId, userInfo.serialNumber,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+ t.traceEnd();
+
+ t.traceBegin("PM.createNewUser");
mPm.createNewUser(userId, disallowedPackages);
+ t.traceEnd();
+
userInfo.partial = false;
synchronized (mPackagesLock) {
writeUserLP(userData);
@@ -2839,7 +2860,11 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mRestrictionsLock) {
mBaseUserRestrictions.append(userId, restrictions);
}
+
+ t.traceBegin("PM.onNewUserCreated");
mPm.onNewUserCreated(userId);
+ t.traceEnd();
+
Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e57f43685108..eb648b33c4ca 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -542,6 +542,10 @@ public final class PowerManagerService extends SystemService
// True if we in the process of performing a forceSuspend
private boolean mForceSuspendActive;
+ // Transition to Doze is in progress. We have transitioned to WAKEFULNESS_DOZING,
+ // but the DreamService has not yet been told to start (it's an async process).
+ private boolean mDozeStartInProgress;
+
private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver {
@Override
public void onUserSwitching(int newUserId) throws RemoteException {}
@@ -1514,6 +1518,7 @@ public final class PowerManagerService extends SystemService
mLastSleepTime = eventTime;
mLastSleepReason = reason;
mSandmanSummoned = true;
+ mDozeStartInProgress = true;
setWakefulnessLocked(WAKEFULNESS_DOZING, reason, eventTime);
// Report the number of wake locks that will be cleared by going to sleep.
@@ -1601,6 +1606,10 @@ public final class PowerManagerService extends SystemService
mWakefulness = wakefulness;
mWakefulnessChanging = true;
mDirty |= DIRTY_WAKEFULNESS;
+
+ // This is only valid while we are in wakefulness dozing. Set to false otherwise.
+ mDozeStartInProgress &= (mWakefulness == WAKEFULNESS_DOZING);
+
if (mNotifier != null) {
mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime);
}
@@ -1631,6 +1640,9 @@ public final class PowerManagerService extends SystemService
if (mWakefulness == WAKEFULNESS_DOZING
&& (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
return; // wait until dream has enabled dozing
+ } else {
+ // Doze wakelock acquired (doze started) or device is no longer dozing.
+ mDozeStartInProgress = false;
}
if (mWakefulness == WAKEFULNESS_DOZING || mWakefulness == WAKEFULNESS_ASLEEP) {
logSleepTimeoutRecapturedLocked();
@@ -2309,6 +2321,10 @@ public final class PowerManagerService extends SystemService
isDreaming = false;
}
+ // At this point, we either attempted to start the dream or no attempt will be made,
+ // so stop holding the display suspend blocker for Doze.
+ mDozeStartInProgress = false;
+
// Update dream state.
synchronized (mLock) {
// Remember the initial battery level when the dream started.
@@ -2734,6 +2750,16 @@ public final class PowerManagerService extends SystemService
if (mScreenBrightnessBoostInProgress) {
return true;
}
+
+ // When we transition to DOZING, we have to keep the display suspend blocker
+ // up until the Doze service has a change to acquire the DOZE wakelock.
+ // Here we wait for mWakefulnessChanging to become false since the wakefulness
+ // transition to DOZING isn't considered "changed" until the doze wake lock is
+ // acquired.
+ if (mWakefulness == WAKEFULNESS_DOZING && mDozeStartInProgress) {
+ return true;
+ }
+
// Let the system suspend if the screen is off or dozing.
return false;
}
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index b1db46fb3276..856a40f3ef12 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -26,27 +26,19 @@ import android.util.Slog;
import android.util.apk.ApkSignatureVerifier;
import android.util.apk.ByteBufferFactory;
import android.util.apk.SignatureNotFoundException;
-import android.util.apk.VerityBuilder;
import libcore.util.HexEncoding;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.FileChannel;
import java.nio.file.Files;
-import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.DigestException;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
-import sun.security.pkcs.PKCS7;
-
/** Provides fsverity related operations. */
abstract public class VerityUtils {
private static final String TAG = "VerityUtils";
@@ -60,8 +52,6 @@ abstract public class VerityUtils {
/** The maximum size of signature file. This is just to avoid potential abuse. */
private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
- private static final int COMMON_LINUX_PAGE_SIZE_IN_BYTES = 4096;
-
private static final boolean DEBUG = false;
/** Returns true if the given file looks like containing an fs-verity signature. */
@@ -74,42 +64,15 @@ abstract public class VerityUtils {
return filePath + FSVERITY_SIGNATURE_FILE_EXTENSION;
}
- /** Generates Merkle tree and fs-verity metadata then enables fs-verity. */
- public static void setUpFsverity(@NonNull String filePath, String signaturePath)
- throws IOException, DigestException, NoSuchAlgorithmException {
- final PKCS7 pkcs7 = new PKCS7(Files.readAllBytes(Paths.get(signaturePath)));
- final byte[] expectedMeasurement = pkcs7.getContentInfo().getContentBytes();
- if (DEBUG) {
- Slog.d(TAG, "Enabling fs-verity with signed fs-verity measurement "
- + bytesToString(expectedMeasurement));
- Slog.d(TAG, "PKCS#7 info: " + pkcs7);
- }
-
- final TrackedBufferFactory bufferFactory = new TrackedBufferFactory();
- final byte[] actualMeasurement = generateFsverityMetadata(filePath, signaturePath,
- bufferFactory);
- try (RandomAccessFile raf = new RandomAccessFile(filePath, "rw")) {
- FileChannel ch = raf.getChannel();
- ch.position(roundUpToNextMultiple(ch.size(), COMMON_LINUX_PAGE_SIZE_IN_BYTES));
- ByteBuffer buffer = bufferFactory.getBuffer();
-
- long offset = buffer.position();
- long size = buffer.limit();
- while (offset < size) {
- long s = ch.write(buffer);
- offset += s;
- size -= s;
- }
+ /** Enables fs-verity for the file with a PKCS#7 detached signature file. */
+ public static void setUpFsverity(@NonNull String filePath, @NonNull String signaturePath)
+ throws IOException {
+ if (Files.size(Paths.get(signaturePath)) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
+ throw new SecurityException("Signature file is unexpectedly large: " + signaturePath);
}
-
- if (!Arrays.equals(expectedMeasurement, actualMeasurement)) {
- throw new SecurityException("fs-verity measurement mismatch: "
- + bytesToString(actualMeasurement) + " != "
- + bytesToString(expectedMeasurement));
- }
-
- // This can fail if the public key is not already in .fs-verity kernel keyring.
- int errno = enableFsverityNative(filePath);
+ byte[] pkcs7Signature = Files.readAllBytes(Paths.get(signaturePath));
+ // This will fail if the public key is not already in .fs-verity kernel keyring.
+ int errno = enableFsverityNative(filePath, pkcs7Signature);
if (errno != 0) {
throw new IOException("Failed to enable fs-verity on " + filePath + ": "
+ Os.strerror(errno));
@@ -131,12 +94,19 @@ abstract public class VerityUtils {
return true;
}
+ private static native int enableFsverityNative(@NonNull String filePath,
+ @NonNull byte[] pkcs7Signature);
+ private static native int measureFsverityNative(@NonNull String filePath);
+
/**
* Generates legacy Merkle tree and fs-verity metadata with Signing Block skipped.
*
+ * @deprecated This is only used for previous fs-verity implementation, and should never be used
+ * on new devices.
* @return {@code SetupResult} that contains the result code, and when success, the
* {@code FileDescriptor} to read all the data from.
*/
+ @Deprecated
public static SetupResult generateApkVeritySetupData(@NonNull String apkPath) {
if (DEBUG) {
Slog.d(TAG, "Trying to install legacy apk verity to " + apkPath);
@@ -173,7 +143,10 @@ abstract public class VerityUtils {
/**
* {@see ApkSignatureVerifier#generateApkVerityRootHash(String)}.
+ * @deprecated This is only used for previous fs-verity implementation, and should never be used
+ * on new devices.
*/
+ @Deprecated
public static byte[] generateApkVerityRootHash(@NonNull String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException {
return ApkSignatureVerifier.generateApkVerityRootHash(apkPath);
@@ -181,104 +154,16 @@ abstract public class VerityUtils {
/**
* {@see ApkSignatureVerifier#getVerityRootHash(String)}.
+ * @deprecated This is only used for previous fs-verity implementation, and should never be used
+ * on new devices.
*/
+ @Deprecated
public static byte[] getVerityRootHash(@NonNull String apkPath)
throws IOException, SignatureNotFoundException {
return ApkSignatureVerifier.getVerityRootHash(apkPath);
}
/**
- * Generates fs-verity metadata for {@code filePath} in the buffer created by {@code
- * trackedBufferFactory}. The metadata contains the Merkle tree, fs-verity descriptor and
- * extensions, including a PKCS#7 signature provided in {@code signaturePath}.
- *
- * <p>It is worthy to note that {@code trackedBufferFactory} generates a "tracked" {@code
- * ByteBuffer}. The data will be used outside this method via the factory itself.
- *
- * @return fs-verity signed data (struct fsverity_digest_disk) of {@code filePath}, which
- * includes SHA-256 of fs-verity descriptor and authenticated extensions.
- */
- private static byte[] generateFsverityMetadata(String filePath, String signaturePath,
- @NonNull ByteBufferFactory trackedBufferFactory)
- throws IOException, DigestException, NoSuchAlgorithmException {
- try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
- VerityBuilder.VerityResult result = VerityBuilder.generateFsVerityTree(
- file, trackedBufferFactory);
-
- ByteBuffer buffer = result.verityData;
- buffer.position(result.merkleTreeSize);
-
- final byte[] measurement = generateFsverityDescriptorAndMeasurement(file,
- result.rootHash, signaturePath, buffer);
- buffer.flip();
- return constructFsveritySignedDataNative(measurement);
- }
- }
-
- /**
- * Generates fs-verity descriptor including the extensions to the {@code output} and returns the
- * fs-verity measurement.
- *
- * @return fs-verity measurement, which is a SHA-256 of fs-verity descriptor and authenticated
- * extensions.
- */
- private static byte[] generateFsverityDescriptorAndMeasurement(
- @NonNull RandomAccessFile file, @NonNull byte[] rootHash,
- @NonNull String pkcs7SignaturePath, @NonNull ByteBuffer output)
- throws IOException, NoSuchAlgorithmException, DigestException {
- final short kRootHashExtensionId = 1;
- final short kPkcs7SignatureExtensionId = 3;
- final int origPosition = output.position();
-
- // For generating fs-verity file measurement, which consists of the descriptor and
- // authenticated extensions (but not unauthenticated extensions and the footer).
- MessageDigest md = MessageDigest.getInstance("SHA-256");
-
- // 1. Generate fs-verity descriptor.
- final byte[] desc = constructFsverityDescriptorNative(file.length());
- output.put(desc);
- md.update(desc);
-
- // 2. Generate authenticated extensions.
- final byte[] authExt =
- constructFsverityExtensionNative(kRootHashExtensionId, rootHash.length);
- output.put(authExt);
- output.put(rootHash);
- md.update(authExt);
- md.update(rootHash);
-
- // 3. Generate unauthenticated extensions.
- ByteBuffer header = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
- output.putShort((short) 1); // number of unauthenticated extensions below
- output.position(output.position() + 6);
-
- // Generate PKCS#7 extension. NB: We do not verify agaist trusted certificate (should be
- // done by the caller if needed).
- Path path = Paths.get(pkcs7SignaturePath);
- if (Files.size(path) > MAX_SIGNATURE_FILE_SIZE_BYTES) {
- throw new IllegalArgumentException("Signature size is unexpectedly large: "
- + pkcs7SignaturePath);
- }
- final byte[] pkcs7Signature = Files.readAllBytes(path);
- output.put(constructFsverityExtensionNative(kPkcs7SignatureExtensionId,
- pkcs7Signature.length));
- output.put(pkcs7Signature);
-
- // 4. Generate the footer.
- output.put(constructFsverityFooterNative(output.position() - origPosition));
-
- return md.digest();
- }
-
- private static native int enableFsverityNative(@NonNull String filePath);
- private static native int measureFsverityNative(@NonNull String filePath);
- private static native byte[] constructFsveritySignedDataNative(@NonNull byte[] measurement);
- private static native byte[] constructFsverityDescriptorNative(long fileSize);
- private static native byte[] constructFsverityExtensionNative(short extensionId,
- int extensionDataSize);
- private static native byte[] constructFsverityFooterNative(int offsetToDescriptorHead);
-
- /**
* Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains
* Merkle tree and fsverity headers for the given apk, in the form that can immediately be used
* for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has
@@ -313,6 +198,11 @@ abstract public class VerityUtils {
return HexEncoding.encodeToString(bytes);
}
+ /**
+ * @deprecated This is only used for previous fs-verity implementation, and should never be used
+ * on new devices.
+ */
+ @Deprecated
public static class SetupResult {
/** Result code if verity is set up correctly. */
private static final int RESULT_OK = 1;
@@ -401,30 +291,4 @@ abstract public class VerityUtils {
return mBuffer == null ? -1 : mBuffer.limit();
}
}
-
- /** A {@code ByteBufferFactory} that tracks the {@code ByteBuffer} it creates. */
- private static class TrackedBufferFactory implements ByteBufferFactory {
- private ByteBuffer mBuffer;
-
- @Override
- public ByteBuffer create(int capacity) {
- if (mBuffer != null) {
- throw new IllegalStateException("Multiple instantiation from this factory");
- }
- mBuffer = ByteBuffer.allocate(capacity);
- return mBuffer;
- }
-
- public ByteBuffer getBuffer() {
- return mBuffer;
- }
- }
-
- /** Round up the number to the next multiple of the divisor. */
- private static long roundUpToNextMultiple(long number, long divisor) {
- if (number > (Long.MAX_VALUE - divisor)) {
- throw new IllegalArgumentException("arithmetic overflow");
- }
- return ((number + (divisor - 1)) / divisor) * divisor;
- }
}
diff --git a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
new file mode 100644
index 000000000000..3076284a80ed
--- /dev/null
+++ b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
@@ -0,0 +1,75 @@
+/*
+ * 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.stats;
+
+import android.os.FileUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+final class ProcfsMemoryUtil {
+ private static final String TAG = "ProcfsMemoryUtil";
+
+ /** Path to procfs status file: /proc/pid/status. */
+ private static final String STATUS_FILE_FMT = "/proc/%d/status";
+
+ private static final Pattern RSS_HIGH_WATER_MARK_IN_KILOBYTES =
+ Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
+
+ private ProcfsMemoryUtil() {}
+
+ /**
+ * Reads RSS high-water mark of a process from procfs. Returns value of the VmHWM field in
+ * /proc/PID/status in kilobytes or 0 if not available.
+ */
+ static int readRssHighWaterMarkFromProcfs(int pid) {
+ final String statusPath = String.format(Locale.US, STATUS_FILE_FMT, pid);
+ return parseVmHWMFromStatus(readFile(statusPath));
+ }
+
+ /**
+ * Parses RSS high-water mark out from the contents of the /proc/pid/status file in procfs. The
+ * returned value is in kilobytes.
+ */
+ @VisibleForTesting
+ static int parseVmHWMFromStatus(String contents) {
+ if (contents.isEmpty()) {
+ return 0;
+ }
+ final Matcher matcher = RSS_HIGH_WATER_MARK_IN_KILOBYTES.matcher(contents);
+ try {
+ return matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Failed to parse value", e);
+ return 0;
+ }
+ }
+
+ private static String readFile(String path) {
+ try {
+ final File file = new File(path);
+ return FileUtils.readTextFile(file, 0 /* max */, null /* ellipsis */);
+ } catch (IOException e) {
+ return "";
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index e92abfddac8f..19b80556a779 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -27,9 +27,9 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
-import static com.android.server.am.MemoryStatUtil.readRssHighWaterMarkFromProcfs;
import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
+import static com.android.server.stats.ProcfsMemoryUtil.readRssHighWaterMarkFromProcfs;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 5f98d1d54a24..59f051bc76a6 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1106,14 +1106,10 @@ final class AccessibilityController {
private final Point mTempPoint = new Point();
- private final Rect mTempRect = new Rect();
-
private final Region mTempRegion = new Region();
private final Region mTempRegion1 = new Region();
- private final Context mContext;
-
private final WindowManagerService mService;
private final Handler mHandler;
@@ -1127,7 +1123,6 @@ final class AccessibilityController {
public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
int displayId,
WindowsForAccessibilityCallback callback) {
- mContext = windowManagerService.mContext;
mService = windowManagerService;
mCallback = callback;
mDisplayId = displayId;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 7a667315c6c3..c54ccd4d6844 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2202,7 +2202,9 @@ final class ActivityRecord extends ConfigurationContainer {
stack.removeTimeoutsForActivity(this);
// Clean-up activities are no longer relaunching (e.g. app process died). Notify window
// manager so it can update its bookkeeping.
- mAtmService.mWindowManager.notifyAppRelaunchesCleared(appToken);
+ if (mAppWindowToken != null) {
+ mAppWindowToken.clearRelaunching();
+ }
}
/**
@@ -2961,6 +2963,11 @@ final class ActivityRecord extends ConfigurationContainer {
if (display != null) {
display.handleActivitySizeCompatModeIfNeeded(r);
}
+
+ if (r.mAppWindowToken != null) {
+ r.mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
+ .notifyAppResumedFinished(r.mAppWindowToken);
+ }
}
/**
@@ -4311,7 +4318,9 @@ final class ActivityRecord extends ConfigurationContainer {
"Moving to " + (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + this
+ " callers=" + Debug.getCallers(6));
forceNewConfig = false;
- mStackSupervisor.activityRelaunchingLocked(this);
+ if (mAppWindowToken != null) {
+ mAppWindowToken.startRelaunching();
+ }
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
new MergedConfiguration(mAtmService.getGlobalConfiguration(),
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 50200a7cf7bc..2ab3e01278e9 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -839,11 +839,12 @@ class ActivityStack extends ConfigurationContainer {
// so that the divider matches and remove this logic.
// TODO: This is currently only called when entering split-screen while in another
// task, and from the tests
- // TODO (b/78247419): Check if launcher and overview are same then move home stack
- // instead of recents stack. Then fix the rotation animation from fullscreen to
- // minimized mode
+ // TODO (b/78247419): Fix the rotation animation from fullscreen to minimized mode
+ final boolean isRecentsComponentHome =
+ mService.getRecentTasks().isRecentsComponentHomeActivity(mCurrentUser);
final ActivityStack recentStack = display.getOrCreateStack(
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ isRecentsComponentHome ? ACTIVITY_TYPE_HOME : ACTIVITY_TYPE_RECENTS,
true /* onTop */);
recentStack.moveToFront("setWindowingMode");
// If task moved to docked stack - show recents if needed.
@@ -4940,12 +4941,6 @@ class ActivityStack extends ConfigurationContainer {
}
}
-
- Rect getDefaultPictureInPictureBounds(float aspectRatio) {
- if (getTaskStack() == null) return null;
- return getTaskStack().getPictureInPictureBounds(aspectRatio, null /* currentStackBounds */);
- }
-
void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
boolean fromFullscreen) {
if (!inPinnedWindowingMode()) return;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 1aa1d483f707..d151f86ff810 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2480,19 +2480,17 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
}
void activityRelaunchedLocked(IBinder token) {
- mWindowManager.notifyAppRelaunchingFinished(token);
final ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
+ if (r.mAppWindowToken != null) {
+ r.mAppWindowToken.finishRelaunching();
+ }
if (r.getActivityStack().shouldSleepOrShutDownActivities()) {
r.setSleeping(true, true);
}
}
}
- void activityRelaunchingLocked(ActivityRecord r) {
- mWindowManager.notifyAppRelaunching(r.appToken);
- }
-
void logStackState() {
mActivityMetricsLogger.logWindowState();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b35bd9e4e81a..47be792802a0 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1550,10 +1550,11 @@ class ActivityStarter {
mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
- mService.getPackageManagerInternalLocked().grantEphemeralAccess(
+ mService.getPackageManagerInternalLocked().grantImplicitAccess(
mStartActivity.mUserId, mIntent,
- UserHandle.getAppId(mStartActivity.info.applicationInfo.uid),
- UserHandle.getAppId(mCallingUid));
+ UserHandle.getAppId(mCallingUid),
+ UserHandle.getAppId(mStartActivity.info.applicationInfo.uid)
+ );
if (newTask) {
EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
mStartActivity.getTaskRecord().taskId);
@@ -2341,7 +2342,12 @@ class ActivityStarter {
REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
"reparentingHome");
mMovedToFront = true;
+ } else if (launchStack.topTask() == null) {
+ // The task does not need to be reparented to the launch stack. Remove the
+ // launch stack if there is no activity in it.
+ launchStack.remove();
}
+
mOptions = null;
// We are moving a task to the front, use starting window to hide initial drawn
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2f7acba595c2..468a13d45d55 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -47,7 +47,6 @@ import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
import static android.os.FactoryTest.FACTORY_TEST_HIGH_LEVEL;
import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
import static android.os.FactoryTest.FACTORY_TEST_OFF;
@@ -550,7 +549,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/** The dimensions of the thumbnails in the Recents UI. */
private int mThumbnailWidth;
private int mThumbnailHeight;
- private float mFullscreenThumbnailScale;
/**
* Flag that indicates if multi-window is enabled.
@@ -790,15 +788,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
com.android.internal.R.dimen.thumbnail_width);
mThumbnailHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.thumbnail_height);
-
- if ((globalConfig.uiMode & UI_MODE_TYPE_TELEVISION) == UI_MODE_TYPE_TELEVISION) {
- mFullscreenThumbnailScale = (float) res
- .getInteger(com.android.internal.R.integer.thumbnail_width_tv) /
- (float) globalConfig.screenWidthDp;
- } else {
- mFullscreenThumbnailScale = res.getFraction(
- com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
- }
}
}
@@ -1697,7 +1686,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
ActivityRecord.activityResumedLocked(token);
- mWindowManager.notifyAppResumedFinished(token);
}
Binder.restoreCallingIdentity(origId);
}
@@ -2920,7 +2908,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mAmInternal.enforceCallingPermission(Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
"updateLockTaskPackages()");
}
- synchronized (this) {
+ synchronized (mGlobalLock) {
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":"
+ Arrays.toString(packages));
getLockTaskController().updateLockTaskPackages(userId, packages);
@@ -5365,7 +5353,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final float scaleFactor = Settings.System.getFloatForUser(mContext.getContentResolver(),
FONT_SCALE, 1.0f, userId);
- synchronized (this) {
+ synchronized (mGlobalLock) {
if (getGlobalConfiguration().fontScale == scaleFactor) {
return;
}
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index be8a0bd7ad32..278a9ba641ab 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import android.annotation.ColorInt;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -41,11 +40,6 @@ interface AnimationAdapter {
boolean getShowWallpaper();
/**
- * @return The background color behind the animation.
- */
- @ColorInt int getBackgroundColor();
-
- /**
* Requests to start the animation.
*
* @param animationLeash The surface to run the animation on. See {@link SurfaceAnimator} for an
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 557a609dbc7b..66d52cc9bf5a 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1999,8 +1999,8 @@ public class AppTransition implements Dump {
mNextAppTransitionFutureCallback, null /* finishedCallback */,
mNextAppTransitionScaleUp);
mNextAppTransitionFutureCallback = null;
+ mService.requestTraversal();
}
- mService.requestTraversal();
});
}
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ffd9021989b4..f647fe46f067 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -955,6 +955,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
updateReportedVisibilityLocked();
}
+ // Reset the last saved PiP snap fraction on removal.
+ mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent);
+
mRemovingFromDisplay = false;
}
@@ -1021,7 +1024,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: " + this);
mAppStopped = true;
// Reset the last saved PiP snap fraction on app stop.
- mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
+ mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(mActivityComponent);
destroySurfaces();
// Remove any starting window that was added for this app if they are still around.
removeStartingWindow();
@@ -1705,10 +1708,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
return;
}
- if (prevWinMode != WINDOWING_MODE_UNDEFINED && winMode == WINDOWING_MODE_PINNED) {
- // Entering PiP from fullscreen, reset the snap fraction
- mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
- } else if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED
+ if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED
&& !isHidden()) {
// Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds
// for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
@@ -1726,8 +1726,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
stackBounds = mTmpRect;
pinnedStack.getBounds(stackBounds);
}
- mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(this,
- stackBounds);
+ mDisplayContent.mPinnedStackControllerLocked.saveReentrySnapFraction(
+ mActivityComponent, stackBounds);
}
} else if (shouldStartChangeTransition(prevWinMode, winMode)) {
initializeChangeTransition(mTmpPrevBounds);
@@ -2503,14 +2503,18 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
@Override
public SurfaceControl getAnimationLeashParent() {
- // All normal app transitions take place in an animation layer which is below the pinned
- // stack but may be above the parent stacks of the given animating apps.
// For transitions in the pinned stack (menu activity) we just let them occur as a child
// of the pinned stack.
- if (!inPinnedWindowingMode()) {
- return getAppAnimationLayer();
- } else {
+ // All normal app transitions take place in an animation layer which is below the pinned
+ // stack but may be above the parent stacks of the given animating apps by default. When
+ // a new hierarchical animation is enabled, we just let them occur as a child of the parent
+ // stack, i.e. the hierarchy of the surfaces is unchanged.
+ if (inPinnedWindowingMode()) {
return getStack().getSurfaceControl();
+ } else if (WindowManagerService.sHierarchicalAnimations) {
+ return super.getAnimationLeashParent();
+ } else {
+ return getAppAnimationLayer();
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f2ad56a8fdfa..63ff2ea8069c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -583,27 +583,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
};
- private final Consumer<WindowState> mUpdateWallpaperForAnimator = w -> {
- final WindowStateAnimator winAnimator = w.mWinAnimator;
- if (winAnimator.mSurfaceController == null || !winAnimator.hasSurface()) {
- return;
- }
-
- // If this window is animating, ensure the animation background is set.
- final AnimationAdapter anim = w.mAppToken != null
- ? w.mAppToken.getAnimation()
- : w.getAnimation();
- if (anim != null) {
- final int color = anim.getBackgroundColor();
- if (color != 0) {
- final TaskStack stack = w.getStack();
- if (stack != null) {
- stack.setAnimationBackground(winAnimator, color);
- }
- }
- }
- };
-
private final Consumer<WindowState> mScheduleToastTimeout = w -> {
final int lostFocusUid = mTmpWindow.mOwnerUid;
final Handler handler = mWmService.mH;
@@ -2167,7 +2146,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* so only need to configure display.
*/
void setForcedDensity(int density, int userId) {
- final boolean clear = density == mInitialDisplayDensity;
final boolean updateCurrent = userId == UserHandle.USER_CURRENT;
if (mWmService.mCurrentUserId == userId || updateCurrent) {
mBaseDisplayDensity = density;
@@ -2384,12 +2362,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mDisplayPolicy.switchUser();
}
- private void resetAnimationBackgroundAnimator() {
- for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- mTaskStackContainers.getChildAt(stackNdx).resetAnimationBackgroundAnimator();
- }
- }
-
@Override
void removeIfPossible() {
if (isAnimating()) {
@@ -3423,14 +3395,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
forAllWindows(mUpdateWindowsForAnimator, true /* traverseTopToBottom */);
}
- /**
- * Updates the {@link TaskStack#setAnimationBackground} for all windows.
- */
- void updateBackgroundForAnimator() {
- resetAnimationBackgroundAnimator();
- forAllWindows(mUpdateWallpaperForAnimator, true /* traverseTopToBottom */);
- }
-
boolean isInputMethodClientFocus(int uid, int pid) {
final WindowState imFocus = computeImeTarget(false /* updateImeTarget */);
if (imFocus == null) {
@@ -3884,21 +3848,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- private static final class ScreenshotApplicationState {
- WindowState appWin;
- int maxLayer;
- int minLayer;
- boolean screenshotReady;
-
- void reset(boolean screenshotReady) {
- appWin = null;
- maxLayer = 0;
- minLayer = 0;
- this.screenshotReady = screenshotReady;
- minLayer = (screenshotReady) ? 0 : Integer.MAX_VALUE;
- }
- }
-
/**
* Base class for any direct child window container of {@link #DisplayContent} need to inherit
* from. This is mainly a pass through class that allows {@link #DisplayContent} to have
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 4dbb0092140c..10d48c4d5282 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -335,7 +335,6 @@ public class DisplayPolicy {
private static final Rect sTmpDisplayCutoutSafeExceptMaybeBarsRect = new Rect();
private static final Rect sTmpRect = new Rect();
- private static final Rect sTmpDockedFrame = new Rect();
private static final Rect sTmpNavFrame = new Rect();
private static final Rect sTmpLastParentFrame = new Rect();
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 120ce3eb146e..ae3b5f2f70d3 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -36,7 +36,6 @@ import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.server.wm.DockedStackDividerControllerProto.MINIMIZED_DOCK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
import android.content.Context;
import android.content.res.Configuration;
@@ -141,8 +140,6 @@ public class DockedStackDividerController {
float mLastDividerProgress;
private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
private boolean mImeHideRequested;
- private final Rect mLastDimLayerRect = new Rect();
- private float mLastDimLayerAlpha;
private TaskStack mDimmedStack;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
@@ -656,14 +653,6 @@ public class DockedStackDividerController {
}
/**
- * @return The layer used for dimming the apps when dismissing docked/fullscreen stack. Just
- * above all application surfaces.
- */
- private int getResizeDimLayer() {
- return (mWindow != null) ? mWindow.mLayer - 1 : LAYER_OFFSET_DIM;
- }
-
- /**
* Notifies the docked stack divider controller of a visibility change that happens without
* an animation.
*/
diff --git a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
index 20a13334e564..c5c236416013 100644
--- a/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
+++ b/services/core/java/com/android/server/wm/DragAndDropPermissionsHandler.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.UriGrantsManager;
import android.content.ClipData;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 34253ed6fc8c..842686441465 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -238,11 +238,6 @@ class InsetsSourceProvider {
}
@Override
- public int getBackgroundColor() {
- return 0;
- }
-
- @Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
mCapturedLeash = animationLeash;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index d528ef6ec6a5..2b5eb3ac29fb 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -71,7 +71,6 @@ class KeyguardController {
private boolean mAodShowing;
private boolean mKeyguardGoingAway;
private boolean mDismissalRequested;
- private int[] mSecondaryDisplayIdsShowing;
private int mBeforeUnoccludeTransit;
private int mVisibilityTransactionDepth;
private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
@@ -328,7 +327,7 @@ class KeyguardController {
return;
}
- mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY));
+ mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY));
if (isKeyguardLocked()) {
mService.deferWindowLayout();
try {
@@ -381,7 +380,7 @@ class KeyguardController {
* @return true if Keyguard can be currently dismissed without entering credentials.
*/
boolean canDismissKeyguard() {
- return mWindowManager.isKeyguardTrusted()
+ return mWindowManager.mPolicy.isKeyguardTrustedLw()
|| !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
}
@@ -516,7 +515,8 @@ class KeyguardController {
}
// TODO(b/123372519): isShowingDream can only works on default display.
if (mDisplayId == DEFAULT_DISPLAY) {
- mOccluded |= controller.mWindowManager.isShowingDream();
+ mOccluded |= mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent
+ .getDisplayPolicy().isShowingDreamLw();
}
if (lastOccluded != mOccluded) {
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index 77a024cc2e99..e67cb6fc6963 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -49,11 +49,6 @@ class LocalAnimationAdapter implements AnimationAdapter {
}
@Override
- public int getBackgroundColor() {
- return mSpec.getBackgroundColor();
- }
-
- @Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
mAnimator.startAnimation(mSpec, animationLeash, t,
@@ -100,13 +95,6 @@ class LocalAnimationAdapter implements AnimationAdapter {
}
/**
- * @see AnimationAdapter#getBackgroundColor
- */
- default int getBackgroundColor() {
- return 0;
- }
-
- /**
* @see AnimationAdapter#getStatusBarTransitionsStartTime
*/
default long calculateStatusBarTransitionStartTime() {
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index ef0049b068f4..8e57fec6ba46 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -27,6 +27,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.app.RemoteAction;
+import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Point;
@@ -50,7 +51,6 @@ import com.android.internal.util.Preconditions;
import com.android.server.UiThread;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -74,7 +74,7 @@ class PinnedStackController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM;
- public static final float INVALID_SNAP_FRACTION = -1f;
+ private static final float INVALID_SNAP_FRACTION = -1f;
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final Handler mHandler = UiThread.getHandler();
@@ -106,9 +106,6 @@ class PinnedStackController {
private int mDefaultStackGravity;
private float mDefaultAspectRatio;
private Point mScreenEdgeInsets;
- private int mCurrentMinSize;
- private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
- private WeakReference<AppWindowToken> mLastPipActivity = null;
// The aspect ratio bounds of the PIP.
private float mMinAspectRatio;
@@ -118,7 +115,6 @@ class PinnedStackController {
private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
private final Rect mTmpInsets = new Rect();
private final Rect mTmpRect = new Rect();
- private final Rect mTmpAnimatingBoundsRect = new Rect();
private final Point mTmpDisplaySize = new Point();
@@ -136,16 +132,19 @@ class PinnedStackController {
}
@Override
- public void setMinEdgeSize(int minEdgeSize) {
- mHandler.post(() -> {
- mCurrentMinSize = Math.max(mDefaultMinSize, minEdgeSize);
- });
+ public int getDisplayRotation() {
+ synchronized (mService.mGlobalLock) {
+ return mDisplayInfo.rotation;
+ }
}
@Override
- public int getDisplayRotation() {
+ public void startAnimation(Rect destinationBounds, Rect sourceRectHint,
+ int animationDuration) {
synchronized (mService.mGlobalLock) {
- return mDisplayInfo.rotation;
+ final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
+ pinnedStack.animateResizePinnedStack(destinationBounds,
+ sourceRectHint, animationDuration, true /* fromFullscreen */);
}
}
}
@@ -188,7 +187,6 @@ class PinnedStackController {
final Resources res = mService.mContext.getResources();
mDefaultMinSize = res.getDimensionPixelSize(
com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
- mCurrentMinSize = mDefaultMinSize;
mDefaultAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
final String screenEdgeInsetsDpString = res.getString(
@@ -216,6 +214,7 @@ class PinnedStackController {
listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0);
listener.onListenerRegistered(mCallbacks);
mPinnedStackListener = listener;
+ notifyDisplayInfoChanged(mDisplayInfo);
notifyImeVisibilityChanged(mIsImeShowing, mImeHeight);
notifyShelfVisibilityChanged(mIsShelfShowing, mShelfHeight);
// The movement bounds notification needs to be sent before the minimized state, since
@@ -238,58 +237,34 @@ class PinnedStackController {
}
/**
- * Returns the current bounds (or the default bounds if there are no current bounds) with the
- * specified aspect ratio.
- */
- Rect transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
- boolean useCurrentMinEdgeSize) {
- // Save the snap fraction, calculate the aspect ratio based on screen size
- final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
- getMovementBounds(stackBounds));
-
- final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
- final Size size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
- mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
- 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));
- }
- return stackBounds;
- }
-
- /**
* Saves the current snap fraction for re-entry of the current activity into PiP.
*/
- void saveReentrySnapFraction(final AppWindowToken token, final Rect stackBounds) {
- mReentrySnapFraction = getSnapFraction(stackBounds);
- mLastPipActivity = new WeakReference<>(token);
+ void saveReentrySnapFraction(final ComponentName componentName, final Rect stackBounds) {
+ if (mPinnedStackListener == null) return;
+ try {
+ mPinnedStackListener.onSaveReentrySnapFraction(componentName, stackBounds);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering save reentry fraction event.", e);
+ }
}
/**
* Resets the last saved snap fraction so that the default bounds will be returned.
*/
- void resetReentrySnapFraction(AppWindowToken token) {
- if (mLastPipActivity != null && mLastPipActivity.get() == token) {
- mReentrySnapFraction = INVALID_SNAP_FRACTION;
- mLastPipActivity = null;
+ void resetReentrySnapFraction(ComponentName componentName) {
+ if (mPinnedStackListener == null) return;
+ try {
+ mPinnedStackListener.onResetReentrySnapFraction(componentName);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering reset reentry fraction event.", e);
}
}
/**
- * @return the default bounds to show the PIP when there is no active PIP.
- */
- Rect getDefaultOrLastSavedBounds() {
- return getDefaultBounds(mReentrySnapFraction);
- }
-
- /**
* @return the default bounds to show the PIP, if a {@param snapFraction} is provided, then it
* will apply the default bounds to the provided snap fraction.
*/
- Rect getDefaultBounds(float snapFraction) {
+ private Rect getDefaultBounds(float snapFraction) {
synchronized (mService.mGlobalLock) {
final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
@@ -311,13 +286,18 @@ class PinnedStackController {
}
}
+ private void setDisplayInfo(DisplayInfo displayInfo) {
+ mDisplayInfo.copyFrom(displayInfo);
+ notifyDisplayInfoChanged(mDisplayInfo);
+ }
+
/**
* In the case where the display rotation is changed but there is no stack, we can't depend on
* onTaskStackBoundsChanged() to be called. But we still should update our known display info
* with the new state so that we can update SystemUI.
*/
synchronized void onDisplayInfoChanged(DisplayInfo displayInfo) {
- mDisplayInfo.copyFrom(displayInfo);
+ setDisplayInfo(displayInfo);
notifyMovementBoundsChanged(false /* fromImeAdjustment */, false /* fromShelfAdjustment */);
}
@@ -335,7 +315,7 @@ class PinnedStackController {
} else if (targetBounds.isEmpty()) {
// The stack is null, we are just initializing the stack, so just store the display
// info and ignore
- mDisplayInfo.copyFrom(displayInfo);
+ setDisplayInfo(displayInfo);
outBounds.setEmpty();
return false;
}
@@ -345,7 +325,8 @@ class PinnedStackController {
// Calculate the snap fraction of the current stack along the old movement bounds
final float snapFraction = getSnapFraction(postChangeStackBounds);
- mDisplayInfo.copyFrom(displayInfo);
+
+ setDisplayInfo(displayInfo);
// Calculate the stack bounds in the new orientation to the same same fraction along the
// rotated movement bounds.
@@ -406,8 +387,11 @@ class PinnedStackController {
void setAspectRatio(float aspectRatio) {
if (Float.compare(mAspectRatio, aspectRatio) != 0) {
mAspectRatio = aspectRatio;
+ notifyAspectRatioChanged(aspectRatio);
notifyMovementBoundsChanged(false /* fromImeAdjustment */,
false /* fromShelfAdjustment */);
+ notifyPrepareAnimation(null /* sourceHintRect */, aspectRatio,
+ null /* stackBounds */);
}
}
@@ -429,6 +413,10 @@ class PinnedStackController {
notifyActionsChanged(mActions);
}
+ void prepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
+ notifyPrepareAnimation(sourceRectHint, aspectRatio, stackBounds);
+ }
+
private boolean isSameDimensionAndRotation(@NonNull DisplayInfo display1,
@NonNull DisplayInfo display2) {
Preconditions.checkNotNull(display1);
@@ -461,6 +449,15 @@ class PinnedStackController {
}
}
+ private void notifyAspectRatioChanged(float aspectRatio) {
+ if (mPinnedStackListener == null) return;
+ try {
+ mPinnedStackListener.onAspectRatioChanged(aspectRatio);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering aspect ratio changed event.", e);
+ }
+ }
+
/**
* Notifies listeners that the PIP minimized state has changed.
*/
@@ -497,23 +494,13 @@ class PinnedStackController {
return;
}
try {
- final Rect insetBounds = new Rect();
- getInsetBounds(insetBounds);
- final Rect normalBounds = getDefaultBounds(INVALID_SNAP_FRACTION);
- if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
- transformBoundsToAspectRatio(normalBounds, mAspectRatio,
- false /* useCurrentMinEdgeSize */);
- }
- final Rect animatingBounds = mTmpAnimatingBoundsRect;
+ final Rect animatingBounds = new Rect();
final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
if (pinnedStack != null) {
pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
- } else {
- animatingBounds.set(normalBounds);
}
- mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
- animatingBounds, fromImeAdjustment, fromShelfAdjustment,
- mDisplayInfo.rotation);
+ mPinnedStackListener.onMovementBoundsChanged(animatingBounds,
+ fromImeAdjustment, fromShelfAdjustment);
} catch (RemoteException e) {
Slog.e(TAG_WM, "Error delivering actions changed event.", e);
}
@@ -521,6 +508,30 @@ class PinnedStackController {
}
/**
+ * Notifies listeners that the PIP animation is about to happen.
+ */
+ private void notifyDisplayInfoChanged(DisplayInfo displayInfo) {
+ if (mPinnedStackListener == null) return;
+ try {
+ mPinnedStackListener.onDisplayInfoChanged(displayInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering DisplayInfo changed event.", e);
+ }
+ }
+
+ /**
+ * Notifies listeners that the PIP animation is about to happen.
+ */
+ private void notifyPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
+ if (mPinnedStackListener == null) return;
+ try {
+ mPinnedStackListener.onPrepareAnimation(sourceRectHint, aspectRatio, stackBounds);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering prepare animation event.", e);
+ }
+ }
+
+ /**
* @return the bounds on the screen that the PIP can be visible in.
*/
private void getInsetBounds(Rect outRect) {
@@ -604,7 +615,6 @@ class PinnedStackController {
pw.println(prefix + " mImeHeight=" + mImeHeight);
pw.println(prefix + " mIsShelfShowing=" + mIsShelfShowing);
pw.println(prefix + " mShelfHeight=" + mShelfHeight);
- pw.println(prefix + " mReentrySnapFraction=" + mReentrySnapFraction);
pw.println(prefix + " mIsMinimized=" + mIsMinimized);
pw.println(prefix + " mAspectRatio=" + mAspectRatio);
pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio);
diff --git a/services/core/java/com/android/server/wm/PointerEventDispatcher.java b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
index 8d08aa370b44..6b8144c69079 100644
--- a/services/core/java/com/android/server/wm/PointerEventDispatcher.java
+++ b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
@@ -28,13 +28,11 @@ import com.android.server.UiThread;
import java.util.ArrayList;
public class PointerEventDispatcher extends InputEventReceiver {
- private final InputChannel mInputChannel;
private final ArrayList<PointerEventListener> mListeners = new ArrayList<>();
private PointerEventListener[] mListenersArray = new PointerEventListener[0];
public PointerEventDispatcher(InputChannel inputChannel) {
super(inputChannel, UiThread.getHandler().getLooper());
- mInputChannel = inputChannel;
}
@Override
@@ -94,7 +92,6 @@ public class PointerEventDispatcher extends InputEventReceiver {
@Override
public void dispose() {
super.dispose();
- mInputChannel.dispose();
synchronized (mListeners) {
mListeners.clear();
mListenersArray = null;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 19b5f3160837..795a2ca67ac3 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -93,6 +93,8 @@ public class RecentsAnimationController implements DeathRecipient {
private IRecentsAnimationRunner mRunner;
private final RecentsAnimationCallbacks mCallbacks;
private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
+ private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
+ new ArrayList<>();
private final int mDisplayId;
private final Runnable mFailsafeRunnable = () ->
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
@@ -434,6 +436,13 @@ public class RecentsAnimationController implements DeathRecipient {
mPendingAnimations.remove(taskAdapter);
}
+ @VisibleForTesting
+ void removeWallpaperAnimation(WallpaperAnimationAdapter wallpaperAdapter) {
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "removeWallpaperAnimation()");
+ wallpaperAdapter.getLeashFinishedCallback().onAnimationFinished(wallpaperAdapter);
+ mPendingWallpaperAnimations.remove(wallpaperAdapter);
+ }
+
void startAnimation() {
if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
+ " mCanceled=" + mCanceled);
@@ -442,25 +451,18 @@ public class RecentsAnimationController implements DeathRecipient {
return;
}
try {
- final ArrayList<RemoteAnimationTarget> appAnimations = new ArrayList<>();
- for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
- final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
- final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationApp();
- if (target != null) {
- appAnimations.add(target);
- } else {
- removeAnimation(taskAdapter);
- }
- }
+ // Create the app targets
+ final RemoteAnimationTarget[] appTargets = createAppAnimations();
// Skip the animation if there is nothing to animate
- if (appAnimations.isEmpty()) {
+ if (appTargets.length == 0) {
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows");
return;
}
- final RemoteAnimationTarget[] appTargets = appAnimations.toArray(
- new RemoteAnimationTarget[appAnimations.size()]);
+ // Create the wallpaper targets
+ final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
+
mPendingStart = false;
// Perform layout if it was scheduled before to make sure that we get correct content
@@ -479,7 +481,8 @@ public class RecentsAnimationController implements DeathRecipient {
mService.getStableInsets(mDisplayId, mTmpRect);
contentInsets = mTmpRect;
}
- mRunner.onAnimationStart(mController, appTargets, contentInsets, minimizedHomeBounds);
+ mRunner.onAnimationStart(mController, appTargets, wallpaperTargets, contentInsets,
+ minimizedHomeBounds);
if (DEBUG_RECENTS_ANIMATIONS) {
Slog.d(TAG, "startAnimation(): Notify animation start:");
for (int i = 0; i < mPendingAnimations.size(); i++) {
@@ -495,6 +498,32 @@ public class RecentsAnimationController implements DeathRecipient {
mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis());
}
+ private RemoteAnimationTarget[] createAppAnimations() {
+ final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
+ for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+ final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
+ final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationTarget();
+ if (target != null) {
+ targets.add(target);
+ } else {
+ removeAnimation(taskAdapter);
+ }
+ }
+ return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
+ private RemoteAnimationTarget[] createWallpaperAnimations() {
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "createWallpaperAnimations()");
+ return WallpaperAnimationAdapter.startWallpaperAnimations(mService, 0L, 0L,
+ adapter -> {
+ synchronized (mService.mGlobalLock) {
+ // If the wallpaper animation is canceled, continue with the recents
+ // animation
+ mPendingWallpaperAnimations.remove(adapter);
+ }
+ }, mPendingWallpaperAnimations);
+ }
+
void cancelAnimation(@ReorderMode int reorderMode, String reason) {
cancelAnimation(reorderMode, false /*screenshot */, reason);
}
@@ -619,6 +648,11 @@ public class RecentsAnimationController implements DeathRecipient {
removeAnimation(taskAdapter);
}
+ for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
+ final WallpaperAnimationAdapter wallpaperAdapter = mPendingWallpaperAnimations.get(i);
+ removeWallpaperAnimation(wallpaperAdapter);
+ }
+
// Clear any pending failsafe runnables
mService.mH.removeCallbacks(mFailsafeRunnable);
mDisplayContent.mAppTransition.unregisterListener(mAppTransitionListener);
@@ -747,6 +781,15 @@ public class RecentsAnimationController implements DeathRecipient {
return false;
}
+ boolean isAnimatingWallpaper(WallpaperWindowToken token) {
+ for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
+ if (token == mPendingWallpaperAnimations.get(i).getToken()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean isAnimatingApp(AppWindowToken appToken) {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final Task task = mPendingAnimations.get(i).mTask;
@@ -784,7 +827,7 @@ public class RecentsAnimationController implements DeathRecipient {
mBounds.set(container.getDisplayedBounds());
}
- RemoteAnimationTarget createRemoteAnimationApp() {
+ RemoteAnimationTarget createRemoteAnimationTarget() {
final AppWindowToken topApp = mTask.getTopVisibleAppToken();
final WindowState mainWindow = topApp != null
? topApp.findMainWindow()
@@ -811,11 +854,6 @@ public class RecentsAnimationController implements DeathRecipient {
}
@Override
- public int getBackgroundColor() {
- return 0;
- }
-
- @Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
// Restore z-layering, position and stack crop until client has a chance to modify it.
@@ -830,6 +868,7 @@ public class RecentsAnimationController implements DeathRecipient {
@Override
public void onAnimationCancelled(SurfaceControl animationLeash) {
+ // Cancel the animation immediately if any single task animator is canceled
cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled");
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 7448e007f2c5..87bda4a545e1 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -58,6 +58,8 @@ class RemoteAnimationController implements DeathRecipient {
private final WindowManagerService mService;
private final RemoteAnimationAdapter mRemoteAnimationAdapter;
private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>();
+ private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
+ new ArrayList<>();
private final Rect mTmpRect = new Rect();
private final Handler mHandler;
private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
@@ -110,16 +112,21 @@ class RemoteAnimationController implements DeathRecipient {
(long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
mFinishedCallback = new FinishedCallback(this);
- final RemoteAnimationTarget[] animations = createAnimations();
- if (animations.length == 0) {
+ // Create the app targets
+ final RemoteAnimationTarget[] appTargets = createAppAnimations();
+ if (appTargets.length == 0) {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): No apps to animate");
onAnimationFinished();
return;
}
+
+ // Create the remote wallpaper animation targets (if any)
+ final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
linkToDeathOfRunner();
- mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback);
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
+ mFinishedCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
@@ -155,8 +162,8 @@ class RemoteAnimationController implements DeathRecipient {
Slog.i(TAG, sw.toString());
}
- private RemoteAnimationTarget[] createAnimations() {
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimations()");
+ private RemoteAnimationTarget[] createAppAnimations() {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAppAnimations()");
final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final RemoteAnimationRecord wrappers = mPendingAnimations.get(i);
@@ -186,6 +193,19 @@ class RemoteAnimationController implements DeathRecipient {
return targets.toArray(new RemoteAnimationTarget[targets.size()]);
}
+ private RemoteAnimationTarget[] createWallpaperAnimations() {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createWallpaperAnimations()");
+ return WallpaperAnimationAdapter.startWallpaperAnimations(mService,
+ mRemoteAnimationAdapter.getDuration(),
+ mRemoteAnimationAdapter.getStatusBarTransitionDelay(),
+ adapter -> {
+ synchronized (mService.mGlobalLock) {
+ // If the wallpaper animation is canceled, continue with the app animation
+ mPendingWallpaperAnimations.remove(adapter);
+ }
+ }, mPendingWallpaperAnimations);
+ }
+
private void onAnimationFinished() {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): mPendingAnimations="
+ mPendingAnimations.size());
@@ -207,7 +227,15 @@ class RemoteAnimationController implements DeathRecipient {
adapters.mThumbnailAdapter.mCapturedFinishCallback
.onAnimationFinished(adapters.mThumbnailAdapter);
}
- if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\t" + adapters.mAppWindowToken);
+ mPendingAnimations.remove(i);
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tapp=" + adapters.mAppWindowToken);
+ }
+
+ for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
+ final WallpaperAnimationAdapter adapter = mPendingWallpaperAnimations.get(i);
+ adapter.getLeashFinishedCallback().onAnimationFinished(adapter);
+ mPendingWallpaperAnimations.remove(i);
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\twallpaper=" + adapter.getToken());
}
} catch (Exception e) {
Slog.e(TAG, "Failed to finish remote animation", e);
@@ -390,11 +418,6 @@ class RemoteAnimationController implements DeathRecipient {
}
@Override
- public int getBackgroundColor() {
- return 0;
- }
-
- @Override
public void startAnimation(SurfaceControl animationLeash, Transaction t,
OnAnimationFinishedCallback finishCallback) {
if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
@@ -424,10 +447,7 @@ class RemoteAnimationController implements DeathRecipient {
mPendingAnimations.remove(mRecord);
}
if (mPendingAnimations.isEmpty()) {
- mHandler.removeCallbacks(mTimeoutRunnable);
- releaseFinishedCallback();
- invokeAnimationCancelled();
- setRunningRemoteAnimation(false);
+ cancelAnimation("allAppAnimationsCanceled");
}
}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 4b2d4ce3d799..734f2248d131 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -193,6 +193,9 @@ class RootActivityContainer extends ConfigurationContainer
/** Set when a power hint has started, but not ended. */
private boolean mPowerHintSent;
+ /** Used to keep ensureActivitiesVisible() from being entered recursively. */
+ private boolean mInEnsureActivitiesVisible = false;
+
// The default minimal size that will be used if the activity doesn't specify its minimal size.
// It will be calculated when the default display gets added.
int mDefaultMinSizeOfResizeableTaskDp = -1;
@@ -805,8 +808,14 @@ class RootActivityContainer extends ConfigurationContainer
*/
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
- mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
+ if (mInEnsureActivitiesVisible) {
+ // Don't do recursive work.
+ return;
+ }
+ mInEnsureActivitiesVisible = true;
+
try {
+ mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
// First the front stacks. In case any are not fullscreen and are in front of home.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
@@ -815,6 +824,7 @@ class RootActivityContainer extends ConfigurationContainer
}
} finally {
mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate();
+ mInEnsureActivitiesVisible = false;
}
}
@@ -959,10 +969,6 @@ class RootActivityContainer extends ConfigurationContainer
// Need to make sure the pinned stack exist so we can resize it below...
stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
- // Calculate the target bounds here before the task is reparented back into pinned windowing
- // mode (which will reset the saved bounds)
- final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
-
try {
final TaskRecord task = r.getTaskRecord();
// Resize the pinned stack to match the current size of the task the activity we are
@@ -1001,9 +1007,14 @@ class RootActivityContainer extends ConfigurationContainer
mService.continueWindowLayout();
}
- stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
- true /* fromFullscreen */);
+ // Notify the pinned stack controller to prepare the PiP animation, expect callback
+ // delivered from SystemUI to WM to start the animation.
+ final PinnedStackController pinnedStackController =
+ display.mDisplayContent.getPinnedStackController();
+ pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio,
+ null /* stackBounds */);
+ // TODO: revisit the following statement after the animation is moved from WM to SysUI.
// Update the visibility of all activities after the they have been reparented to the new
// stack. This MUST run after the animation above is scheduled to ensure that the windows
// drawn signal is scheduled after the bounds animation start call on the bounds animator
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d0b6fc890ca0..4365d0325545 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -472,8 +472,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
- final DisplayContent dc = mChildren.get(i);
- final int pendingChanges = animator.getPendingLayoutChanges(dc.getDisplayId());
+ final int pendingChanges = mChildren.get(i).pendingLayoutChanges;
if ((pendingChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
animator.mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index cd211a28a908..ba728ba18d57 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -54,6 +54,7 @@ class SurfaceAnimator {
final Animatable mAnimatable;
private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
@VisibleForTesting
+ @Nullable
final Runnable mAnimationFinishedCallback;
private boolean mAnimationStartDelayed;
@@ -262,7 +263,7 @@ class SurfaceAnimator {
if (!mAnimationStartDelayed && forwardCancel) {
animation.onAnimationCancelled(leash);
}
- if (!restarting) {
+ if (!restarting && mAnimationFinishedCallback != null) {
mAnimationFinishedCallback.run();
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3a2eb57f1d80..85ba80602bdb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -85,8 +85,6 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
private Rect mTmpRect = new Rect();
// For handling display rotations.
private Rect mTmpRect2 = new Rect();
- // For retrieving dim bounds
- private Rect mTmpRect3 = new Rect();
// Resize mode of the task. See {@link ActivityInfo#resizeMode}
private int mResizeMode;
@@ -613,6 +611,9 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta
@Override
public SurfaceControl getAnimationLeashParent() {
+ if (!WindowManagerService.sHierarchicalAnimations) {
+ return super.getAnimationLeashParent();
+ }
// Currently, only the recents animation will create animation leashes for tasks. In this
// case, reparent the task to the home animation layer while it is being animated to allow
// the home activity to reorder the app windows relative to its own.
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 8b0b6ce8ce32..42866f97b4b8 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -64,10 +64,6 @@ class TaskPositioner implements IBinder.DeathRecipient {
private static Factory sFactory;
- // The margin the pointer position has to be within the side of the screen to be
- // considered at the side of the screen.
- static final int SIDE_MARGIN_DIP = 100;
-
@IntDef(flag = true,
value = {
CTRL_NONE,
@@ -101,7 +97,6 @@ class TaskPositioner implements IBinder.DeathRecipient {
private DisplayContent mDisplayContent;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private Rect mTmpRect = new Rect();
- private int mSideMargin;
private int mMinVisibleWidth;
private int mMinVisibleHeight;
@@ -309,7 +304,6 @@ class TaskPositioner implements IBinder.DeathRecipient {
// Notify InputMonitor to take mDragWindowHandle.
mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
- mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
display.getRealSize(mMaxVisibleSize);
@@ -488,12 +482,6 @@ class TaskPositioner implements IBinder.DeathRecipient {
int right = mWindowOriginalBounds.right;
int bottom = mWindowOriginalBounds.bottom;
- // The aspect which we have to respect. Note that if the orientation does not need to be
- // preserved the aspect will be calculated as 1.0 which neutralizes the following
- // computations.
- final float minAspect = !mPreserveOrientation
- ? 1.0f
- : (mStartOrientationWasLandscape ? MIN_ASPECT : (1.0f / MIN_ASPECT));
// Calculate the resulting width and height of the drag operation.
int width = right - left;
int height = bottom - top;
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 4b3691c88a06..0ea108e30280 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1085,8 +1085,8 @@ class TaskRecord extends ConfigurationContainer {
clearRootProcess();
- // TODO: Use window container controller once tasks are better synced between AM and WM
- mService.mWindowManager.notifyTaskRemovedFromRecents(taskId, userId);
+ mService.mWindowManager.mTaskSnapshotController.notifyTaskRemovedFromRecents(
+ taskId, userId);
}
void setTaskToAffiliateWith(TaskRecord taskToAffiliateWith) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 239bd004705f..10d8328a7535 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -45,7 +45,6 @@ import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
import static com.android.server.wm.StackProto.ADJUST_IME_AMOUNT;
import static com.android.server.wm.StackProto.ANIMATING_BOUNDS;
-import static com.android.server.wm.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
import static com.android.server.wm.StackProto.BOUNDS;
import static com.android.server.wm.StackProto.DEFER_REMOVAL;
import static com.android.server.wm.StackProto.FILLS_PARENT;
@@ -111,12 +110,6 @@ public class TaskStack extends WindowContainer<Task> implements
*/
private final Rect mFullyAdjustedImeBounds = new Rect();
- private SurfaceControl mAnimationBackgroundSurface;
- private boolean mAnimationBackgroundSurfaceIsShown = false;
-
- /** The particular window with an Animation with non-zero background color. */
- private WindowStateAnimator mAnimationBackgroundAnimator;
-
/** Application tokens that are exiting, but still on screen for animations. */
final AppTokenList mExitingAppTokens = new AppTokenList();
final AppTokenList mTmpAppTokens = new AppTokenList();
@@ -230,39 +223,6 @@ public class TaskStack extends WindowContainer<Task> implements
}
}
- private void updateAnimationBackgroundBounds() {
- if (mAnimationBackgroundSurface == null) {
- return;
- }
- getRawBounds(mTmpRect);
- final Rect stackBounds = getBounds();
- getPendingTransaction()
- .setWindowCrop(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
- .setPosition(mAnimationBackgroundSurface, mTmpRect.left - stackBounds.left,
- mTmpRect.top - stackBounds.top);
- scheduleAnimation();
- }
-
- private void hideAnimationSurface() {
- if (mAnimationBackgroundSurface == null) {
- return;
- }
- getPendingTransaction().hide(mAnimationBackgroundSurface);
- mAnimationBackgroundSurfaceIsShown = false;
- scheduleAnimation();
- }
-
- private void showAnimationSurface(float alpha) {
- if (mAnimationBackgroundSurface == null) {
- return;
- }
- getPendingTransaction().setLayer(mAnimationBackgroundSurface, Integer.MIN_VALUE)
- .setAlpha(mAnimationBackgroundSurface, alpha)
- .show(mAnimationBackgroundSurface);
- mAnimationBackgroundSurfaceIsShown = true;
- scheduleAnimation();
- }
-
@Override
public int setBounds(Rect bounds) {
return setBounds(getRequestedOverrideBounds(), bounds);
@@ -275,10 +235,6 @@ public class TaskStack extends WindowContainer<Task> implements
final int result = super.setBounds(bounds);
- if (getParent() != null) {
- updateAnimationBackgroundBounds();
- }
-
updateAdjustedBounds();
updateSurfaceBounds();
@@ -738,7 +694,6 @@ public class TaskStack extends WindowContainer<Task> implements
// surface position.
updateSurfaceSize(getPendingTransaction());
final int windowingMode = getWindowingMode();
- final boolean isAlwaysOnTop = isAlwaysOnTop();
if (mDisplayContent == null) {
return;
@@ -822,11 +777,6 @@ public class TaskStack extends WindowContainer<Task> implements
super.onDisplayChanged(dc);
updateSurfaceBounds();
- if (mAnimationBackgroundSurface == null) {
- mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer()
- .setName("animation background stackId=" + mStackId)
- .build();
- }
}
/**
@@ -1008,27 +958,10 @@ public class TaskStack extends WindowContainer<Task> implements
EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
- if (mAnimationBackgroundSurface != null) {
- mWmService.mTransactionFactory.get().remove(mAnimationBackgroundSurface).apply();
- mAnimationBackgroundSurface = null;
- }
-
mDisplayContent = null;
mWmService.mWindowPlacerLocked.requestTraversal();
}
- void resetAnimationBackgroundAnimator() {
- mAnimationBackgroundAnimator = null;
- hideAnimationSurface();
- }
-
- void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
- if (mAnimationBackgroundAnimator == null) {
- mAnimationBackgroundAnimator = winAnimator;
- showAnimationSurface(((color >> 24) & 0xff) / 255f);
- }
- }
-
// TODO: Should each user have there own stacks?
@Override
void switchUser() {
@@ -1365,7 +1298,6 @@ public class TaskStack extends WindowContainer<Task> implements
}
proto.write(FILLS_PARENT, matchParentBounds());
getRawBounds().writeToProto(proto, BOUNDS);
- proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown);
proto.write(DEFER_REMOVAL, mDeferRemoval);
proto.write(MINIMIZE_AMOUNT, mMinimizeAmount);
proto.write(ADJUSTED_FOR_IME, mAdjustedForIme);
@@ -1395,9 +1327,6 @@ public class TaskStack extends WindowContainer<Task> implements
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
mChildren.get(taskNdx).dump(pw, prefix + " ", dumpAll);
}
- if (mAnimationBackgroundSurfaceIsShown) {
- pw.println(prefix + "mWindowAnimationBackgroundSurface is shown");
- }
if (!mExitingAppTokens.isEmpty()) {
pw.println();
pw.println(" Exiting application tokens:");
@@ -1661,40 +1590,6 @@ public class TaskStack extends WindowContainer<Task> implements
}
/**
- * @return the current stack bounds transformed to the given {@param aspectRatio}. If
- * the default bounds is {@code null}, then the {@param aspectRatio} is applied to the
- * default bounds.
- */
- Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
- if (!mWmService.mAtmService.mSupportsPictureInPicture) {
- return null;
- }
-
- final DisplayContent displayContent = getDisplayContent();
- if (displayContent == null) {
- return null;
- }
-
- if (!inPinnedWindowingMode()) {
- return null;
- }
-
- final PinnedStackController pinnedStackController =
- displayContent.getPinnedStackController();
- if (stackBounds == null) {
- // Calculate the aspect ratio bounds from the default bounds
- stackBounds = pinnedStackController.getDefaultOrLastSavedBounds();
- }
-
- if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) {
- return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio,
- true /* useCurrentMinEdgeSize */);
- } else {
- return stackBounds;
- }
- }
-
- /**
* Animates the pinned stack.
*/
void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
@@ -1771,6 +1666,11 @@ public class TaskStack extends WindowContainer<Task> implements
return;
}
+ final DisplayContent displayContent = getDisplayContent();
+ if (displayContent == null) {
+ return;
+ }
+
if (!inPinnedWindowingMode()) {
return;
}
@@ -1781,13 +1681,10 @@ public class TaskStack extends WindowContainer<Task> implements
if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) {
return;
}
- getAnimationOrCurrentBounds(mTmpFromBounds);
- mTmpToBounds.set(mTmpFromBounds);
- getPictureInPictureBounds(aspectRatio, mTmpToBounds);
- if (!mTmpToBounds.equals(mTmpFromBounds)) {
- animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */,
- -1 /* duration */, false /* fromFullscreen */);
- }
+
+ // Notify the pinned stack controller about aspect ratio change.
+ // This would result a callback delivered from SystemUI to WM to start animation,
+ // if the bounds are ought to be altered due to aspect ratio change.
pinnedStackController.setAspectRatio(
pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
? aspectRatio : -1f);
diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
new file mode 100644
index 000000000000..895350b43eeb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wm;
+
+import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_REMOTE_ANIMATIONS;
+
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * An animation adapter for wallpaper windows.
+ */
+class WallpaperAnimationAdapter implements AnimationAdapter {
+ private static final String TAG = "WallpaperAnimationAdapter";
+
+ private final WallpaperWindowToken mWallpaperToken;
+ private SurfaceControl mCapturedLeash;
+ private SurfaceAnimator.OnAnimationFinishedCallback mCapturedLeashFinishCallback;
+
+ private long mDurationHint;
+ private long mStatusBarTransitionDelay;
+
+ private Consumer<WallpaperAnimationAdapter> mAnimationCanceledRunnable;
+ private RemoteAnimationTarget mTarget;
+
+ WallpaperAnimationAdapter(WallpaperWindowToken wallpaperToken,
+ long durationHint, long statusBarTransitionDelay,
+ Consumer<WallpaperAnimationAdapter> animationCanceledRunnable) {
+ mWallpaperToken = wallpaperToken;
+ mDurationHint = durationHint;
+ mStatusBarTransitionDelay = statusBarTransitionDelay;
+ mAnimationCanceledRunnable = animationCanceledRunnable;
+ }
+
+ /**
+ * Creates and starts remote animations for all the visible wallpaper windows.
+ *
+ * @return RemoteAnimationTarget[] targets for all the visible wallpaper windows
+ */
+ public static RemoteAnimationTarget[] startWallpaperAnimations(WindowManagerService service,
+ long durationHint, long statusBarTransitionDelay,
+ Consumer<WallpaperAnimationAdapter> animationCanceledRunnable,
+ ArrayList<WallpaperAnimationAdapter> adaptersOut) {
+ final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
+ service.mRoot.forAllWallpaperWindows(wallpaperWindow -> {
+ if (!wallpaperWindow.getDisplayContent().mWallpaperController.isWallpaperVisible()) {
+ if (DEBUG_REMOTE_ANIMATIONS || DEBUG_RECENTS_ANIMATIONS) {
+ Slog.d(TAG, "\tNot visible=" + wallpaperWindow);
+ }
+ return;
+ }
+
+ if (DEBUG_REMOTE_ANIMATIONS || DEBUG_RECENTS_ANIMATIONS) {
+ Slog.d(TAG, "\tvisible=" + wallpaperWindow);
+ }
+ final WallpaperAnimationAdapter wallpaperAdapter = new WallpaperAnimationAdapter(
+ wallpaperWindow, durationHint, statusBarTransitionDelay,
+ animationCanceledRunnable);
+ wallpaperWindow.startAnimation(wallpaperWindow.getPendingTransaction(),
+ wallpaperAdapter, false /* hidden */);
+ targets.add(wallpaperAdapter.createRemoteAnimationTarget());
+ adaptersOut.add(wallpaperAdapter);
+ });
+ return targets.toArray(new RemoteAnimationTarget[targets.size()]);
+ }
+
+ /**
+ * Create a remote animation target for this animation adapter.
+ */
+ RemoteAnimationTarget createRemoteAnimationTarget() {
+ mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false, null, null,
+ mWallpaperToken.getPrefixOrderIndex(), new Point(), null,
+ mWallpaperToken.getWindowConfiguration(), true, null, null);
+ return mTarget;
+ }
+
+ /**
+ * @return the leash for this animation (only valid after the wallpaper window surface animation
+ * has started).
+ */
+ SurfaceControl getLeash() {
+ return mCapturedLeash;
+ }
+
+ /**
+ * @return the callback to call to clean up when the animation has finished.
+ */
+ SurfaceAnimator.OnAnimationFinishedCallback getLeashFinishedCallback() {
+ return mCapturedLeashFinishCallback;
+ }
+
+ /**
+ * @return the wallpaper window
+ */
+ WallpaperWindowToken getToken() {
+ return mWallpaperToken;
+ }
+
+ @Override
+ public boolean getShowWallpaper() {
+ // Not used
+ return false;
+ }
+
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
+
+ // Restore z-layering until client has a chance to modify it.
+ t.setLayer(animationLeash, mWallpaperToken.getPrefixOrderIndex());
+ mCapturedLeash = animationLeash;
+ mCapturedLeashFinishCallback = finishCallback;
+ }
+
+ @Override
+ public void onAnimationCancelled(SurfaceControl animationLeash) {
+ if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationCancelled");
+ mAnimationCanceledRunnable.accept(this);
+ }
+
+ @Override
+ public long getDurationHint() {
+ return mDurationHint;
+ }
+
+ @Override
+ public long getStatusBarTransitionsStartTime() {
+ return SystemClock.uptimeMillis() + mStatusBarTransitionDelay;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print("token=");
+ pw.println(mWallpaperToken);
+ if (mTarget != null) {
+ pw.print(prefix);
+ pw.println("Target:");
+ mTarget.dump(pw, prefix + " ");
+ } else {
+ pw.print(prefix);
+ pw.println("Target: null");
+ }
+ }
+
+ @Override
+ public void writeToProto(ProtoOutputStream proto) {
+ final long token = proto.start(REMOTE);
+ if (mTarget != null) {
+ mTarget.writeToProto(proto, TARGET);
+ }
+ proto.end(token);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 614727263469..13902eedbfba 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -110,7 +110,6 @@ class WallpaperController {
private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult();
private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
- final WindowAnimator winAnimator = mService.mAnimator;
if ((w.mAttrs.type == TYPE_WALLPAPER)) {
if (mFindResults.topWallpaper == null || mFindResults.resetTopWallpaper) {
mFindResults.setTopWallpaper(w);
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index e15b783b5606..528cece9a78b 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -29,6 +30,8 @@ import android.util.Slog;
import android.view.DisplayInfo;
import android.view.animation.Animation;
+import java.util.function.Consumer;
+
/**
* A token that represents a set of wallpaper windows.
*/
@@ -153,6 +156,11 @@ class WallpaperWindowToken extends WindowToken {
}
@Override
+ void forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback) {
+ callback.accept(this);
+ }
+
+ @Override
public String toString() {
if (stringName == null) {
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index d8ebd84b3e73..7c183a8bf739 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -19,7 +19,6 @@ package com.android.server.wm;
import static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION;
import static com.android.server.wm.AnimationSpecProto.WINDOW;
import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
-import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
import android.graphics.Point;
@@ -81,11 +80,6 @@ public class WindowAnimationSpec implements AnimationSpec {
}
@Override
- public int getBackgroundColor() {
- return mAnimation.getBackgroundColor();
- }
-
- @Override
public long getDuration() {
return mAnimation.computeDurationHint();
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 4fce46b9dfaf..c7916e829349 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -180,7 +180,6 @@ public class WindowAnimator {
// Update animations of all applications, including those
// associated with exiting/removed apps
dc.updateWindowsForAnimator();
- dc.updateBackgroundForAnimator();
dc.prepareSurfaces();
}
@@ -306,24 +305,6 @@ public class WindowAnimator {
}
}
- int getPendingLayoutChanges(final int displayId) {
- if (displayId < 0) {
- return 0;
- }
- final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
- return (displayContent != null) ? displayContent.pendingLayoutChanges : 0;
- }
-
- void setPendingLayoutChanges(final int displayId, final int changes) {
- if (displayId < 0) {
- return;
- }
- final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
- if (displayContent != null) {
- displayContent.pendingLayoutChanges |= changes;
- }
- }
-
private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
if (displayId < 0) {
return null;
diff --git a/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java b/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java
index 775d5b2fb79a..d5d4e085b7d2 100644
--- a/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowChangeAnimationSpec.java
@@ -70,11 +70,6 @@ public class WindowChangeAnimationSpec implements AnimationSpec {
}
@Override
- public int getBackgroundColor() {
- return 0;
- }
-
- @Override
public long getDuration() {
return mAnimation.getDuration();
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index e280a663b7f5..586375f9d714 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -895,6 +895,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
+ void forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ mChildren.get(i).forAllWallpaperWindows(callback);
+ }
+ }
+
/**
* For all tasks at or below this container call the callback.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 607a013abc51..14214b4be7d1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -313,11 +313,6 @@ public class WindowManagerService extends IWindowManager.Stub
static final int WINDOW_LAYER_MULTIPLIER = 5;
/**
- * Dim surface layer is immediately below target window.
- */
- static final int LAYER_OFFSET_DIM = 1;
-
- /**
* Animation thumbnail is as far as possible below the window above
* the thumbnail (or in other words as far as possible above the window
* below it).
@@ -367,14 +362,28 @@ public class WindowManagerService extends IWindowManager.Stub
private static final String DENSITY_OVERRIDE = "ro.config.density_override";
private static final String SIZE_OVERRIDE = "ro.config.size_override";
- private static final int MAX_SCREENSHOT_RETRIES = 3;
-
private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
// Used to indicate that if there is already a transition set, it should be preserved when
// trying to apply a new one.
private static final boolean ALWAYS_KEEP_CURRENT = true;
+ /**
+ * If set, new app transition framework which supports setting animation on any element
+ * in a surface is used.
+ * <p>
+ * Only set this to non-zero once the new app transition framework is productionalized.
+ * </p>
+ */
+ private static final String HIERARCHICAL_ANIMATIONS_PROPERTY =
+ "persist.wm.hierarchical_animations";
+
+ /**
+ * @see #HIERARCHICAL_ANIMATIONS_PROPERTY
+ */
+ static boolean sHierarchicalAnimations =
+ SystemProperties.getBoolean(HIERARCHICAL_ANIMATIONS_PROPERTY, false);
+
// Enums for animation scale update types.
@Retention(RetentionPolicy.SOURCE)
@IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
@@ -2048,13 +2057,11 @@ public class WindowManagerService extends IWindowManager.Stub
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
long origId = Binder.clearCallingIdentity();
- final int displayId;
synchronized (mGlobalLock) {
final WindowState win = windowForClientLocked(session, client, false);
if (win == null) {
return 0;
}
- displayId = win.getDisplayId();
final DisplayContent displayContent = win.getDisplayContent();
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
@@ -2632,16 +2639,14 @@ public class WindowManagerService extends IWindowManager.Stub
getDefaultDisplayContentLocked().executeAppTransition();
}
- public void initializeRecentsAnimation(int targetActivityType,
+ void initializeRecentsAnimation(int targetActivityType,
IRecentsAnimationRunner recentsAnimationRunner,
RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId,
SparseBooleanArray recentTaskIds) {
- synchronized (mGlobalLock) {
- mRecentsAnimationController = new RecentsAnimationController(this,
- recentsAnimationRunner, callbacks, displayId);
- mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
- mRecentsAnimationController.initialize(targetActivityType, recentTaskIds);
- }
+ mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner,
+ callbacks, displayId);
+ mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
+ mRecentsAnimationController.initialize(targetActivityType, recentTaskIds);
}
@VisibleForTesting
@@ -2787,12 +2792,6 @@ public class WindowManagerService extends IWindowManager.Stub
mAtmInternal.notifyKeyguardFlagsChanged(callback, displayId);
}
- public boolean isKeyguardTrusted() {
- synchronized (mGlobalLock) {
- return mPolicy.isKeyguardTrustedLw();
- }
- }
-
public void setKeyguardGoingAway(boolean keyguardGoingAway) {
synchronized (mGlobalLock) {
mKeyguardGoingAway = keyguardGoingAway;
@@ -2938,13 +2937,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public boolean isShowingDream() {
- synchronized (mGlobalLock) {
- // TODO(b/123372519): Fix this when dream can be shown on non-default display.
- return getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw();
- }
- }
-
@Override
public void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message) {
if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) {
@@ -2955,12 +2947,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public void onKeyguardOccludedChanged(boolean occluded) {
- synchronized (mGlobalLock) {
- mPolicy.onKeyguardOccludedChangedLw(occluded);
- }
- }
-
@Override
public void setSwitchingUser(boolean switching) {
if (!checkCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -5331,9 +5317,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
void requestTraversal() {
- synchronized (mGlobalLock) {
- mWindowPlacerLocked.requestTraversal();
- }
+ mWindowPlacerLocked.requestTraversal();
}
/** Note that Locked in this case is on mLayoutToAnim */
@@ -5801,55 +5785,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public void notifyAppRelaunching(IBinder token) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
- if (appWindow != null) {
- appWindow.startRelaunching();
- }
- }
- }
-
- public void notifyAppRelaunchingFinished(IBinder token) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
- if (appWindow != null) {
- appWindow.finishRelaunching();
- }
- }
- }
-
- public void notifyAppRelaunchesCleared(IBinder token) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
- if (appWindow != null) {
- appWindow.clearRelaunching();
- }
- }
- }
-
- public void notifyAppResumedFinished(IBinder token) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindow = mRoot.getAppWindowToken(token);
- if (appWindow != null) {
- appWindow.getDisplayContent().mUnknownAppVisibilityController
- .notifyAppResumedFinished(appWindow);
- }
- }
- }
-
- /**
- * Called when a task has been removed from the recent tasks list.
- * <p>
- * Note: This doesn't go through {@link TaskWindowContainerController} yet as the window
- * container may not exist when this happens.
- */
- public void notifyTaskRemovedFromRecents(int taskId, int userId) {
- synchronized (mGlobalLock) {
- mTaskSnapshotController.notifyTaskRemovedFromRecents(taskId, userId);
- }
- }
-
private void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) {
pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
mPolicy.dump(" ", pw, args);
@@ -6359,16 +6294,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public void onDisplayChanged(int displayId) {
- synchronized (mGlobalLock) {
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- if (displayContent != null) {
- displayContent.updateDisplayInfo();
- }
- mWindowPlacerLocked.requestTraversal();
- }
- }
-
@Override
public Object getWindowManagerLock() {
return mGlobalLock;
@@ -6379,21 +6304,18 @@ public class WindowManagerService extends IWindowManager.Stub
* a window.
* @param token Application token for which the activity will be relaunched.
*/
- public void setWillReplaceWindow(IBinder token, boolean animate) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null) {
- Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token "
- + token);
- return;
- }
- if (!appWindowToken.hasContentToDisplay()) {
- Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
- + token);
- return;
- }
- appWindowToken.setWillReplaceWindows(animate);
+ void setWillReplaceWindow(IBinder token, boolean animate) {
+ final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
+ if (appWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + token);
+ return;
+ }
+ if (!appWindowToken.hasContentToDisplay()) {
+ Slog.w(TAG_WM, "Attempted to set replacing window on app token with no content"
+ + token);
+ return;
}
+ appWindowToken.setWillReplaceWindows(animate);
}
/**
@@ -6441,19 +6363,17 @@ public class WindowManagerService extends IWindowManager.Stub
* @param token Application token for the activity whose window might be replaced.
* @param replacing Whether the window is being replaced or not.
*/
- public void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) {
- synchronized (mGlobalLock) {
- final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
- if (appWindowToken == null) {
- Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token "
- + token);
- return;
- }
- if (replacing) {
- scheduleWindowReplacementTimeouts(appWindowToken);
- } else {
- appWindowToken.clearWillReplaceWindows();
- }
+ void scheduleClearWillReplaceWindows(IBinder token, boolean replacing) {
+ final AppWindowToken appWindowToken = mRoot.getAppWindowToken(token);
+ if (appWindowToken == null) {
+ Slog.w(TAG_WM, "Attempted to reset replacing window on non-existing app token "
+ + token);
+ return;
+ }
+ if (replacing) {
+ scheduleWindowReplacementTimeouts(appWindowToken);
+ } else {
+ appWindowToken.clearWillReplaceWindows();
}
}
@@ -6475,11 +6395,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public void setDockedStackResizing(boolean resizing) {
- synchronized (mGlobalLock) {
- getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
- requestTraversal();
- }
+ void setDockedStackResizing(boolean resizing) {
+ getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
+ requestTraversal();
}
@Override
@@ -7024,7 +6942,9 @@ public class WindowManagerService extends IWindowManager.Stub
private final class LocalService extends WindowManagerInternal {
@Override
public void requestTraversalFromDisplayManager() {
- requestTraversal();
+ synchronized (mGlobalLock) {
+ requestTraversal();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowProcessListener.java b/services/core/java/com/android/server/wm/WindowProcessListener.java
index 23d7a6a9d293..1dade1519fdb 100644
--- a/services/core/java/com/android/server/wm/WindowProcessListener.java
+++ b/services/core/java/com/android/server/wm/WindowProcessListener.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import android.util.proto.ProtoOutputStream;
-import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
/**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 99ae18d67be5..501a93ef6645 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -520,11 +520,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/** When true this window can be displayed on screens owther than mOwnerUid's */
private boolean mShowToOwnerOnly;
- // Whether the window was visible when we set the app to invisible last time. WM uses
- // this as a hint to restore the surface (if available) for early animation next time
- // the app is brought visible.
- private boolean mWasVisibleBeforeClientHidden;
-
// This window will be replaced due to relaunch. This allows window manager
// to differentiate between simple removal of a window and replacement. In the latter case it
// will preserve the old window until the new one is drawn.
@@ -2013,8 +2008,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Visibility of the removed window. Will be used later to update orientation later on.
boolean wasVisible = false;
- final int displayId = getDisplayId();
-
// First, see if we need to run an animation. If we do, we have to hold off on removing the
// window until the animation is done. If the display is frozen, just remove immediately,
// since the animation wouldn't be seen.
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ef1d110c9617..c676e723de71 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -271,20 +271,17 @@ class WindowStateAnimator {
if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.isVisibleByPolicy()) {
// Upon completion of a not-visible to visible status bar animation a relayout is
// required.
- if (displayContent != null) {
- displayContent.setLayoutNeeded();
- }
+ displayContent.setLayoutNeeded();
}
mWin.onExitAnimationDone();
- final int displayId = mWin.getDisplayId();
- int pendingLayoutChanges = FINISH_LAYOUT_REDO_ANIM;
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
if (displayContent.mWallpaperController.isWallpaperTarget(mWin)) {
- pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
- mAnimator.setPendingLayoutChanges(displayId, pendingLayoutChanges);
- if (DEBUG_LAYOUT_REPEATS)
+ if (DEBUG_LAYOUT_REPEATS) {
mService.mWindowPlacerLocked.debugLayoutRepeats(
- "WindowStateAnimator", mAnimator.getPendingLayoutChanges(displayId));
+ "WindowStateAnimator", displayContent.pendingLayoutChanges);
+ }
if (mWin.mAppToken != null) {
mWin.mAppToken.updateReportedVisibilityLocked();
@@ -429,10 +426,6 @@ class WindowStateAnimator {
}
}
- private int getLayerStack() {
- return mWin.getDisplayContent().getDisplay().getLayerStack();
- }
-
void resetDrawState() {
mDrawState = DRAW_PENDING;
@@ -1072,8 +1065,7 @@ class WindowStateAnimator {
if (mSurfaceResized) {
mReportSurfaceResized = true;
- mAnimator.setPendingLayoutChanges(w.getDisplayId(),
- FINISH_LAYOUT_REDO_WALLPAPER);
+ mWin.getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
@@ -1168,16 +1160,16 @@ class WindowStateAnimator {
if (mIsWallpaper) {
w.dispatchWallpaperVisibility(true);
}
- if (!w.getDisplayContent().getLastHasContent()) {
+ final DisplayContent displayContent = w.getDisplayContent();
+ if (!displayContent.getLastHasContent()) {
// This draw means the difference between unique content and mirroring.
// Run another pass through performLayout to set mHasContent in the
// LogicalDisplay.
- mAnimator.setPendingLayoutChanges(w.getDisplayId(),
- FINISH_LAYOUT_REDO_ANIM);
+ displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
if (DEBUG_LAYOUT_REPEATS) {
mService.mWindowPlacerLocked.debugLayoutRepeats(
"showSurfaceRobustlyLocked " + w,
- mAnimator.getPendingLayoutChanges(w.getDisplayId()));
+ displayContent.pendingLayoutChanges);
}
}
} else {
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 6cd9f2c718ee..9ceb7706628a 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -36,61 +36,33 @@
#include <linux/fsverity.h>
#else
-// Before fs-verity is upstreamed, use the current snapshot for development.
-// https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/tree/include/uapi/linux/fsverity.h?h=fsverity
-
#include <linux/limits.h>
#include <linux/ioctl.h>
#include <linux/types.h>
+#define FS_VERITY_HASH_ALG_SHA256 1
+
+struct fsverity_enable_arg {
+ __u32 version;
+ __u32 hash_algorithm;
+ __u32 block_size;
+ __u32 salt_size;
+ __u64 salt_ptr;
+ __u32 sig_size;
+ __u32 __reserved1;
+ __u64 sig_ptr;
+ __u64 __reserved2[11];
+};
+
struct fsverity_digest {
__u16 digest_algorithm;
__u16 digest_size; /* input/output */
__u8 digest[];
};
-#define FS_IOC_ENABLE_VERITY _IO('f', 133)
+#define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg)
#define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest)
-#define FS_VERITY_MAGIC "FSVerity"
-
-#define FS_VERITY_ALG_SHA256 1
-
-struct fsverity_descriptor {
- __u8 magic[8]; /* must be FS_VERITY_MAGIC */
- __u8 major_version; /* must be 1 */
- __u8 minor_version; /* must be 0 */
- __u8 log_data_blocksize;/* log2(data-bytes-per-hash), e.g. 12 for 4KB */
- __u8 log_tree_blocksize;/* log2(tree-bytes-per-hash), e.g. 12 for 4KB */
- __le16 data_algorithm; /* hash algorithm for data blocks */
- __le16 tree_algorithm; /* hash algorithm for tree blocks */
- __le32 flags; /* flags */
- __le32 __reserved1; /* must be 0 */
- __le64 orig_file_size; /* size of the original file data */
- __le16 auth_ext_count; /* number of authenticated extensions */
- __u8 __reserved2[30]; /* must be 0 */
-};
-
-#define FS_VERITY_EXT_ROOT_HASH 1
-#define FS_VERITY_EXT_PKCS7_SIGNATURE 3
-
-struct fsverity_extension {
- __le32 length;
- __le16 type; /* Type of this extension (see codes above) */
- __le16 __reserved; /* Reserved, must be 0 */
-};
-
-struct fsverity_digest_disk {
- __le16 digest_algorithm;
- __le16 digest_size;
- __u8 digest[];
-};
-
-struct fsverity_footer {
- __le32 desc_reverse_offset; /* distance to fsverity_descriptor */
- __u8 magic[8]; /* FS_VERITY_MAGIC */
-} __packed;
-
#endif
const int kSha256Bytes = 32;
@@ -99,52 +71,24 @@ namespace android {
namespace {
-class JavaByteArrayHolder {
- public:
- JavaByteArrayHolder(const JavaByteArrayHolder &other) = delete;
- JavaByteArrayHolder(JavaByteArrayHolder &&other)
- : mEnv(other.mEnv), mBytes(other.mBytes), mElements(other.mElements) {
- other.mElements = nullptr;
- }
-
- static JavaByteArrayHolder newArray(JNIEnv* env, jsize size) {
- return JavaByteArrayHolder(env, size);
- }
-
- jbyte* getRaw() {
- return mElements;
- }
-
- jbyteArray release() {
- mEnv->ReleaseByteArrayElements(mBytes, mElements, 0);
- mElements = nullptr;
- return mBytes;
- }
-
- ~JavaByteArrayHolder() {
- LOG_ALWAYS_FATAL_IF(mElements != nullptr, "Elements are not released");
- }
-
- private:
- JavaByteArrayHolder(JNIEnv* env, jsize size) {
- mEnv = env;
- mBytes = mEnv->NewByteArray(size);
- mElements = mEnv->GetByteArrayElements(mBytes, nullptr);
- memset(mElements, 0, size);
- }
-
- JNIEnv* mEnv;
- jbyteArray mBytes;
- jbyte* mElements;
-};
-
-int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
+int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArray signature) {
const char* path = env->GetStringUTFChars(filePath, nullptr);
::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
+ env->ReleaseStringUTFChars(filePath, path);
if (rfd.get() < 0) {
return errno;
}
- if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, nullptr) < 0) {
+
+ fsverity_enable_arg arg = {};
+ arg.version = 1;
+ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+ arg.block_size = 4096;
+ arg.salt_size = 0;
+ arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
+ arg.sig_size = env->GetArrayLength(signature);
+ arg.sig_ptr = reinterpret_cast<uintptr_t>(signature);
+
+ if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
return errno;
}
return 0;
@@ -159,6 +103,7 @@ int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
const char* path = env->GetStringUTFChars(filePath, nullptr);
::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
+ env->ReleaseStringUTFChars(filePath, path);
if (rfd.get() < 0) {
return errno;
}
@@ -168,71 +113,9 @@ int measureFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath) {
return 0;
}
-jbyteArray constructFsveritySignedData(JNIEnv* env, jobject /* clazz */, jbyteArray digest) {
- auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_digest_disk) + kSha256Bytes);
- fsverity_digest_disk* data = reinterpret_cast<fsverity_digest_disk*>(raii.getRaw());
-
- data->digest_algorithm = FS_VERITY_ALG_SHA256;
- data->digest_size = kSha256Bytes;
- if (env->GetArrayLength(digest) != kSha256Bytes) {
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "Invalid hash size of %d",
- env->GetArrayLength(digest));
- return 0;
- }
- const jbyte* src = env->GetByteArrayElements(digest, nullptr);
- memcpy(data->digest, src, kSha256Bytes);
-
- return raii.release();
-}
-
-
-jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong fileSize) {
- auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_descriptor));
- fsverity_descriptor* desc = reinterpret_cast<fsverity_descriptor*>(raii.getRaw());
-
- memcpy(desc->magic, FS_VERITY_MAGIC, sizeof(desc->magic));
- desc->major_version = 1;
- desc->minor_version = 0;
- desc->log_data_blocksize = 12;
- desc->log_tree_blocksize = 12;
- desc->data_algorithm = FS_VERITY_ALG_SHA256;
- desc->tree_algorithm = FS_VERITY_ALG_SHA256;
- desc->flags = 0;
- desc->orig_file_size = fileSize;
- desc->auth_ext_count = 1;
-
- return raii.release();
-}
-
-jbyteArray constructFsverityExtension(JNIEnv* env, jobject /* clazz */, jshort extensionId,
- jint extensionDataSize) {
- auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_extension));
- fsverity_extension* ext = reinterpret_cast<fsverity_extension*>(raii.getRaw());
-
- ext->length = sizeof(fsverity_extension) + extensionDataSize;
- ext->type = extensionId;
-
- return raii.release();
-}
-
-jbyteArray constructFsverityFooter(JNIEnv* env, jobject /* clazz */,
- jint offsetToDescriptorHead) {
- auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_footer));
- fsverity_footer* footer = reinterpret_cast<fsverity_footer*>(raii.getRaw());
-
- footer->desc_reverse_offset = offsetToDescriptorHead + sizeof(fsverity_footer);
- memcpy(footer->magic, FS_VERITY_MAGIC, sizeof(footer->magic));
-
- return raii.release();
-}
-
const JNINativeMethod sMethods[] = {
- { "enableFsverityNative", "(Ljava/lang/String;)I", (void *)enableFsverity },
+ { "enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity },
{ "measureFsverityNative", "(Ljava/lang/String;)I", (void *)measureFsverity },
- { "constructFsveritySignedDataNative", "([B)[B", (void *)constructFsveritySignedData },
- { "constructFsverityDescriptorNative", "(J)[B", (void *)constructFsverityDescriptor },
- { "constructFsverityExtensionNative", "(SI)[B", (void *)constructFsverityExtension },
- { "constructFsverityFooterNative", "(I)[B", (void *)constructFsverityFooter },
};
} // namespace
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3154c7021255..704c80870fe5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1113,142 +1113,107 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
info.writePoliciesToXml(out);
out.endTag(null, TAG_POLICIES);
if (minimumPasswordMetrics.quality != PASSWORD_QUALITY_UNSPECIFIED) {
- out.startTag(null, TAG_PASSWORD_QUALITY);
- out.attribute(null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.quality));
- out.endTag(null, TAG_PASSWORD_QUALITY);
+ writeAttributeValueToXml(
+ out, TAG_PASSWORD_QUALITY, minimumPasswordMetrics.quality);
if (minimumPasswordMetrics.length != DEF_MINIMUM_PASSWORD_LENGTH) {
- out.startTag(null, TAG_MIN_PASSWORD_LENGTH);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.length));
- out.endTag(null, TAG_MIN_PASSWORD_LENGTH);
- }
- if(passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
- out.startTag(null, TAG_PASSWORD_HISTORY_LENGTH);
- out.attribute(null, ATTR_VALUE, Integer.toString(passwordHistoryLength));
- out.endTag(null, TAG_PASSWORD_HISTORY_LENGTH);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_LENGTH, minimumPasswordMetrics.length);
}
if (minimumPasswordMetrics.upperCase != DEF_MINIMUM_PASSWORD_UPPER_CASE) {
- out.startTag(null, TAG_MIN_PASSWORD_UPPERCASE);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.upperCase));
- out.endTag(null, TAG_MIN_PASSWORD_UPPERCASE);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_UPPERCASE, minimumPasswordMetrics.upperCase);
}
if (minimumPasswordMetrics.lowerCase != DEF_MINIMUM_PASSWORD_LOWER_CASE) {
- out.startTag(null, TAG_MIN_PASSWORD_LOWERCASE);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.lowerCase));
- out.endTag(null, TAG_MIN_PASSWORD_LOWERCASE);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_LOWERCASE, minimumPasswordMetrics.lowerCase);
}
if (minimumPasswordMetrics.letters != DEF_MINIMUM_PASSWORD_LETTERS) {
- out.startTag(null, TAG_MIN_PASSWORD_LETTERS);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.letters));
- out.endTag(null, TAG_MIN_PASSWORD_LETTERS);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_LETTERS, minimumPasswordMetrics.letters);
}
if (minimumPasswordMetrics.numeric != DEF_MINIMUM_PASSWORD_NUMERIC) {
- out.startTag(null, TAG_MIN_PASSWORD_NUMERIC);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.numeric));
- out.endTag(null, TAG_MIN_PASSWORD_NUMERIC);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_NUMERIC, minimumPasswordMetrics.numeric);
}
if (minimumPasswordMetrics.symbols != DEF_MINIMUM_PASSWORD_SYMBOLS) {
- out.startTag(null, TAG_MIN_PASSWORD_SYMBOLS);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.symbols));
- out.endTag(null, TAG_MIN_PASSWORD_SYMBOLS);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_SYMBOLS, minimumPasswordMetrics.symbols);
}
if (minimumPasswordMetrics.nonLetter > DEF_MINIMUM_PASSWORD_NON_LETTER) {
- out.startTag(null, TAG_MIN_PASSWORD_NONLETTER);
- out.attribute(
- null, ATTR_VALUE, Integer.toString(minimumPasswordMetrics.nonLetter));
- out.endTag(null, TAG_MIN_PASSWORD_NONLETTER);
+ writeAttributeValueToXml(
+ out, TAG_MIN_PASSWORD_NONLETTER, minimumPasswordMetrics.nonLetter);
}
}
+ if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
+ writeAttributeValueToXml(
+ out, TAG_PASSWORD_HISTORY_LENGTH, passwordHistoryLength);
+ }
if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) {
- out.startTag(null, TAG_MAX_TIME_TO_UNLOCK);
- out.attribute(null, ATTR_VALUE, Long.toString(maximumTimeToUnlock));
- out.endTag(null, TAG_MAX_TIME_TO_UNLOCK);
+ writeAttributeValueToXml(
+ out, TAG_MAX_TIME_TO_UNLOCK, maximumTimeToUnlock);
}
if (strongAuthUnlockTimeout != DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) {
- out.startTag(null, TAG_STRONG_AUTH_UNLOCK_TIMEOUT);
- out.attribute(null, ATTR_VALUE, Long.toString(strongAuthUnlockTimeout));
- out.endTag(null, TAG_STRONG_AUTH_UNLOCK_TIMEOUT);
+ writeAttributeValueToXml(
+ out, TAG_STRONG_AUTH_UNLOCK_TIMEOUT, strongAuthUnlockTimeout);
}
if (maximumFailedPasswordsForWipe != DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
- out.startTag(null, TAG_MAX_FAILED_PASSWORD_WIPE);
- out.attribute(null, ATTR_VALUE, Integer.toString(maximumFailedPasswordsForWipe));
- out.endTag(null, TAG_MAX_FAILED_PASSWORD_WIPE);
+ writeAttributeValueToXml(
+ out, TAG_MAX_FAILED_PASSWORD_WIPE, maximumFailedPasswordsForWipe);
}
if (specifiesGlobalProxy) {
- out.startTag(null, TAG_SPECIFIES_GLOBAL_PROXY);
- out.attribute(null, ATTR_VALUE, Boolean.toString(specifiesGlobalProxy));
- out.endTag(null, TAG_SPECIFIES_GLOBAL_PROXY);
+ writeAttributeValueToXml(
+ out, TAG_SPECIFIES_GLOBAL_PROXY, specifiesGlobalProxy);
if (globalProxySpec != null) {
- out.startTag(null, TAG_GLOBAL_PROXY_SPEC);
- out.attribute(null, ATTR_VALUE, globalProxySpec);
- out.endTag(null, TAG_GLOBAL_PROXY_SPEC);
+ writeAttributeValueToXml(out, TAG_GLOBAL_PROXY_SPEC, globalProxySpec);
}
if (globalProxyExclusionList != null) {
- out.startTag(null, TAG_GLOBAL_PROXY_EXCLUSION_LIST);
- out.attribute(null, ATTR_VALUE, globalProxyExclusionList);
- out.endTag(null, TAG_GLOBAL_PROXY_EXCLUSION_LIST);
+ writeAttributeValueToXml(
+ out, TAG_GLOBAL_PROXY_EXCLUSION_LIST, globalProxyExclusionList);
}
}
if (passwordExpirationTimeout != DEF_PASSWORD_EXPIRATION_TIMEOUT) {
- out.startTag(null, TAG_PASSWORD_EXPIRATION_TIMEOUT);
- out.attribute(null, ATTR_VALUE, Long.toString(passwordExpirationTimeout));
- out.endTag(null, TAG_PASSWORD_EXPIRATION_TIMEOUT);
+ writeAttributeValueToXml(
+ out, TAG_PASSWORD_EXPIRATION_TIMEOUT, passwordExpirationTimeout);
}
if (passwordExpirationDate != DEF_PASSWORD_EXPIRATION_DATE) {
- out.startTag(null, TAG_PASSWORD_EXPIRATION_DATE);
- out.attribute(null, ATTR_VALUE, Long.toString(passwordExpirationDate));
- out.endTag(null, TAG_PASSWORD_EXPIRATION_DATE);
+ writeAttributeValueToXml(
+ out, TAG_PASSWORD_EXPIRATION_DATE, passwordExpirationDate);
}
if (encryptionRequested) {
- out.startTag(null, TAG_ENCRYPTION_REQUESTED);
- out.attribute(null, ATTR_VALUE, Boolean.toString(encryptionRequested));
- out.endTag(null, TAG_ENCRYPTION_REQUESTED);
+ writeAttributeValueToXml(
+ out, TAG_ENCRYPTION_REQUESTED, encryptionRequested);
}
if (testOnlyAdmin) {
- out.startTag(null, TAG_TEST_ONLY_ADMIN);
- out.attribute(null, ATTR_VALUE, Boolean.toString(testOnlyAdmin));
- out.endTag(null, TAG_TEST_ONLY_ADMIN);
+ writeAttributeValueToXml(
+ out, TAG_TEST_ONLY_ADMIN, testOnlyAdmin);
}
if (disableCamera) {
- out.startTag(null, TAG_DISABLE_CAMERA);
- out.attribute(null, ATTR_VALUE, Boolean.toString(disableCamera));
- out.endTag(null, TAG_DISABLE_CAMERA);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_CAMERA, disableCamera);
}
if (disableCallerId) {
- out.startTag(null, TAG_DISABLE_CALLER_ID);
- out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
- out.endTag(null, TAG_DISABLE_CALLER_ID);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_CALLER_ID, disableCallerId);
}
if (disableContactsSearch) {
- out.startTag(null, TAG_DISABLE_CONTACTS_SEARCH);
- out.attribute(null, ATTR_VALUE, Boolean.toString(disableContactsSearch));
- out.endTag(null, TAG_DISABLE_CONTACTS_SEARCH);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_CONTACTS_SEARCH, disableContactsSearch);
}
if (!disableBluetoothContactSharing) {
- out.startTag(null, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING);
- out.attribute(null, ATTR_VALUE,
- Boolean.toString(disableBluetoothContactSharing));
- out.endTag(null, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_BLUETOOTH_CONTACT_SHARING, disableBluetoothContactSharing);
}
if (disableScreenCapture) {
- out.startTag(null, TAG_DISABLE_SCREEN_CAPTURE);
- out.attribute(null, ATTR_VALUE, Boolean.toString(disableScreenCapture));
- out.endTag(null, TAG_DISABLE_SCREEN_CAPTURE);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_SCREEN_CAPTURE, disableScreenCapture);
}
if (requireAutoTime) {
- out.startTag(null, TAG_REQUIRE_AUTO_TIME);
- out.attribute(null, ATTR_VALUE, Boolean.toString(requireAutoTime));
- out.endTag(null, TAG_REQUIRE_AUTO_TIME);
+ writeAttributeValueToXml(
+ out, TAG_REQUIRE_AUTO_TIME, requireAutoTime);
}
if (forceEphemeralUsers) {
- out.startTag(null, TAG_FORCE_EPHEMERAL_USERS);
- out.attribute(null, ATTR_VALUE, Boolean.toString(forceEphemeralUsers));
- out.endTag(null, TAG_FORCE_EPHEMERAL_USERS);
+ writeAttributeValueToXml(
+ out, TAG_FORCE_EPHEMERAL_USERS, forceEphemeralUsers);
}
if (isNetworkLoggingEnabled) {
out.startTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
@@ -1260,15 +1225,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.endTag(null, TAG_IS_NETWORK_LOGGING_ENABLED);
}
if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
- out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
- out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
- out.endTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
+ writeAttributeValueToXml(
+ out, TAG_DISABLE_KEYGUARD_FEATURES, disabledKeyguardFeatures);
}
if (!accountTypesWithManagementDisabled.isEmpty()) {
- out.startTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT);
writeAttributeValuesToXml(
- out, TAG_ACCOUNT_TYPE, accountTypesWithManagementDisabled);
- out.endTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT);
+ out, TAG_DISABLE_ACCOUNT_MANAGEMENT, TAG_ACCOUNT_TYPE,
+ accountTypesWithManagementDisabled);
}
if (!trustAgentInfos.isEmpty()) {
Set<Entry<String, TrustAgentInfo>> set = trustAgentInfos.entrySet();
@@ -1291,9 +1254,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.endTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
}
if (crossProfileWidgetProviders != null && !crossProfileWidgetProviders.isEmpty()) {
- out.startTag(null, TAG_CROSS_PROFILE_WIDGET_PROVIDERS);
- writeAttributeValuesToXml(out, TAG_PROVIDER, crossProfileWidgetProviders);
- out.endTag(null, TAG_CROSS_PROFILE_WIDGET_PROVIDERS);
+ writeAttributeValuesToXml(
+ out, TAG_CROSS_PROFILE_WIDGET_PROVIDERS, TAG_PROVIDER,
+ crossProfileWidgetProviders);
}
writePackageListToXml(out, TAG_PERMITTED_ACCESSIBILITY_SERVICES,
permittedAccessiblityServices);
@@ -1307,20 +1270,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out, userRestrictions, TAG_USER_RESTRICTIONS);
}
if (!defaultEnabledRestrictionsAlreadySet.isEmpty()) {
- out.startTag(null, TAG_DEFAULT_ENABLED_USER_RESTRICTIONS);
- writeAttributeValuesToXml(
- out, TAG_RESTRICTION, defaultEnabledRestrictionsAlreadySet);
- out.endTag(null, TAG_DEFAULT_ENABLED_USER_RESTRICTIONS);
+ writeAttributeValuesToXml(out, TAG_DEFAULT_ENABLED_USER_RESTRICTIONS,
+ TAG_RESTRICTION,
+ defaultEnabledRestrictionsAlreadySet);
}
if (!TextUtils.isEmpty(shortSupportMessage)) {
- out.startTag(null, TAG_SHORT_SUPPORT_MESSAGE);
- out.text(shortSupportMessage.toString());
- out.endTag(null, TAG_SHORT_SUPPORT_MESSAGE);
+ writeTextToXml(out, TAG_SHORT_SUPPORT_MESSAGE, shortSupportMessage.toString());
}
if (!TextUtils.isEmpty(longSupportMessage)) {
- out.startTag(null, TAG_LONG_SUPPORT_MESSAGE);
- out.text(longSupportMessage.toString());
- out.endTag(null, TAG_LONG_SUPPORT_MESSAGE);
+ writeTextToXml(out, TAG_LONG_SUPPORT_MESSAGE, longSupportMessage.toString());
}
if (parentAdmin != null) {
out.startTag(null, TAG_PARENT_ADMIN);
@@ -1328,29 +1286,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
out.endTag(null, TAG_PARENT_ADMIN);
}
if (organizationColor != DEF_ORGANIZATION_COLOR) {
- out.startTag(null, TAG_ORGANIZATION_COLOR);
- out.attribute(null, ATTR_VALUE, Integer.toString(organizationColor));
- out.endTag(null, TAG_ORGANIZATION_COLOR);
+ writeAttributeValueToXml(out, TAG_ORGANIZATION_COLOR, organizationColor);
}
if (organizationName != null) {
- out.startTag(null, TAG_ORGANIZATION_NAME);
- out.text(organizationName);
- out.endTag(null, TAG_ORGANIZATION_NAME);
+ writeTextToXml(out, TAG_ORGANIZATION_NAME, organizationName);
}
if (isLogoutEnabled) {
- out.startTag(null, TAG_IS_LOGOUT_ENABLED);
- out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutEnabled));
- out.endTag(null, TAG_IS_LOGOUT_ENABLED);
+ writeAttributeValueToXml(
+ out, TAG_IS_LOGOUT_ENABLED, isLogoutEnabled);
}
if (startUserSessionMessage != null) {
- out.startTag(null, TAG_START_USER_SESSION_MESSAGE);
- out.text(startUserSessionMessage);
- out.endTag(null, TAG_START_USER_SESSION_MESSAGE);
+ writeTextToXml(out, TAG_START_USER_SESSION_MESSAGE, startUserSessionMessage);
}
if (endUserSessionMessage != null) {
- out.startTag(null, TAG_END_USER_SESSION_MESSAGE);
- out.text(endUserSessionMessage);
- out.endTag(null, TAG_END_USER_SESSION_MESSAGE);
+ writeTextToXml(out, TAG_END_USER_SESSION_MESSAGE, endUserSessionMessage);
}
if (mCrossProfileCalendarPackages == null) {
out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES_NULL);
@@ -1361,25 +1310,58 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
+ void writeTextToXml(XmlSerializer out, String tag, String text) throws IOException {
+ out.startTag(null, tag);
+ out.text(text);
+ out.endTag(null, tag);
+ }
+
void writePackageListToXml(XmlSerializer out, String outerTag,
List<String> packageList)
throws IllegalArgumentException, IllegalStateException, IOException {
if (packageList == null) {
return;
}
+ writeAttributeValuesToXml(out, outerTag, TAG_PACKAGE_LIST_ITEM, packageList);
+ }
- out.startTag(null, outerTag);
- writeAttributeValuesToXml(out, TAG_PACKAGE_LIST_ITEM, packageList);
- out.endTag(null, outerTag);
+ void writeAttributeValueToXml(XmlSerializer out, String tag, String value)
+ throws IOException {
+ out.startTag(null, tag);
+ out.attribute(null, ATTR_VALUE, value);
+ out.endTag(null, tag);
+ }
+
+ void writeAttributeValueToXml(XmlSerializer out, String tag, int value)
+ throws IOException {
+ out.startTag(null, tag);
+ out.attribute(null, ATTR_VALUE, Integer.toString(value));
+ out.endTag(null, tag);
+ }
+
+ void writeAttributeValueToXml(XmlSerializer out, String tag, long value)
+ throws IOException {
+ out.startTag(null, tag);
+ out.attribute(null, ATTR_VALUE, Long.toString(value));
+ out.endTag(null, tag);
}
- void writeAttributeValuesToXml(XmlSerializer out, String tag,
+ void writeAttributeValueToXml(XmlSerializer out, String tag, boolean value)
+ throws IOException {
+ out.startTag(null, tag);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(value));
+ out.endTag(null, tag);
+ }
+
+ void writeAttributeValuesToXml(XmlSerializer out, String outerTag, String innerTag,
@NonNull Collection<String> values) throws IOException {
+ out.startTag(null, outerTag);
for (String value : values) {
- out.startTag(null, tag);
+ out.startTag(null, innerTag);
out.attribute(null, ATTR_VALUE, value);
- out.endTag(null, tag);
+ out.endTag(null, innerTag);
}
+ out.endTag(null, outerTag);
}
void readFromXml(XmlPullParser parser, boolean shouldOverridePolicies)
@@ -5663,7 +5645,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
KeyChain.bindAsUser(mContext, UserHandle.getUserHandleForUid(callingUid));
try {
IKeyChainService keyChain = keyChainConnection.getService();
- if (!keyChain.installKeyPair(privKey, cert, chain, alias)) {
+ if (!keyChain.installKeyPair(privKey, cert, chain, alias, KeyStore.UID_SELF)) {
return false;
}
if (requestAccess) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java
index 2585a2832094..b7079124fb79 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/AccessibilityGestureDetectorTest.java
@@ -176,6 +176,6 @@ public class AccessibilityGestureDetectorTest {
// Check that correct gesture was recognized.
verify(mResultListener).onGestureCompleted(
- argThat(gestureInfo -> gestureInfo.getGestureId() == gestureId));
+ argThat(gestureEvent -> gestureEvent.getGestureId() == gestureId));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 6a0d7f192adb..9e3b54d1ca96 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -22,7 +22,6 @@ import static com.android.server.am.MemoryStatUtil.MemoryStat;
import static com.android.server.am.MemoryStatUtil.parseCmdlineFromProcfs;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
-import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -230,18 +229,6 @@ public class MemoryStatUtilTest {
}
@Test
- public void testParseVmHWMFromProcfs_parsesCorrectValue() {
- assertEquals(137668, parseVmHWMFromProcfs(PROC_STATUS_CONTENTS));
- }
-
- @Test
- public void testParseVmHWMFromProcfs_emptyContents() {
- assertEquals(0, parseVmHWMFromProcfs(""));
-
- assertEquals(0, parseVmHWMFromProcfs(null));
- }
-
- @Test
public void testParseCmdlineFromProcfs_invalidValue() {
byte[] nothing = new byte[] {0x00, 0x74, 0x65, 0x73, 0x74}; // \0test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index d6cb9826d514..d90091017116 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -765,7 +765,38 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}
/**
- * Test for: @{link DevicePolicyManager#reportPasswordChanged}
+ * Test for: {@link DevicePolicyManager#setPasswordHistoryLength(ComponentName, int)}
+ *
+ * Validates that when the password history length is set, it is persisted after rebooting
+ */
+ public void testSaveAndLoadPasswordHistoryLength_persistedAfterReboot() throws Exception {
+ int passwordHistoryLength = 2;
+
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // Install admin1 on system user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+ // Set admin1 to active admin and device owner
+ dpm.setActiveAdmin(admin1, false);
+ dpm.setDeviceOwner(admin1, null, UserHandle.USER_SYSTEM);
+
+ // Save password history length
+ dpm.setPasswordHistoryLength(admin1, passwordHistoryLength);
+
+ assertEquals(dpm.getPasswordHistoryLength(admin1), passwordHistoryLength);
+
+ initializeDpms();
+ reset(mContext.spiedContext);
+
+ // Password history length should persist after rebooted
+ assertEquals(dpm.getPasswordHistoryLength(admin1), passwordHistoryLength);
+ }
+
+ /**
+ * Test for: {@link DevicePolicyManager#reportPasswordChanged}
*
* Validates that when the password for a user changes, the notification broadcast intent
* {@link DeviceAdminReceiver#ACTION_PASSWORD_CHANGED} is sent to managed profile owners, in
diff --git a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
new file mode 100644
index 000000000000..4fb533f726b2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.stats;
+
+import static com.android.server.stats.ProcfsMemoryUtil.parseVmHWMFromStatus;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:ProcfsMemoryUtilTest
+ */
+@SmallTest
+public class ProcfsMemoryUtilTest {
+ private static final String STATUS_CONTENTS = "Name:\tandroid.youtube\n"
+ + "State:\tS (sleeping)\n"
+ + "Tgid:\t12088\n"
+ + "Pid:\t12088\n"
+ + "PPid:\t723\n"
+ + "TracerPid:\t0\n"
+ + "Uid:\t10083\t10083\t10083\t10083\n"
+ + "Gid:\t10083\t10083\t10083\t10083\n"
+ + "Ngid:\t0\n"
+ + "FDSize:\t128\n"
+ + "Groups:\t3003 9997 20083 50083 \n"
+ + "VmPeak:\t 4546844 kB\n"
+ + "VmSize:\t 4542636 kB\n"
+ + "VmLck:\t 0 kB\n"
+ + "VmPin:\t 0 kB\n"
+ + "VmHWM:\t 137668 kB\n" // RSS high-water mark
+ + "VmRSS:\t 126776 kB\n" // RSS
+ + "RssAnon:\t 37860 kB\n"
+ + "RssFile:\t 88764 kB\n"
+ + "RssShmem:\t 152 kB\n"
+ + "VmData:\t 4125112 kB\n"
+ + "VmStk:\t 8192 kB\n"
+ + "VmExe:\t 24 kB\n"
+ + "VmLib:\t 102432 kB\n"
+ + "VmPTE:\t 1300 kB\n"
+ + "VmPMD:\t 36 kB\n"
+ + "VmSwap:\t 22 kB\n" // Swap
+ + "Threads:\t95\n"
+ + "SigQ:\t0/13641\n"
+ + "SigPnd:\t0000000000000000\n"
+ + "ShdPnd:\t0000000000000000\n"
+ + "SigBlk:\t0000000000001204\n"
+ + "SigIgn:\t0000000000000001\n"
+ + "SigCgt:\t00000006400084f8\n"
+ + "CapInh:\t0000000000000000\n"
+ + "CapPrm:\t0000000000000000\n"
+ + "CapEff:\t0000000000000000\n"
+ + "CapBnd:\t0000000000000000\n"
+ + "CapAmb:\t0000000000000000\n"
+ + "Seccomp:\t2\n"
+ + "Cpus_allowed:\tff\n"
+ + "Cpus_allowed_list:\t0-7\n"
+ + "Mems_allowed:\t1\n"
+ + "Mems_allowed_list:\t0\n"
+ + "voluntary_ctxt_switches:\t903\n"
+ + "nonvoluntary_ctxt_switches:\t104\n";
+
+ @Test
+ public void testParseVmHWMFromStatus_parsesCorrectValue() {
+ assertThat(parseVmHWMFromStatus(STATUS_CONTENTS)).isEqualTo(137668);
+ }
+
+ @Test
+ public void testParseVmHWMFromStatus_invalidValue() {
+ assertThat(parseVmHWMFromStatus("test\nVmHWM: x0x0x\ntest")).isEqualTo(0);
+ }
+
+ @Test
+ public void testParseVmHWMFromStatus_emptyContents() {
+ assertThat(parseVmHWMFromStatus("")).isEqualTo(0);
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index e15af3dbecc4..0b4760d89686 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -68,6 +68,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
private final int uid2 = 1111111;
private static final String TEST_CHANNEL_ID = "test_channel_id";
+ private NotificationRecord mRecordMinCallNonInterruptive;
private NotificationRecord mRecordMinCall;
private NotificationRecord mRecordHighCall;
private NotificationRecord mRecordDefaultMedia;
@@ -105,6 +106,18 @@ public class NotificationComparatorTest extends UiServiceTestCase {
smsPkg = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.SMS_DEFAULT_APPLICATION);
+ Notification nonInterruptiveNotif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setCategory(Notification.CATEGORY_CALL)
+ .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+ .build();
+ mRecordMinCallNonInterruptive = new NotificationRecord(mContext,
+ new StatusBarNotification(callPkg,
+ callPkg, 1, "mRecordMinCallNonInterruptive", callUid, callUid,
+ nonInterruptiveNotif,
+ new UserHandle(userId), "", 2000), getDefaultChannel());
+ mRecordMinCallNonInterruptive.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
+ mRecordMinCallNonInterruptive.setInterruptive(false);
+
Notification n1 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setCategory(Notification.CATEGORY_CALL)
.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
@@ -113,6 +126,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
callPkg, 1, "minCall", callUid, callUid, n1,
new UserHandle(userId), "", 2000), getDefaultChannel());
mRecordMinCall.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
+ mRecordMinCall.setInterruptive(true);
Notification n2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setCategory(Notification.CATEGORY_CALL)
@@ -245,6 +259,7 @@ public class NotificationComparatorTest extends UiServiceTestCase {
expected.add(mRecordCheater);
expected.add(mRecordCheaterColorized);
expected.add(mRecordMinCall);
+ expected.add(mRecordMinCallNonInterruptive);
List<NotificationRecord> actual = new ArrayList<>();
actual.addAll(expected);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 397d2155beeb..a9fe1a62b558 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -51,6 +51,8 @@ import android.service.notification.NotificationRankingUpdate;
import android.service.notification.SnoozeCriterion;
import android.test.suitebuilder.annotation.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.server.UiServiceTestCase;
import org.junit.After;
@@ -61,8 +63,6 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
-import androidx.test.runner.AndroidJUnit4;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NotificationListenerServiceTest extends UiServiceTestCase {
@@ -116,6 +116,7 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions());
assertEquals(getSmartReplies(key, i), ranking.getSmartReplies());
assertEquals(canBubble(i), ranking.canBubble());
+ assertEquals(visuallyInterruptive(i), ranking.visuallyInterruptive());
}
}
@@ -182,7 +183,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
tweak.isNoisy(),
(ArrayList) tweak.getSmartActions(),
(ArrayList) tweak.getSmartReplies(),
- tweak.canBubble()
+ tweak.canBubble(),
+ tweak.visuallyInterruptive()
);
assertNotEquals(nru, nru2);
}
@@ -258,7 +260,8 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
getNoisy(i),
getSmartActions(key, i),
getSmartReplies(key, i),
- canBubble(i)
+ canBubble(i),
+ visuallyInterruptive(i)
);
rankings[i] = ranking;
}
@@ -363,6 +366,10 @@ public class NotificationListenerServiceTest extends UiServiceTestCase {
return index % 4 == 0;
}
+ private boolean visuallyInterruptive(int index) {
+ return index % 4 == 0;
+ }
+
private void assertActionsEqual(
List<Notification.Action> expecteds, List<Notification.Action> actuals) {
assertEquals(expecteds.size(), actuals.size());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8444ab2ebeb8..30c8eb36f34a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -658,6 +658,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Override
public void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
IRemoteAnimationFinishedCallback finishedCallback) {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 8393ae0c3aec..1f672c0c95f3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -366,7 +366,7 @@ public class ActivityStarterTests extends ActivityTestsBase {
// Never review permissions
doReturn(false).when(mockPackageManager).isPermissionsReviewRequired(any(), anyInt());
- doNothing().when(mockPackageManager).grantEphemeralAccess(
+ doNothing().when(mockPackageManager).grantImplicitAccess(
anyInt(), any(), anyInt(), anyInt());
final Intent intent = new Intent();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 4f00383d1789..d311dfc60675 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -38,7 +38,6 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.testing.DexmakerShareClassLoaderRule;
@@ -446,12 +445,7 @@ class ActivityTestsBase {
final ActivityStackSupervisor supervisor = mRootActivityContainer.mStackSupervisor;
if (mWindowingMode == WINDOWING_MODE_PINNED) {
stack = new ActivityStack(mDisplay, stackId, supervisor,
- mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) {
- @Override
- Rect getDefaultPictureInPictureBounds(float aspectRatio) {
- return new Rect(50, 50, 100, 100);
- }
- };
+ mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop);
} else {
stack = new ActivityStack(mDisplay, stackId, supervisor,
mWindowingMode, mActivityType, mOnTop);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 035568f489be..629a95453054 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -75,6 +75,7 @@ public class AppChangeTransitionTests extends WindowTestsBase {
class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
@Override
public void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
IRemoteAnimationFinishedCallback finishedCallback) {
for (RemoteAnimationTarget target : apps) {
assertNotNull(target.startBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index c162b6a5a289..45e68902e123 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -248,6 +248,7 @@ public class AppTransitionTests extends WindowTestsBase {
boolean mCancelled = false;
@Override
public void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index efd468f1f77a..e9c226340164 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -66,8 +66,8 @@ public class PinnedStackControllerTest extends WindowTestsBase {
verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0);
verify(mIPinnedStackListener).onShelfVisibilityChanged(false, 0);
- verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
- eq(false), anyInt());
+ verify(mIPinnedStackListener).onMovementBoundsChanged(any(), eq(false),
+ eq(false));
verify(mIPinnedStackListener).onActionsChanged(any());
verify(mIPinnedStackListener).onMinimizedStateChanged(anyBoolean());
@@ -75,8 +75,8 @@ public class PinnedStackControllerTest extends WindowTestsBase {
mWm.setShelfHeight(true, SHELF_HEIGHT);
verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT);
- verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
- eq(true), anyInt());
+ verify(mIPinnedStackListener).onMovementBoundsChanged(any(), eq(false),
+ eq(true));
verify(mIPinnedStackListener, never()).onImeVisibilityChanged(anyBoolean(), anyInt());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 9ca0180e507d..f792b0db6cb5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -26,13 +26,17 @@ import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -44,9 +48,12 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import android.app.ActivityManager.TaskSnapshot;
import android.os.Binder;
+import android.os.IBinder;
import android.os.IInterface;
import android.platform.test.annotations.Presubmit;
import android.util.SparseBooleanArray;
@@ -88,8 +95,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
doReturn(mDisplayContent).when(mWm.mRoot).getDisplayContent(anyInt());
}
when(mMockRunner.asBinder()).thenReturn(new Binder());
- mController = new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
- DEFAULT_DISPLAY);
+ mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
+ DEFAULT_DISPLAY));
}
@Test
@@ -133,11 +140,11 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
@Test
public void testIncludedApps_expectTargetAndVisible() {
mWm.setRecentsAnimationController(mController);
- final ActivityStack homStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
final AppWindowToken homeAppWindow =
new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
- .setStack(homStack)
+ .setStack(homeStack)
.setCreateTask(true)
.build()
.mAppWindowToken;
@@ -157,6 +164,102 @@ public class RecentsAnimationControllerTest extends WindowTestsBase {
}
@Test
+ public void testWallpaperIncluded_expectTarget() throws Exception {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final AppWindowToken homeAppWindow =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build()
+ .mAppWindowToken;
+ final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+ appWindow.addWindow(win1);
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+
+ mDisplayContent.getConfiguration().windowConfiguration.setRotation(
+ mDisplayContent.getRotation());
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+ mController.startAnimation();
+
+ // Ensure that we are animating the app and wallpaper target
+ assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+ assertTrue(mController.isAnimatingWallpaper(wallpaperWindowToken));
+ }
+
+ @Test
+ public void testWallpaperAnimatorCanceled_expectAnimationKeepsRunning() throws Exception {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final AppWindowToken homeAppWindow =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build()
+ .mAppWindowToken;
+ final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+ appWindow.addWindow(win1);
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+
+ mDisplayContent.getConfiguration().windowConfiguration.setRotation(
+ mDisplayContent.getRotation());
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+ mController.startAnimation();
+
+ // Cancel the animation and ensure the controller is still running
+ wallpaperWindowToken.cancelAnimation();
+ assertTrue(mController.isAnimatingTask(appWindow.getTask()));
+ assertFalse(mController.isAnimatingWallpaper(wallpaperWindowToken));
+ verify(mMockRunner, never()).onAnimationCanceled(null /* taskSnapshot */);
+ }
+
+ @Test
+ public void testFinish_expectTargetAndWallpaperAdaptersRemoved() {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityStack homeStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final AppWindowToken homeAppWindow =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build()
+ .mAppWindowToken;
+ final WindowState hwin1 = createWindow(null, TYPE_BASE_APPLICATION, homeAppWindow, "hwin1");
+ homeAppWindow.addWindow(hwin1);
+ final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1");
+ appWindow.addWindow(win1);
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+
+ // Start and finish the animation
+ mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray());
+ mController.startAnimation();
+ // Reset at this point since we may remove adapters that couldn't be created
+ reset(mController);
+ mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
+
+ // Ensure that we remove the task (home & app) and wallpaper adapters
+ verify(mController, times(2)).removeAnimation(any());
+ verify(mController, times(1)).removeWallpaperAnimation(any());
+ }
+
+ @Test
public void testDeferCancelAnimation() throws Exception {
mWm.setRecentsAnimationController(mController);
final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 3e05dcc0f297..3b9c3bbdc4ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -19,7 +19,9 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -27,10 +29,12 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
+import android.os.IBinder;
import android.os.IInterface;
import android.platform.test.annotations.Presubmit;
import android.view.IRemoteAnimationFinishedCallback;
@@ -96,9 +100,12 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
assertEquals(new Point(50, 100), app.position);
@@ -201,9 +208,12 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
}
@@ -237,9 +247,12 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture());
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
assertEquals(RemoteAnimationTarget.MODE_CHANGING, app.mode);
@@ -264,6 +277,66 @@ public class RemoteAnimationControllerTest extends WindowTestsBase {
}
}
+ @Test
+ public void testWallpaperIncluded_expectTarget() throws Exception {
+ final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
+ true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mAppToken);
+ try {
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, wallpapersCaptor.getValue().length);
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
+ @Test
+ public void testWallpaperAnimatorCanceled_expectAnimationKeepsRunning() throws Exception {
+ final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
+ true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+ final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+ mDisplayContent.mOpeningApps.add(win.mAppToken);
+ try {
+ final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mAppToken,
+ new Point(50, 100), new Rect(50, 100, 150, 150), null).mAdapter;
+ adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+ mController.goodToGo();
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+ final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
+ ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
+ verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ finishedCaptor.capture());
+ assertEquals(1, wallpapersCaptor.getValue().length);
+
+ // Cancel the wallpaper window animator and ensure the runner is not canceled
+ wallpaperWindowToken.cancelAnimation();
+ verify(mMockRunner, never()).onAnimationCancelled();
+ } finally {
+ mDisplayContent.mOpeningApps.clear();
+ }
+ }
+
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
verify(binder, atLeast(0)).asBinder();
verifyNoMoreInteractions(binder);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 340e7411d21e..2b1c4fff5861 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -138,6 +138,19 @@ public class SurfaceAnimatorTest extends WindowTestsBase {
}
@Test
+ public void testCancelWithNullFinishCallbackAnimation() {
+ SurfaceAnimator animator = new SurfaceAnimator(mAnimatable, null, mWm);
+ animator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ assertTrue(animator.isAnimating());
+ assertNotNull(animator.getAnimation());
+ animator.cancelAnimation();
+ assertFalse(animator.isAnimating());
+ assertNull(animator.getAnimation());
+ verify(mSpec).onAnimationCancelled(any());
+ verify(mTransaction).remove(eq(mAnimatable.mLeash));
+ }
+
+ @Test
public void testDelayingAnimationStart() {
mAnimatable.mSurfaceAnimator.startDelayingAnimationStart();
mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 2fe2c41e0036..5a4d3991bedd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -231,7 +231,7 @@ public class SystemServicesTestRule implements TestRule {
new AMTestInjector(mContext, mHandlerThread), mHandlerThread);
spyOn(mAmService);
doReturn(mock(IPackageManager.class)).when(mAmService).getPackageManager();
- doNothing().when(mAmService).grantEphemeralAccessLocked(
+ doNothing().when(mAmService).grantImplicitAccess(
anyInt(), any(), anyInt(), anyInt());
// ActivityManagerInternal
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 99337565e128..735b9a1dcf2e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -325,9 +325,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
false /* Don't notify for synchronous calls */);
// Initialize power save, call active state monitoring logic.
- if (status == STATUS_OK && !mRecognitionRequested) {
+ if (status == STATUS_OK) {
initializeTelephonyAndPowerStateListeners();
- mRecognitionRequested = true;
}
return status;
@@ -481,6 +480,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (unloadModel && modelData.isModelLoaded()) {
Slog.d(TAG, "Unloading previously loaded stale model.");
+ if (mModule == null) {
+ return STATUS_ERROR;
+ }
status = mModule.unloadSoundModel(modelData.getHandle());
MetricsLogger.count(mContext, "sth_unloading_stale_model", 1);
if (status != SoundTrigger.STATUS_OK) {
@@ -550,6 +552,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
}
+ if (mModule == null) {
+ return STATUS_ERROR;
+ }
int status = mModule.unloadSoundModel(modelData.getHandle());
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "unloadGenericSoundModel() call failed with " + status);
@@ -878,6 +883,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
mContext.unregisterReceiver(mPowerSaveModeListener);
mPowerSaveModeListener = null;
}
+ mRecognitionRequested = false;
}
// Clears state for all models (generic and keyphrase).
@@ -924,6 +930,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
private void initializeTelephonyAndPowerStateListeners() {
+ if (mRecognitionRequested) {
+ return;
+ }
long token = Binder.clearCallingIdentity();
try {
// Get the current call state synchronously for the first recognition.
@@ -941,6 +950,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
mIsPowerSaveMode = mPowerManager.getPowerSaveState(ServiceType.SOUND)
.batterySaverEnabled;
+
+ mRecognitionRequested = true;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -987,6 +998,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (exception != null) {
Slog.e(TAG, "forceStopAndUnloadModel", exception);
}
+ if (mModule == null) {
+ return;
+ }
if (modelData.isModelStarted()) {
Slog.d(TAG, "Stopping previously started dangling model " + modelData.getHandle());
if (mModule.stopRecognition(modelData.getHandle()) != STATUS_OK) {
@@ -1093,6 +1107,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
// a recognition include: no active phone call or not being in a power save mode. Also,
// the native service should be enabled.
private boolean isRecognitionAllowed() {
+ // if mRecognitionRequested is false, call and power state listeners are not registered so
+ // we read current state directly from services
+ if (!mRecognitionRequested) {
+ mCallActive = mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK;
+ mIsPowerSaveMode =
+ mPowerManager.getPowerSaveState(ServiceType.SOUND).batterySaverEnabled;
+ }
return !mCallActive && !mServiceDisabled && !mIsPowerSaveMode;
}
@@ -1116,6 +1137,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return STATUS_OK;
}
+ if (mModule == null) {
+ return STATUS_ERROR;
+ }
int status = mModule.startRecognition(handle, config);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "startRecognition failed with " + status);
@@ -1152,8 +1176,11 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
private int stopRecognitionLocked(ModelData modelData, boolean notify) {
- IRecognitionStatusCallback callback = modelData.getCallback();
+ if (mModule == null) {
+ return STATUS_ERROR;
+ }
+ IRecognitionStatusCallback callback = modelData.getCallback();
// Stop recognition.
int status = STATUS_OK;
diff --git a/services/wifi/java/android/net/wifi/WifiStackClient.java b/services/wifi/java/android/net/wifi/WifiStackClient.java
index fa66e4c5eeea..64af7a8845a7 100644
--- a/services/wifi/java/android/net/wifi/WifiStackClient.java
+++ b/services/wifi/java/android/net/wifi/WifiStackClient.java
@@ -21,6 +21,7 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityModuleConnector;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -56,13 +57,20 @@ public class WifiStackClient {
public void onModuleServiceConnected(IBinder service) {
Log.i(TAG, "Wifi stack connected");
- registerWifiStackService(service);
- IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
- registerApiServiceAndStart(connector, Context.WIFI_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_SCANNING_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_P2P_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_AWARE_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_RTT_RANGING_SERVICE);
+ // spin up a new thread to not block system_server main thread
+ HandlerThread thread = new HandlerThread("InitWifiServicesThread");
+ thread.start();
+ thread.getThreadHandler().post(() -> {
+ registerWifiStackService(service);
+ IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
+ registerApiServiceAndStart(connector, Context.WIFI_SCANNING_SERVICE);
+ registerApiServiceAndStart(connector, Context.WIFI_SERVICE);
+ registerApiServiceAndStart(connector, Context.WIFI_P2P_SERVICE);
+ registerApiServiceAndStart(connector, Context.WIFI_AWARE_SERVICE);
+ registerApiServiceAndStart(connector, Context.WIFI_RTT_RANGING_SERVICE);
+
+ thread.quitSafely();
+ });
}
}
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index db1d305070d9..a8063209b56f 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -17,6 +17,7 @@
android_app {
name: "startop_test_app",
srcs: [
+ "src/CPUIntensive.java",
"src/EmptyActivity.java",
"src/LayoutInflationActivity.java",
"src/ComplexLayoutInflationActivity.java",
diff --git a/startop/apps/test/src/CPUIntensive.java b/startop/apps/test/src/CPUIntensive.java
new file mode 100644
index 000000000000..a411e8ce8ce0
--- /dev/null
+++ b/startop/apps/test/src/CPUIntensive.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A threaded CPU intensive class for use in benchmarks.
+ */
+
+package com.android.startop.test;
+
+final class CPUIntensive {
+ public static final int THREAD_COUNT = 8;
+ public static final int ARRAY_SIZE = 30000;
+ public static int[][] array = new int[THREAD_COUNT][ARRAY_SIZE];
+
+ static class WorkerThread extends Thread {
+ int mThreadNumber;
+ WorkerThread(int number) {
+ mThreadNumber = number;
+ }
+ public void run() {
+ final int arrayLength = array[mThreadNumber].length;
+ for (int i = 0; i < arrayLength; ++i) {
+ array[mThreadNumber][i] = i * i;
+ }
+ for (int i = 0; i < arrayLength; ++i) {
+ for (int j = 0; j < arrayLength; ++j) {
+ int swap = array[mThreadNumber][j];
+ array[mThreadNumber][j] = array[mThreadNumber][(j + i) % arrayLength];
+ array[mThreadNumber][(j + i) % arrayLength] = swap;
+ }
+ }
+ }
+ };
+
+ public static void doSomeWork(int threadCount) {
+ WorkerThread[] threads = new WorkerThread[threadCount];
+ // Create the threads.
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i] = new WorkerThread(i);
+ }
+ // Start the threads.
+ for (int i = 0; i < threadCount; ++i) {
+ threads[i].start();
+ }
+ // Join the threads.
+ for (int i = 0; i < threadCount; ++i) {
+ try {
+ threads[i].join();
+ } catch (Exception ex) {
+ }
+ }
+ }
+}
+
diff --git a/startop/apps/test/src/SystemServerBenchmarkActivity.java b/startop/apps/test/src/SystemServerBenchmarkActivity.java
index ed3cbe9e3e30..c8d9fde0bdaf 100644
--- a/startop/apps/test/src/SystemServerBenchmarkActivity.java
+++ b/startop/apps/test/src/SystemServerBenchmarkActivity.java
@@ -26,15 +26,11 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Trace;
-import android.view.LayoutInflater;
-import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.TextView;
-import java.util.Arrays;
class Benchmark {
// Time limit to run benchmarks in seconds
@@ -107,6 +103,22 @@ public class SystemServerBenchmarkActivity extends Activity {
new Benchmark(benchmarkList, "Empty", () -> {
});
+ new Benchmark(benchmarkList, "CPU Intensive (1 thread)", () -> {
+ CPUIntensive.doSomeWork(1);
+ });
+
+ new Benchmark(benchmarkList, "CPU Intensive (2 thread)", () -> {
+ CPUIntensive.doSomeWork(2);
+ });
+
+ new Benchmark(benchmarkList, "CPU Intensive (4 thread)", () -> {
+ CPUIntensive.doSomeWork(4);
+ });
+
+ new Benchmark(benchmarkList, "CPU Intensive (8 thread)", () -> {
+ CPUIntensive.doSomeWork(8);
+ });
+
PackageManager pm = getPackageManager();
new Benchmark(benchmarkList, "getInstalledApplications", () -> {
pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY);
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 35488100fb58..0abd9fc62b14 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -30,6 +30,7 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.telecom.Logging.Session;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.IConnectionService;
import com.android.internal.telecom.IConnectionServiceAdapter;
@@ -2672,4 +2673,13 @@ public abstract class ConnectionService extends Service {
return ++mId;
}
}
+
+ /**
+ * Returns this handler, ONLY FOR TESTING.
+ * @hide
+ */
+ @VisibleForTesting
+ public Handler getHandler() {
+ return mHandler;
+ }
}
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 949f7b7a89ae..49c3a7205d59 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -391,6 +391,20 @@ public class SessionManager {
return mCurrentThreadId.get();
}
+ /**
+ * @return A String representation of the active sessions at the time that this method is
+ * called.
+ */
+ @VisibleForTesting
+ public synchronized String printActiveSessions() {
+ StringBuilder message = new StringBuilder();
+ for (ConcurrentHashMap.Entry<Integer, Session> entry : mSessionMapper.entrySet()) {
+ message.append(entry.getValue().printFullSessionTree());
+ message.append("\n");
+ }
+ return message.toString();
+ }
+
@VisibleForTesting
public synchronized void cleanupStaleSessions(long timeoutMs) {
String logMessage = "Stale Sessions Cleaned:\n";
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 58f285851375..f527bc3b6df6 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -578,7 +578,8 @@ public class SubscriptionInfo implements Parcelable {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
try {
- packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ packageInfo = packageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
} catch (PackageManager.NameNotFoundException e) {
Log.d("SubscriptionInfo", "canManageSubscription: Unknown package: " + packageName, e);
return false;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d3cba2e3e889..51de903ed37e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2701,7 +2701,8 @@ public class SubscriptionManager {
PackageManager packageManager = mContext.getPackageManager();
PackageInfo packageInfo;
try {
- packageInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ packageInfo = packageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNING_CERTIFICATES);
} catch (PackageManager.NameNotFoundException e) {
logd("Unknown package: " + packageName);
return false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f25b012389c4..ea55a82cb8fe 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -270,9 +270,6 @@ public class TelephonyManager {
private SubscriptionManager mSubscriptionManager;
private TelephonyScanManager mTelephonyScanManager;
- private static String multiSimConfig =
- SystemProperties.get(TelephonyProperties.PROPERTY_MULTI_SIM_CONFIG);
-
/** Enum indicating multisim variants
* DSDS - Dual SIM Dual Standby
* DSDA - Dual SIM Dual Active
@@ -364,7 +361,6 @@ public class TelephonyManager {
}
}
-
/**
* Returns the number of phones available.
* Returns 0 if none of voice, sms, data is not supported
@@ -397,6 +393,31 @@ public class TelephonyManager {
return phoneCount;
}
+ /**
+ *
+ * Return how many phone / logical modem can be active simultaneously, in terms of device
+ * capability.
+ * For example, for a dual-SIM capable device, it always returns 2, even if only one logical
+ * modem / SIM is active (aka in single SIM mode).
+ *
+ * TODO: b/139642279 publicize and rename.
+ * @hide
+ */
+ public static int getMaxPhoneCount() {
+ // TODO: b/139642279 when turning on this feature, remove dependency of
+ // PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE and always return result based on
+ // PROPERTY_MAX_ACTIVE_MODEMS.
+ String rebootRequired = SystemProperties.get(
+ TelephonyProperties.PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE);
+ if (rebootRequired.equals("false")) {
+ // If no reboot is required, return max possible active modems.
+ return SystemProperties.getInt(
+ TelephonyProperties.PROPERTY_MAX_ACTIVE_MODEMS, getDefault().getPhoneCount());
+ } else {
+ return getDefault().getPhoneCount();
+ }
+ }
+
/** {@hide} */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public static TelephonyManager from(Context context) {
@@ -432,8 +453,7 @@ public class TelephonyManager {
/** {@hide} */
@UnsupportedAppUsage
public boolean isMultiSimEnabled() {
- return (multiSimConfig.equals("dsds") || multiSimConfig.equals("dsda") ||
- multiSimConfig.equals("tsts"));
+ return getPhoneCount() > 1;
}
//
@@ -3251,6 +3271,31 @@ public class TelephonyManager {
}
}
+
+ /**
+ * Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present
+ * on the UICC card.
+ *
+ * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
+ *
+ * @param appType the uicc app type like {@link APPTYPE_CSIM}
+ * @return true if the specified type of application in UICC CARD or false if no uicc or error.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isApplicationOnUicc(@UiccAppType int appType) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.isApplicationOnUicc(getSubId(), appType);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#isApplicationOnUicc", e);
+ }
+ return false;
+ }
+
/**
* Returns a constant indicating the state of the device SIM card in a logical slot.
*
@@ -6600,11 +6645,7 @@ public class TelephonyManager {
public int getSimCount() {
// FIXME Need to get it from Telephony Dev Controller when that gets implemented!
// and then this method shouldn't be used at all!
- if(isMultiSimEnabled()) {
- return getPhoneCount();
- } else {
- return 1;
- }
+ return getPhoneCount();
}
/**
diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index 811722f0bbff..93ccba1dd996 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.pm.PackageInfo;
import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -169,17 +170,28 @@ public final class UiccAccessRule implements Parcelable {
*
* @param packageInfo package info fetched from
* {@link android.content.pm.PackageManager#getPackageInfo}.
- * {@link android.content.pm.PackageManager#GET_SIGNATURES} must have been passed in.
+ * {@link android.content.pm.PackageManager#GET_SIGNING_CERTIFICATES} must have been
+ * passed in.
* @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or
* {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}.
*/
public int getCarrierPrivilegeStatus(PackageInfo packageInfo) {
- if (packageInfo.signatures == null || packageInfo.signatures.length == 0) {
+ Signature[] signatures = packageInfo.signatures;
+ SigningInfo sInfo = packageInfo.signingInfo;
+
+ if (sInfo != null) {
+ signatures = sInfo.getSigningCertificateHistory();
+ if (sInfo.hasMultipleSigners()) {
+ signatures = sInfo.getApkContentsSigners();
+ }
+ }
+
+ if (signatures == null || signatures.length == 0) {
throw new IllegalArgumentException(
- "Must use GET_SIGNATURES when looking up package info");
+ "Must use GET_SIGNING_CERTIFICATES when looking up package info");
}
- for (Signature sig : packageInfo.signatures) {
+ for (Signature sig : signatures) {
int accessStatus = getCarrierPrivilegeStatus(sig, packageInfo.packageName);
if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) {
return accessStatus;
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 77ee20512d11..4ddeb908a200 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -309,6 +309,37 @@ public final class ImsCallProfile implements Parcelable {
public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
/**
+ * The VERSTAT for an incoming call's phone number.
+ */
+ private @VerificationStatus int mCallerNumberVerificationStatus;
+
+ /**
+ * Indicates that the network could not perform verification.
+ */
+ public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0;
+
+ /**
+ * Indicates that verification by the network passed. This indicates there is a high likelihood
+ * that the call originated from a valid source.
+ */
+ public static final int VERIFICATION_STATUS_PASSED = 1;
+
+ /**
+ * Indicates that verification by the network failed. This indicates there is a high likelihood
+ * that the call did not originate from a valid source.
+ */
+ public static final int VERIFICATION_STATUS_FAILED = 2;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "VERIFICATION_STATUS_", value = {
+ VERIFICATION_STATUS_NOT_VERIFIED,
+ VERIFICATION_STATUS_PASSED,
+ VERIFICATION_STATUS_FAILED
+ })
+ public @interface VerificationStatus {}
+
+ /**
* The emergency service categories, only valid if {@link #getServiceType} returns
* {@link #SERVICE_TYPE_EMERGENCY}
*
@@ -539,6 +570,29 @@ public final class ImsCallProfile implements Parcelable {
mMediaProfile = profile.mMediaProfile;
}
+ /**
+ * Sets the verification status for the phone number of an incoming call as identified in
+ * ATIS-1000082.
+ * <p>
+ * The ImsService should parse the verstat information from the SIP INVITE headers for the call
+ * to determine this information. It is typically found in the P-Asserted-Identity OR From
+ * header fields.
+ * @param callerNumberVerificationStatus the new verification status.
+ */
+ public void setCallerNumberVerificationStatus(
+ @VerificationStatus int callerNumberVerificationStatus) {
+ mCallerNumberVerificationStatus = callerNumberVerificationStatus;
+ }
+
+ /**
+ * Gets the verification status for the phone number of an incoming call as identified in
+ * ATIS-1000082.
+ * @return the verification status.
+ */
+ public @VerificationStatus int getCallerNumberVerificationStatus() {
+ return mCallerNumberVerificationStatus;
+ }
+
@NonNull
@Override
public String toString() {
@@ -551,7 +605,8 @@ public final class ImsCallProfile implements Parcelable {
+ ", emergencyCallRouting=" + mEmergencyCallRouting
+ ", emergencyCallTesting=" + mEmergencyCallTesting
+ ", hasKnownUserIntentEmergency=" + mHasKnownUserIntentEmergency
- + ", mRestrictCause=" + mRestrictCause + " }";
+ + ", mRestrictCause=" + mRestrictCause
+ + ", mCallerNumberVerstat= " + mCallerNumberVerificationStatus + " }";
}
@Override
@@ -572,6 +627,7 @@ public final class ImsCallProfile implements Parcelable {
out.writeBoolean(mEmergencyCallTesting);
out.writeBoolean(mHasKnownUserIntentEmergency);
out.writeInt(mRestrictCause);
+ out.writeInt(mCallerNumberVerificationStatus);
}
private void readFromParcel(Parcel in) {
@@ -585,6 +641,7 @@ public final class ImsCallProfile implements Parcelable {
mEmergencyCallTesting = in.readBoolean();
mHasKnownUserIntentEmergency = in.readBoolean();
mRestrictCause = in.readInt();
+ mCallerNumberVerificationStatus = in.readInt();
}
public static final @android.annotation.NonNull Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
diff --git a/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
index 6a35e33f44f9..5aa58c1ee7ee 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl
@@ -21,7 +21,7 @@ package android.telephony.ims.aidl;
* {@hide}
*/
oneway interface IImsSmsListener {
- void onSendSmsResult(int token, int messageRef, int status, int reason);
+ void onSendSmsResult(int token, int messageRef, int status, int reason, int networkErrorCode);
void onSmsStatusReportReceived(int token, in String format, in byte[] pdu);
void onSmsReceived(int token, in String format, in byte[] pdu);
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index 2e4bfb3149be..175769bd34e4 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -118,6 +118,12 @@ public class ImsSmsImplBase {
*/
public static final int STATUS_REPORT_STATUS_ERROR = 2;
+ /**
+ * No network error was generated while processing the SMS message.
+ */
+ // Should match SmsResponse.NO_ERROR_CODE
+ public static final int RESULT_NO_NETWORK_ERROR = -1;
+
// Lock for feature synchronization
private final Object mLock = new Object();
private IImsSmsListener mListener;
@@ -231,16 +237,37 @@ public class ImsSmsImplBase {
}
/**
+ * This method should be triggered by the IMS providers when an outgoing SMS message has been
+ * sent successfully.
+ *
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ *
+ * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+ * connection to the framework is not available. If this happens attempting to send the SMS
+ * should be aborted.
+ */
+ public final void onSendSmsResultSuccess(int token, int messageRef) throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSendSmsResult(token, messageRef, SEND_STATUS_OK,
+ SmsManager.RESULT_ERROR_NONE, RESULT_NO_NETWORK_ERROR);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* This method should be triggered by the IMS providers to pass the result of the sent message
* to the platform.
*
* @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
* @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
- * @param status result of sending the SMS. Valid values are:
- * {@link #SEND_STATUS_OK},
- * {@link #SEND_STATUS_ERROR},
- * {@link #SEND_STATUS_ERROR_RETRY},
- * {@link #SEND_STATUS_ERROR_FALLBACK},
+ * @param status result of sending the SMS.
* @param reason reason in case status is failure. Valid values are:
* {@link SmsManager#RESULT_ERROR_NONE},
* {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
@@ -271,7 +298,11 @@ public class ImsSmsImplBase {
* @throws RuntimeException if called before {@link #onReady()} is triggered or if the
* connection to the framework is not available. If this happens attempting to send the SMS
* should be aborted.
+ * @deprecated Use {@link #onSendSmsResultSuccess(int, int)} or
+ * {@link #onSendSmsResultError(int, int, int, int, int)} to notify the framework of the SMS
+ * send result.
*/
+ @Deprecated
public final void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
int reason) throws RuntimeException {
synchronized (mLock) {
@@ -279,7 +310,65 @@ public class ImsSmsImplBase {
throw new RuntimeException("Feature not ready.");
}
try {
- mListener.onSendSmsResult(token, messageRef, status, reason);
+ mListener.onSendSmsResult(token, messageRef, status, reason,
+ RESULT_NO_NETWORK_ERROR);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * This method should be triggered by the IMS providers when an outgoing message fails to be
+ * sent due to an error generated while processing the message or after being sent to the
+ * network.
+ *
+ * @param token token provided in {@link #sendSms(int, int, String, String, boolean, byte[])}
+ * @param messageRef the message reference. Should be between 0 and 255 per TS.123.040
+ * @param status result of sending the SMS.
+ * @param reason Valid values are:
+ * {@link SmsManager#RESULT_ERROR_NONE},
+ * {@link SmsManager#RESULT_ERROR_GENERIC_FAILURE},
+ * {@link SmsManager#RESULT_ERROR_RADIO_OFF},
+ * {@link SmsManager#RESULT_ERROR_NULL_PDU},
+ * {@link SmsManager#RESULT_ERROR_NO_SERVICE},
+ * {@link SmsManager#RESULT_ERROR_LIMIT_EXCEEDED},
+ * {@link SmsManager#RESULT_ERROR_FDN_CHECK_FAILURE},
+ * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NOT_ALLOWED},
+ * {@link SmsManager#RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED},
+ * {@link SmsManager#RESULT_RADIO_NOT_AVAILABLE},
+ * {@link SmsManager#RESULT_NETWORK_REJECT},
+ * {@link SmsManager#RESULT_INVALID_ARGUMENTS},
+ * {@link SmsManager#RESULT_INVALID_STATE},
+ * {@link SmsManager#RESULT_NO_MEMORY},
+ * {@link SmsManager#RESULT_INVALID_SMS_FORMAT},
+ * {@link SmsManager#RESULT_SYSTEM_ERROR},
+ * {@link SmsManager#RESULT_MODEM_ERROR},
+ * {@link SmsManager#RESULT_NETWORK_ERROR},
+ * {@link SmsManager#RESULT_ENCODING_ERROR},
+ * {@link SmsManager#RESULT_INVALID_SMSC_ADDRESS},
+ * {@link SmsManager#RESULT_OPERATION_NOT_ALLOWED},
+ * {@link SmsManager#RESULT_INTERNAL_ERROR},
+ * {@link SmsManager#RESULT_NO_RESOURCES},
+ * {@link SmsManager#RESULT_CANCELLED},
+ * {@link SmsManager#RESULT_REQUEST_NOT_SUPPORTED}
+ * @param networkErrorCode the error code reported by the carrier network if sending this SMS
+ * has resulted in an error or {@link #RESULT_NO_NETWORK_ERROR} if no network error was
+ * generated. See 3GPP TS 24.011 Section 7.3.4 for valid error codes and more information.
+ *
+ * @throws RuntimeException if called before {@link #onReady()} is triggered or if the
+ * connection to the framework is not available. If this happens attempting to send the SMS
+ * should be aborted.
+ */
+ public final void onSendSmsResultError(int token, int messageRef, @SendStatusResult int status,
+ int reason, int networkErrorCode)
+ throws RuntimeException {
+ synchronized (mLock) {
+ if (mListener == null) {
+ throw new RuntimeException("Feature not ready.");
+ }
+ try {
+ mListener.onSendSmsResult(token, messageRef, status, reason, networkErrorCode);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9f1a2f765eee..866e936a1211 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2012,6 +2012,13 @@ interface ITelephony {
*/
int getRadioHalVersion();
+ /**
+ * Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present
+ * on the UICC card.
+ * @hide
+ */
+ boolean isApplicationOnUicc(int subId, int appType);
+
boolean isModemEnabledForSlot(int slotIndex, String callingPackage);
boolean isDataEnabledForApn(int apnType, int subId, String callingPackage);
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index ef4850716701..4e42c20d28db 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -234,4 +234,11 @@ public interface TelephonyProperties
String DISPLAY_OPPORTUNISTIC_SUBSCRIPTION_CARRIER_TEXT_PROPERTY_NAME =
"persist.radio.display_opportunistic_carrier";
+ /**
+ * How many logical modems can be active simultaneously. For example, if a device is dual-SIM
+ * capable but currently only one SIM slot and one logical modem is active, this value is still
+ * two.
+ * Type: int
+ */
+ static final String PROPERTY_MAX_ACTIVE_MODEMS = "ro.telephony.max.active.modems";
}
diff --git a/telephony/java/com/google/android/mms/ContentType.java b/telephony/java/com/google/android/mms/ContentType.java
new file mode 100644
index 000000000000..12e4b7e26e1e
--- /dev/null
+++ b/telephony/java/com/google/android/mms/ContentType.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.ArrayList;
+
+public class ContentType {
+ public static final String MMS_MESSAGE = "application/vnd.wap.mms-message";
+ // The phony content type for generic PDUs (e.g. ReadOrig.ind,
+ // Notification.ind, Delivery.ind).
+ public static final String MMS_GENERIC = "application/vnd.wap.mms-generic";
+ public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed";
+ public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related";
+ public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative";
+
+ public static final String TEXT_PLAIN = "text/plain";
+ public static final String TEXT_HTML = "text/html";
+ public static final String TEXT_VCALENDAR = "text/x-vCalendar";
+ public static final String TEXT_VCARD = "text/x-vCard";
+
+ public static final String IMAGE_UNSPECIFIED = "image/*";
+ public static final String IMAGE_JPEG = "image/jpeg";
+ public static final String IMAGE_JPG = "image/jpg";
+ public static final String IMAGE_GIF = "image/gif";
+ public static final String IMAGE_WBMP = "image/vnd.wap.wbmp";
+ public static final String IMAGE_PNG = "image/png";
+ public static final String IMAGE_X_MS_BMP = "image/x-ms-bmp";
+
+ public static final String AUDIO_UNSPECIFIED = "audio/*";
+ public static final String AUDIO_AAC = "audio/aac";
+ public static final String AUDIO_AMR = "audio/amr";
+ public static final String AUDIO_IMELODY = "audio/imelody";
+ public static final String AUDIO_MID = "audio/mid";
+ public static final String AUDIO_MIDI = "audio/midi";
+ public static final String AUDIO_MP3 = "audio/mp3";
+ public static final String AUDIO_MPEG3 = "audio/mpeg3";
+ public static final String AUDIO_MPEG = "audio/mpeg";
+ public static final String AUDIO_MPG = "audio/mpg";
+ public static final String AUDIO_MP4 = "audio/mp4";
+ public static final String AUDIO_X_MID = "audio/x-mid";
+ public static final String AUDIO_X_MIDI = "audio/x-midi";
+ public static final String AUDIO_X_MP3 = "audio/x-mp3";
+ public static final String AUDIO_X_MPEG3 = "audio/x-mpeg3";
+ public static final String AUDIO_X_MPEG = "audio/x-mpeg";
+ public static final String AUDIO_X_MPG = "audio/x-mpg";
+ public static final String AUDIO_3GPP = "audio/3gpp";
+ public static final String AUDIO_X_WAV = "audio/x-wav";
+ public static final String AUDIO_OGG = "application/ogg";
+ public static final String AUDIO_OGG2 = "audio/ogg";
+
+ public static final String VIDEO_UNSPECIFIED = "video/*";
+ public static final String VIDEO_3GPP = "video/3gpp";
+ public static final String VIDEO_3G2 = "video/3gpp2";
+ public static final String VIDEO_H263 = "video/h263";
+ public static final String VIDEO_MP4 = "video/mp4";
+
+ public static final String APP_SMIL = "application/smil";
+ public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml";
+ public static final String APP_XHTML = "application/xhtml+xml";
+
+ public static final String APP_DRM_CONTENT = "application/vnd.oma.drm.content";
+ public static final String APP_DRM_MESSAGE = "application/vnd.oma.drm.message";
+
+ private static final ArrayList<String> sSupportedContentTypes = new ArrayList<String>();
+ private static final ArrayList<String> sSupportedImageTypes = new ArrayList<String>();
+ private static final ArrayList<String> sSupportedAudioTypes = new ArrayList<String>();
+ private static final ArrayList<String> sSupportedVideoTypes = new ArrayList<String>();
+
+ static {
+ sSupportedContentTypes.add(TEXT_PLAIN);
+ sSupportedContentTypes.add(TEXT_HTML);
+ sSupportedContentTypes.add(TEXT_VCALENDAR);
+ sSupportedContentTypes.add(TEXT_VCARD);
+
+ sSupportedContentTypes.add(IMAGE_JPEG);
+ sSupportedContentTypes.add(IMAGE_GIF);
+ sSupportedContentTypes.add(IMAGE_WBMP);
+ sSupportedContentTypes.add(IMAGE_PNG);
+ sSupportedContentTypes.add(IMAGE_JPG);
+ sSupportedContentTypes.add(IMAGE_X_MS_BMP);
+ //supportedContentTypes.add(IMAGE_SVG); not yet supported.
+
+ sSupportedContentTypes.add(AUDIO_AAC);
+ sSupportedContentTypes.add(AUDIO_AMR);
+ sSupportedContentTypes.add(AUDIO_IMELODY);
+ sSupportedContentTypes.add(AUDIO_MID);
+ sSupportedContentTypes.add(AUDIO_MIDI);
+ sSupportedContentTypes.add(AUDIO_MP3);
+ sSupportedContentTypes.add(AUDIO_MP4);
+ sSupportedContentTypes.add(AUDIO_MPEG3);
+ sSupportedContentTypes.add(AUDIO_MPEG);
+ sSupportedContentTypes.add(AUDIO_MPG);
+ sSupportedContentTypes.add(AUDIO_X_MID);
+ sSupportedContentTypes.add(AUDIO_X_MIDI);
+ sSupportedContentTypes.add(AUDIO_X_MP3);
+ sSupportedContentTypes.add(AUDIO_X_MPEG3);
+ sSupportedContentTypes.add(AUDIO_X_MPEG);
+ sSupportedContentTypes.add(AUDIO_X_MPG);
+ sSupportedContentTypes.add(AUDIO_X_WAV);
+ sSupportedContentTypes.add(AUDIO_3GPP);
+ sSupportedContentTypes.add(AUDIO_OGG);
+ sSupportedContentTypes.add(AUDIO_OGG2);
+
+ sSupportedContentTypes.add(VIDEO_3GPP);
+ sSupportedContentTypes.add(VIDEO_3G2);
+ sSupportedContentTypes.add(VIDEO_H263);
+ sSupportedContentTypes.add(VIDEO_MP4);
+
+ sSupportedContentTypes.add(APP_SMIL);
+ sSupportedContentTypes.add(APP_WAP_XHTML);
+ sSupportedContentTypes.add(APP_XHTML);
+
+ sSupportedContentTypes.add(APP_DRM_CONTENT);
+ sSupportedContentTypes.add(APP_DRM_MESSAGE);
+
+ // add supported image types
+ sSupportedImageTypes.add(IMAGE_JPEG);
+ sSupportedImageTypes.add(IMAGE_GIF);
+ sSupportedImageTypes.add(IMAGE_WBMP);
+ sSupportedImageTypes.add(IMAGE_PNG);
+ sSupportedImageTypes.add(IMAGE_JPG);
+ sSupportedImageTypes.add(IMAGE_X_MS_BMP);
+
+ // add supported audio types
+ sSupportedAudioTypes.add(AUDIO_AAC);
+ sSupportedAudioTypes.add(AUDIO_AMR);
+ sSupportedAudioTypes.add(AUDIO_IMELODY);
+ sSupportedAudioTypes.add(AUDIO_MID);
+ sSupportedAudioTypes.add(AUDIO_MIDI);
+ sSupportedAudioTypes.add(AUDIO_MP3);
+ sSupportedAudioTypes.add(AUDIO_MPEG3);
+ sSupportedAudioTypes.add(AUDIO_MPEG);
+ sSupportedAudioTypes.add(AUDIO_MPG);
+ sSupportedAudioTypes.add(AUDIO_MP4);
+ sSupportedAudioTypes.add(AUDIO_X_MID);
+ sSupportedAudioTypes.add(AUDIO_X_MIDI);
+ sSupportedAudioTypes.add(AUDIO_X_MP3);
+ sSupportedAudioTypes.add(AUDIO_X_MPEG3);
+ sSupportedAudioTypes.add(AUDIO_X_MPEG);
+ sSupportedAudioTypes.add(AUDIO_X_MPG);
+ sSupportedAudioTypes.add(AUDIO_X_WAV);
+ sSupportedAudioTypes.add(AUDIO_3GPP);
+ sSupportedAudioTypes.add(AUDIO_OGG);
+ sSupportedAudioTypes.add(AUDIO_OGG2);
+
+ // add supported video types
+ sSupportedVideoTypes.add(VIDEO_3GPP);
+ sSupportedVideoTypes.add(VIDEO_3G2);
+ sSupportedVideoTypes.add(VIDEO_H263);
+ sSupportedVideoTypes.add(VIDEO_MP4);
+ }
+
+ // This class should never be instantiated.
+ private ContentType() {
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedType(String contentType) {
+ return (null != contentType) && sSupportedContentTypes.contains(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedImageType(String contentType) {
+ return isImageType(contentType) && isSupportedType(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedAudioType(String contentType) {
+ return isAudioType(contentType) && isSupportedType(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isSupportedVideoType(String contentType) {
+ return isVideoType(contentType) && isSupportedType(contentType);
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isTextType(String contentType) {
+ return (null != contentType) && contentType.startsWith("text/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isImageType(String contentType) {
+ return (null != contentType) && contentType.startsWith("image/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isAudioType(String contentType) {
+ return (null != contentType) && contentType.startsWith("audio/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isVideoType(String contentType) {
+ return (null != contentType) && contentType.startsWith("video/");
+ }
+
+ @UnsupportedAppUsage
+ public static boolean isDrmType(String contentType) {
+ return (null != contentType)
+ && (contentType.equals(APP_DRM_CONTENT)
+ || contentType.equals(APP_DRM_MESSAGE));
+ }
+
+ public static boolean isUnspecified(String contentType) {
+ return (null != contentType) && contentType.endsWith("*");
+ }
+
+ @UnsupportedAppUsage
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getImageTypes() {
+ return (ArrayList<String>) sSupportedImageTypes.clone();
+ }
+
+ @UnsupportedAppUsage
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getAudioTypes() {
+ return (ArrayList<String>) sSupportedAudioTypes.clone();
+ }
+
+ @UnsupportedAppUsage
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getVideoTypes() {
+ return (ArrayList<String>) sSupportedVideoTypes.clone();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static ArrayList<String> getSupportedTypes() {
+ return (ArrayList<String>) sSupportedContentTypes.clone();
+ }
+}
diff --git a/telephony/java/com/google/android/mms/InvalidHeaderValueException.java b/telephony/java/com/google/android/mms/InvalidHeaderValueException.java
new file mode 100644
index 000000000000..2836c3075b3b
--- /dev/null
+++ b/telephony/java/com/google/android/mms/InvalidHeaderValueException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+/**
+ * Thrown when an invalid header value was set.
+ */
+public class InvalidHeaderValueException extends MmsException {
+ private static final long serialVersionUID = -2053384496042052262L;
+
+ /**
+ * Constructs an InvalidHeaderValueException with no detailed message.
+ */
+ public InvalidHeaderValueException() {
+ super();
+ }
+
+ /**
+ * Constructs an InvalidHeaderValueException with the specified detailed message.
+ *
+ * @param message the detailed message.
+ */
+ @UnsupportedAppUsage
+ public InvalidHeaderValueException(String message) {
+ super(message);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/MmsException.java b/telephony/java/com/google/android/mms/MmsException.java
new file mode 100644
index 000000000000..5be33ed1fac9
--- /dev/null
+++ b/telephony/java/com/google/android/mms/MmsException.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+/**
+ * A generic exception that is thrown by the Mms client.
+ */
+public class MmsException extends Exception {
+ private static final long serialVersionUID = -7323249827281485390L;
+
+ /**
+ * Creates a new MmsException.
+ */
+ @UnsupportedAppUsage
+ public MmsException() {
+ super();
+ }
+
+ /**
+ * Creates a new MmsException with the specified detail message.
+ *
+ * @param message the detail message.
+ */
+ @UnsupportedAppUsage
+ public MmsException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new MmsException with the specified cause.
+ *
+ * @param cause the cause.
+ */
+ @UnsupportedAppUsage
+ public MmsException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new MmsException with the specified detail message and cause.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ @UnsupportedAppUsage
+ public MmsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/package.html b/telephony/java/com/google/android/mms/package.html
new file mode 100755
index 000000000000..c9f96a66ab3b
--- /dev/null
+++ b/telephony/java/com/google/android/mms/package.html
@@ -0,0 +1,5 @@
+<body>
+
+{@hide}
+
+</body>
diff --git a/telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java b/telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java
new file mode 100644
index 000000000000..ae447d7a7417
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/AcknowledgeInd.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Acknowledge.ind PDU.
+ */
+public class AcknowledgeInd extends GenericPdu {
+ /**
+ * Constructor, used when composing a M-Acknowledge.ind pdu.
+ *
+ * @param mmsVersion current viersion of mms
+ * @param transactionId the transaction-id value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if transactionId is null.
+ */
+ @UnsupportedAppUsage
+ public AcknowledgeInd(int mmsVersion, byte[] transactionId)
+ throws InvalidHeaderValueException {
+ super();
+
+ setMessageType(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND);
+ setMmsVersion(mmsVersion);
+ setTransactionId(transactionId);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ AcknowledgeInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get X-Mms-Report-Allowed field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public int getReportAllowed() {
+ return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Set X-Mms-Report-Allowed field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setReportAllowed(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/Base64.java b/telephony/java/com/google/android/mms/pdu/Base64.java
new file mode 100644
index 000000000000..483fa7f9842e
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/Base64.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public class Base64 {
+ /**
+ * Used to get the number of Quadruples.
+ */
+ static final int FOURBYTE = 4;
+
+ /**
+ * Byte used to pad output.
+ */
+ static final byte PAD = (byte) '=';
+
+ /**
+ * The base length.
+ */
+ static final int BASELENGTH = 255;
+
+ // Create arrays to hold the base64 characters
+ private static byte[] base64Alphabet = new byte[BASELENGTH];
+
+ // Populating the character arrays
+ static {
+ for (int i = 0; i < BASELENGTH; i++) {
+ base64Alphabet[i] = (byte) -1;
+ }
+ for (int i = 'Z'; i >= 'A'; i--) {
+ base64Alphabet[i] = (byte) (i - 'A');
+ }
+ for (int i = 'z'; i >= 'a'; i--) {
+ base64Alphabet[i] = (byte) (i - 'a' + 26);
+ }
+ for (int i = '9'; i >= '0'; i--) {
+ base64Alphabet[i] = (byte) (i - '0' + 52);
+ }
+
+ base64Alphabet['+'] = 62;
+ base64Alphabet['/'] = 63;
+ }
+
+ /**
+ * Decodes Base64 data into octects
+ *
+ * @param base64Data Byte array containing Base64 data
+ * @return Array containing decoded data.
+ */
+ @UnsupportedAppUsage
+ public static byte[] decodeBase64(byte[] base64Data) {
+ // RFC 2045 requires that we discard ALL non-Base64 characters
+ base64Data = discardNonBase64(base64Data);
+
+ // handle the edge case, so we don't have to worry about it later
+ if (base64Data.length == 0) {
+ return new byte[0];
+ }
+
+ int numberQuadruple = base64Data.length / FOURBYTE;
+ byte decodedData[] = null;
+ byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0;
+
+ // Throw away anything not in base64Data
+
+ int encodedIndex = 0;
+ int dataIndex = 0;
+ {
+ // this sizes the output array properly - rlw
+ int lastData = base64Data.length;
+ // ignore the '=' padding
+ while (base64Data[lastData - 1] == PAD) {
+ if (--lastData == 0) {
+ return new byte[0];
+ }
+ }
+ decodedData = new byte[lastData - numberQuadruple];
+ }
+
+ for (int i = 0; i < numberQuadruple; i++) {
+ dataIndex = i * 4;
+ marker0 = base64Data[dataIndex + 2];
+ marker1 = base64Data[dataIndex + 3];
+
+ b1 = base64Alphabet[base64Data[dataIndex]];
+ b2 = base64Alphabet[base64Data[dataIndex + 1]];
+
+ if (marker0 != PAD && marker1 != PAD) {
+ //No PAD e.g 3cQl
+ b3 = base64Alphabet[marker0];
+ b4 = base64Alphabet[marker1];
+
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ decodedData[encodedIndex + 1] =
+ (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+ decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4);
+ } else if (marker0 == PAD) {
+ //Two PAD e.g. 3c[Pad][Pad]
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ } else if (marker1 == PAD) {
+ //One PAD e.g. 3cQ[Pad]
+ b3 = base64Alphabet[marker0];
+
+ decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
+ decodedData[encodedIndex + 1] =
+ (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
+ }
+ encodedIndex += 3;
+ }
+ return decodedData;
+ }
+
+ /**
+ * Check octect whether it is a base64 encoding.
+ *
+ * @param octect to be checked byte
+ * @return ture if it is base64 encoding, false otherwise.
+ */
+ private static boolean isBase64(byte octect) {
+ if (octect == PAD) {
+ return true;
+ } else if (base64Alphabet[octect] == -1) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Discards any characters outside of the base64 alphabet, per
+ * the requirements on page 25 of RFC 2045 - "Any characters
+ * outside of the base64 alphabet are to be ignored in base64
+ * encoded data."
+ *
+ * @param data The base-64 encoded data to groom
+ * @return The data, less non-base64 characters (see RFC 2045).
+ */
+ static byte[] discardNonBase64(byte[] data) {
+ byte groomedData[] = new byte[data.length];
+ int bytesCopied = 0;
+
+ for (int i = 0; i < data.length; i++) {
+ if (isBase64(data[i])) {
+ groomedData[bytesCopied++] = data[i];
+ }
+ }
+
+ byte packedData[] = new byte[bytesCopied];
+
+ System.arraycopy(groomedData, 0, packedData, 0, bytesCopied);
+
+ return packedData;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/CharacterSets.java b/telephony/java/com/google/android/mms/pdu/CharacterSets.java
new file mode 100644
index 000000000000..27da35e2d928
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/CharacterSets.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+
+public class CharacterSets {
+ /**
+ * IANA assigned MIB enum numbers.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * Any-charset = <Octet 128>
+ * Equivalent to the special RFC2616 charset value "*"
+ */
+ public static final int ANY_CHARSET = 0x00;
+ public static final int US_ASCII = 0x03;
+ public static final int ISO_8859_1 = 0x04;
+ public static final int ISO_8859_2 = 0x05;
+ public static final int ISO_8859_3 = 0x06;
+ public static final int ISO_8859_4 = 0x07;
+ public static final int ISO_8859_5 = 0x08;
+ public static final int ISO_8859_6 = 0x09;
+ public static final int ISO_8859_7 = 0x0A;
+ public static final int ISO_8859_8 = 0x0B;
+ public static final int ISO_8859_9 = 0x0C;
+ public static final int SHIFT_JIS = 0x11;
+ public static final int UTF_8 = 0x6A;
+ public static final int BIG5 = 0x07EA;
+ public static final int UCS2 = 0x03E8;
+ public static final int UTF_16 = 0x03F7;
+
+ /**
+ * If the encoding of given data is unsupported, use UTF_8 to decode it.
+ */
+ public static final int DEFAULT_CHARSET = UTF_8;
+
+ /**
+ * Array of MIB enum numbers.
+ */
+ private static final int[] MIBENUM_NUMBERS = {
+ ANY_CHARSET,
+ US_ASCII,
+ ISO_8859_1,
+ ISO_8859_2,
+ ISO_8859_3,
+ ISO_8859_4,
+ ISO_8859_5,
+ ISO_8859_6,
+ ISO_8859_7,
+ ISO_8859_8,
+ ISO_8859_9,
+ SHIFT_JIS,
+ UTF_8,
+ BIG5,
+ UCS2,
+ UTF_16,
+ };
+
+ /**
+ * The Well-known-charset Mime name.
+ */
+ public static final String MIMENAME_ANY_CHARSET = "*";
+ public static final String MIMENAME_US_ASCII = "us-ascii";
+ public static final String MIMENAME_ISO_8859_1 = "iso-8859-1";
+ public static final String MIMENAME_ISO_8859_2 = "iso-8859-2";
+ public static final String MIMENAME_ISO_8859_3 = "iso-8859-3";
+ public static final String MIMENAME_ISO_8859_4 = "iso-8859-4";
+ public static final String MIMENAME_ISO_8859_5 = "iso-8859-5";
+ public static final String MIMENAME_ISO_8859_6 = "iso-8859-6";
+ public static final String MIMENAME_ISO_8859_7 = "iso-8859-7";
+ public static final String MIMENAME_ISO_8859_8 = "iso-8859-8";
+ public static final String MIMENAME_ISO_8859_9 = "iso-8859-9";
+ public static final String MIMENAME_SHIFT_JIS = "shift_JIS";
+ public static final String MIMENAME_UTF_8 = "utf-8";
+ public static final String MIMENAME_BIG5 = "big5";
+ public static final String MIMENAME_UCS2 = "iso-10646-ucs-2";
+ public static final String MIMENAME_UTF_16 = "utf-16";
+
+ public static final String DEFAULT_CHARSET_NAME = MIMENAME_UTF_8;
+
+ /**
+ * Array of the names of character sets.
+ */
+ private static final String[] MIME_NAMES = {
+ MIMENAME_ANY_CHARSET,
+ MIMENAME_US_ASCII,
+ MIMENAME_ISO_8859_1,
+ MIMENAME_ISO_8859_2,
+ MIMENAME_ISO_8859_3,
+ MIMENAME_ISO_8859_4,
+ MIMENAME_ISO_8859_5,
+ MIMENAME_ISO_8859_6,
+ MIMENAME_ISO_8859_7,
+ MIMENAME_ISO_8859_8,
+ MIMENAME_ISO_8859_9,
+ MIMENAME_SHIFT_JIS,
+ MIMENAME_UTF_8,
+ MIMENAME_BIG5,
+ MIMENAME_UCS2,
+ MIMENAME_UTF_16,
+ };
+
+ private static final HashMap<Integer, String> MIBENUM_TO_NAME_MAP;
+ private static final HashMap<String, Integer> NAME_TO_MIBENUM_MAP;
+
+ static {
+ // Create the HashMaps.
+ MIBENUM_TO_NAME_MAP = new HashMap<Integer, String>();
+ NAME_TO_MIBENUM_MAP = new HashMap<String, Integer>();
+ assert(MIBENUM_NUMBERS.length == MIME_NAMES.length);
+ int count = MIBENUM_NUMBERS.length - 1;
+ for(int i = 0; i <= count; i++) {
+ MIBENUM_TO_NAME_MAP.put(MIBENUM_NUMBERS[i], MIME_NAMES[i]);
+ NAME_TO_MIBENUM_MAP.put(MIME_NAMES[i], MIBENUM_NUMBERS[i]);
+ }
+ }
+
+ private CharacterSets() {} // Non-instantiatable
+
+ /**
+ * Map an MIBEnum number to the name of the charset which this number
+ * is assigned to by IANA.
+ *
+ * @param mibEnumValue An IANA assigned MIBEnum number.
+ * @return The name string of the charset.
+ * @throws UnsupportedEncodingException
+ */
+ @UnsupportedAppUsage
+ public static String getMimeName(int mibEnumValue)
+ throws UnsupportedEncodingException {
+ String name = MIBENUM_TO_NAME_MAP.get(mibEnumValue);
+ if (name == null) {
+ throw new UnsupportedEncodingException();
+ }
+ return name;
+ }
+
+ /**
+ * Map a well-known charset name to its assigned MIBEnum number.
+ *
+ * @param mimeName The charset name.
+ * @return The MIBEnum number assigned by IANA for this charset.
+ * @throws UnsupportedEncodingException
+ */
+ @UnsupportedAppUsage
+ public static int getMibEnumValue(String mimeName)
+ throws UnsupportedEncodingException {
+ if(null == mimeName) {
+ return -1;
+ }
+
+ Integer mibEnumValue = NAME_TO_MIBENUM_MAP.get(mimeName);
+ if (mibEnumValue == null) {
+ throw new UnsupportedEncodingException();
+ }
+ return mibEnumValue;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/DeliveryInd.java b/telephony/java/com/google/android/mms/pdu/DeliveryInd.java
new file mode 100644
index 000000000000..7093ac63338c
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/DeliveryInd.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Delivery.Ind Pdu.
+ */
+public class DeliveryInd extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ public DeliveryInd() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_DELIVERY_IND);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ DeliveryInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value.
+ *
+ * @param value the value
+ */
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value, should not be null
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get Status value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getStatus() {
+ return mPduHeaders.getOctet(PduHeaders.STATUS);
+ }
+
+ /**
+ * Set Status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.STATUS);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * set To value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public EncodedStringValue getStatusText() {return null;}
+ * public void setStatusText(EncodedStringValue value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/EncodedStringValue.java b/telephony/java/com/google/android/mms/pdu/EncodedStringValue.java
new file mode 100644
index 000000000000..41662750842f
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/EncodedStringValue.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+
+/**
+ * Encoded-string-value = Text-string | Value-length Char-set Text-string
+ */
+public class EncodedStringValue implements Cloneable {
+ private static final String TAG = "EncodedStringValue";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ /**
+ * The Char-set value.
+ */
+ private int mCharacterSet;
+
+ /**
+ * The Text-string value.
+ */
+ private byte[] mData;
+
+ /**
+ * Constructor.
+ *
+ * @param charset the Char-set value
+ * @param data the Text-string value
+ * @throws NullPointerException if Text-string value is null.
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue(int charset, byte[] data) {
+ // TODO: CharSet needs to be validated against MIBEnum.
+ if(null == data) {
+ throw new NullPointerException("EncodedStringValue: Text-string is null.");
+ }
+
+ mCharacterSet = charset;
+ mData = new byte[data.length];
+ System.arraycopy(data, 0, mData, 0, data.length);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param data the Text-string value
+ * @throws NullPointerException if Text-string value is null.
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue(byte[] data) {
+ this(CharacterSets.DEFAULT_CHARSET, data);
+ }
+
+ @UnsupportedAppUsage
+ public EncodedStringValue(String data) {
+ try {
+ mData = data.getBytes(CharacterSets.DEFAULT_CHARSET_NAME);
+ mCharacterSet = CharacterSets.DEFAULT_CHARSET;
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "Default encoding must be supported.", e);
+ }
+ }
+
+ /**
+ * Get Char-set value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getCharacterSet() {
+ return mCharacterSet;
+ }
+
+ /**
+ * Set Char-set value.
+ *
+ * @param charset the Char-set value
+ */
+ @UnsupportedAppUsage
+ public void setCharacterSet(int charset) {
+ // TODO: CharSet needs to be validated against MIBEnum.
+ mCharacterSet = charset;
+ }
+
+ /**
+ * Get Text-string value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTextString() {
+ byte[] byteArray = new byte[mData.length];
+
+ System.arraycopy(mData, 0, byteArray, 0, mData.length);
+ return byteArray;
+ }
+
+ /**
+ * Set Text-string value.
+ *
+ * @param textString the Text-string value
+ * @throws NullPointerException if Text-string value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTextString(byte[] textString) {
+ if(null == textString) {
+ throw new NullPointerException("EncodedStringValue: Text-string is null.");
+ }
+
+ mData = new byte[textString.length];
+ System.arraycopy(textString, 0, mData, 0, textString.length);
+ }
+
+ /**
+ * Convert this object to a {@link java.lang.String}. If the encoding of
+ * the EncodedStringValue is null or unsupported, it will be
+ * treated as iso-8859-1 encoding.
+ *
+ * @return The decoded String.
+ */
+ @UnsupportedAppUsage
+ public String getString() {
+ if (CharacterSets.ANY_CHARSET == mCharacterSet) {
+ return new String(mData); // system default encoding.
+ } else {
+ try {
+ String name = CharacterSets.getMimeName(mCharacterSet);
+ return new String(mData, name);
+ } catch (UnsupportedEncodingException e) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, e.getMessage(), e);
+ }
+ try {
+ return new String(mData, CharacterSets.MIMENAME_ISO_8859_1);
+ } catch (UnsupportedEncodingException e2) {
+ return new String(mData); // system default encoding.
+ }
+ }
+ }
+ }
+
+ /**
+ * Append to Text-string.
+ *
+ * @param textString the textString to append
+ * @throws NullPointerException if the text String is null
+ * or an IOException occurred.
+ */
+ @UnsupportedAppUsage
+ public void appendTextString(byte[] textString) {
+ if(null == textString) {
+ throw new NullPointerException("Text-string is null.");
+ }
+
+ if(null == mData) {
+ mData = new byte[textString.length];
+ System.arraycopy(textString, 0, mData, 0, textString.length);
+ } else {
+ ByteArrayOutputStream newTextString = new ByteArrayOutputStream();
+ try {
+ newTextString.write(mData);
+ newTextString.write(textString);
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new NullPointerException(
+ "appendTextString: failed when write a new Text-string");
+ }
+
+ mData = newTextString.toByteArray();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ super.clone();
+ int len = mData.length;
+ byte[] dstBytes = new byte[len];
+ System.arraycopy(mData, 0, dstBytes, 0, len);
+
+ try {
+ return new EncodedStringValue(mCharacterSet, dstBytes);
+ } catch (Exception e) {
+ Log.e(TAG, "failed to clone an EncodedStringValue: " + this);
+ e.printStackTrace();
+ throw new CloneNotSupportedException(e.getMessage());
+ }
+ }
+
+ /**
+ * Split this encoded string around matches of the given pattern.
+ *
+ * @param pattern the delimiting pattern
+ * @return the array of encoded strings computed by splitting this encoded
+ * string around matches of the given pattern
+ */
+ public EncodedStringValue[] split(String pattern) {
+ String[] temp = getString().split(pattern);
+ EncodedStringValue[] ret = new EncodedStringValue[temp.length];
+ for (int i = 0; i < ret.length; ++i) {
+ try {
+ ret[i] = new EncodedStringValue(mCharacterSet,
+ temp[i].getBytes());
+ } catch (NullPointerException e) {
+ // Can't arrive here
+ return null;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Extract an EncodedStringValue[] from a given String.
+ */
+ @UnsupportedAppUsage
+ public static EncodedStringValue[] extract(String src) {
+ String[] values = src.split(";");
+
+ ArrayList<EncodedStringValue> list = new ArrayList<EncodedStringValue>();
+ for (int i = 0; i < values.length; i++) {
+ if (values[i].length() > 0) {
+ list.add(new EncodedStringValue(values[i]));
+ }
+ }
+
+ int len = list.size();
+ if (len > 0) {
+ return list.toArray(new EncodedStringValue[len]);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Concatenate an EncodedStringValue[] into a single String.
+ */
+ @UnsupportedAppUsage
+ public static String concat(EncodedStringValue[] addr) {
+ StringBuilder sb = new StringBuilder();
+ int maxIndex = addr.length - 1;
+ for (int i = 0; i <= maxIndex; i++) {
+ sb.append(addr[i].getString());
+ if (i < maxIndex) {
+ sb.append(";");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ @UnsupportedAppUsage
+ public static EncodedStringValue copy(EncodedStringValue value) {
+ if (value == null) {
+ return null;
+ }
+
+ return new EncodedStringValue(value.mCharacterSet, value.mData);
+ }
+
+ @UnsupportedAppUsage
+ public static EncodedStringValue[] encodeStrings(String[] array) {
+ int count = array.length;
+ if (count > 0) {
+ EncodedStringValue[] encodedArray = new EncodedStringValue[count];
+ for (int i = 0; i < count; i++) {
+ encodedArray[i] = new EncodedStringValue(array[i]);
+ }
+ return encodedArray;
+ }
+ return null;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/GenericPdu.java b/telephony/java/com/google/android/mms/pdu/GenericPdu.java
new file mode 100644
index 000000000000..ebf16ac7e632
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/GenericPdu.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class GenericPdu {
+ /**
+ * The headers of pdu.
+ */
+ @UnsupportedAppUsage
+ PduHeaders mPduHeaders = null;
+
+ /**
+ * Constructor.
+ */
+ @UnsupportedAppUsage
+ public GenericPdu() {
+ mPduHeaders = new PduHeaders();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param headers Headers for this PDU.
+ */
+ GenericPdu(PduHeaders headers) {
+ mPduHeaders = headers;
+ }
+
+ /**
+ * Get the headers of this PDU.
+ *
+ * @return A PduHeaders of this PDU.
+ */
+ @UnsupportedAppUsage
+ PduHeaders getPduHeaders() {
+ return mPduHeaders;
+ }
+
+ /**
+ * Get X-Mms-Message-Type field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ @UnsupportedAppUsage
+ public int getMessageType() {
+ return mPduHeaders.getOctet(PduHeaders.MESSAGE_TYPE);
+ }
+
+ /**
+ * Set X-Mms-Message-Type field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if field's value is not Octet.
+ */
+ @UnsupportedAppUsage
+ public void setMessageType(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.MESSAGE_TYPE);
+ }
+
+ /**
+ * Get X-Mms-MMS-Version field value.
+ *
+ * @return the X-Mms-MMS-Version value
+ */
+ public int getMmsVersion() {
+ return mPduHeaders.getOctet(PduHeaders.MMS_VERSION);
+ }
+
+ /**
+ * Set X-Mms-MMS-Version field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if field's value is not Octet.
+ */
+ public void setMmsVersion(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.MMS_VERSION);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java b/telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java
new file mode 100644
index 000000000000..e108f7600baf
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/MultimediaMessagePdu.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * Multimedia message PDU.
+ */
+public class MultimediaMessagePdu extends GenericPdu{
+ /**
+ * The body.
+ */
+ private PduBody mMessageBody;
+
+ /**
+ * Constructor.
+ */
+ @UnsupportedAppUsage
+ public MultimediaMessagePdu() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param header the header of this PDU
+ * @param body the body of this PDU
+ */
+ @UnsupportedAppUsage
+ public MultimediaMessagePdu(PduHeaders header, PduBody body) {
+ super(header);
+ mMessageBody = body;
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ MultimediaMessagePdu(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get body of the PDU.
+ *
+ * @return the body
+ */
+ @UnsupportedAppUsage
+ public PduBody getBody() {
+ return mMessageBody;
+ }
+
+ /**
+ * Set body of the PDU.
+ *
+ * @param body the body
+ */
+ @UnsupportedAppUsage
+ public void setBody(PduBody body) {
+ mMessageBody = body;
+ }
+
+ /**
+ * Get subject.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getSubject() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Set subject.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setSubject(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * Add a "To" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addTo(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.TO);
+ }
+
+ /**
+ * Get X-Mms-Priority value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getPriority() {
+ return mPduHeaders.getOctet(PduHeaders.PRIORITY);
+ }
+
+ /**
+ * Set X-Mms-Priority value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setPriority(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.PRIORITY);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value in seconds.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/NotificationInd.java b/telephony/java/com/google/android/mms/pdu/NotificationInd.java
new file mode 100644
index 000000000000..b561bd4ab3a7
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/NotificationInd.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Notification.ind PDU.
+ */
+public class NotificationInd extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public NotificationInd() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ NotificationInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get X-Mms-Content-Class Value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getContentClass() {
+ return mPduHeaders.getOctet(PduHeaders.CONTENT_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Content-Class Value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setContentClass(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.CONTENT_CLASS);
+ }
+
+ /**
+ * Get X-Mms-Content-Location value.
+ * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf:
+ * Content-location-value = Uri-value
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentLocation() {
+ return mPduHeaders.getTextString(PduHeaders.CONTENT_LOCATION);
+ }
+
+ /**
+ * Set X-Mms-Content-Location value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setContentLocation(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.CONTENT_LOCATION);
+ }
+
+ /**
+ * Get X-Mms-Expiry value.
+ *
+ * Expiry-value = Value-length
+ * (Absolute-token Date-value | Relative-token Delta-seconds-value)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getExpiry() {
+ return mPduHeaders.getLongInteger(PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Set X-Mms-Expiry value.
+ *
+ * @param value the value
+ * @throws RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setExpiry(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+
+ /**
+ * Get X-Mms-Message-Class value.
+ * Message-class-value = Class-identifier | Token-text
+ * Class-identifier = Personal | Advertisement | Informational | Auto
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageClass() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Message-Class value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setMessageClass(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Get X-Mms-Message-Size value.
+ * Message-size-value = Long-integer
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getMessageSize() {
+ return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Set X-Mms-Message-Size value.
+ *
+ * @param value the value
+ * @throws RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setMessageSize(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Get subject.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getSubject() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Set subject.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setSubject(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Get X-Mms-Delivery-Report Value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getDeliveryReport() {
+ return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Delivery-Report Value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setDeliveryReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte getDrmContent() {return 0x00;}
+ * public void setDrmContent(byte value) {}
+ *
+ * public byte getDistributionIndicator() {return 0x00;}
+ * public void setDistributionIndicator(byte value) {}
+ *
+ * public ElementDescriptorValue getElementDescriptor() {return null;}
+ * public void getElementDescriptor(ElementDescriptorValue value) {}
+ *
+ * public byte getPriority() {return 0x00;}
+ * public void setPriority(byte value) {}
+ *
+ * public byte getRecommendedRetrievalMode() {return 0x00;}
+ * public void setRecommendedRetrievalMode(byte value) {}
+ *
+ * public byte getRecommendedRetrievalModeText() {return 0x00;}
+ * public void setRecommendedRetrievalModeText(byte value) {}
+ *
+ * public byte[] getReplaceId() {return 0x00;}
+ * public void setReplaceId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getReplyCharging() {return 0x00;}
+ * public void setReplyCharging(byte value) {}
+ *
+ * public byte getReplyChargingDeadline() {return 0x00;}
+ * public void setReplyChargingDeadline(byte value) {}
+ *
+ * public byte[] getReplyChargingId() {return 0x00;}
+ * public void setReplyChargingId(byte[] value) {}
+ *
+ * public long getReplyChargingSize() {return 0;}
+ * public void setReplyChargingSize(long value) {}
+ *
+ * public byte getStored() {return 0x00;}
+ * public void setStored(byte value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/NotifyRespInd.java b/telephony/java/com/google/android/mms/pdu/NotifyRespInd.java
new file mode 100644
index 000000000000..3c70f86a0890
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/NotifyRespInd.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-NofifyResp.ind PDU.
+ */
+public class NotifyRespInd extends GenericPdu {
+ /**
+ * Constructor, used when composing a M-NotifyResp.ind pdu.
+ *
+ * @param mmsVersion current version of mms
+ * @param transactionId the transaction-id value
+ * @param status the status value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if transactionId is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public NotifyRespInd(int mmsVersion,
+ byte[] transactionId,
+ int status) throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND);
+ setMmsVersion(mmsVersion);
+ setTransactionId(transactionId);
+ setStatus(status);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ NotifyRespInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get X-Mms-Report-Allowed field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public int getReportAllowed() {
+ return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Set X-Mms-Report-Allowed field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setReportAllowed(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED);
+ }
+
+ /**
+ * Set X-Mms-Status field value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.STATUS);
+ }
+
+ /**
+ * GetX-Mms-Status field value.
+ *
+ * @return the X-Mms-Status value
+ */
+ public int getStatus() {
+ return mPduHeaders.getOctet(PduHeaders.STATUS);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ * RuntimeException if an undeclared error occurs.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduBody.java b/telephony/java/com/google/android/mms/pdu/PduBody.java
new file mode 100644
index 000000000000..51914e4110b0
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduBody.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+public class PduBody {
+ private Vector<PduPart> mParts = null;
+
+ private Map<String, PduPart> mPartMapByContentId = null;
+ private Map<String, PduPart> mPartMapByContentLocation = null;
+ private Map<String, PduPart> mPartMapByName = null;
+ private Map<String, PduPart> mPartMapByFileName = null;
+
+ /**
+ * Constructor.
+ */
+ @UnsupportedAppUsage
+ public PduBody() {
+ mParts = new Vector<PduPart>();
+
+ mPartMapByContentId = new HashMap<String, PduPart>();
+ mPartMapByContentLocation = new HashMap<String, PduPart>();
+ mPartMapByName = new HashMap<String, PduPart>();
+ mPartMapByFileName = new HashMap<String, PduPart>();
+ }
+
+ private void putPartToMaps(PduPart part) {
+ // Put part to mPartMapByContentId.
+ byte[] contentId = part.getContentId();
+ if(null != contentId) {
+ mPartMapByContentId.put(new String(contentId), part);
+ }
+
+ // Put part to mPartMapByContentLocation.
+ byte[] contentLocation = part.getContentLocation();
+ if(null != contentLocation) {
+ String clc = new String(contentLocation);
+ mPartMapByContentLocation.put(clc, part);
+ }
+
+ // Put part to mPartMapByName.
+ byte[] name = part.getName();
+ if(null != name) {
+ String clc = new String(name);
+ mPartMapByName.put(clc, part);
+ }
+
+ // Put part to mPartMapByFileName.
+ byte[] fileName = part.getFilename();
+ if(null != fileName) {
+ String clc = new String(fileName);
+ mPartMapByFileName.put(clc, part);
+ }
+ }
+
+ /**
+ * Appends the specified part to the end of this body.
+ *
+ * @param part part to be appended
+ * @return true when success, false when fail
+ * @throws NullPointerException when part is null
+ */
+ @UnsupportedAppUsage
+ public boolean addPart(PduPart part) {
+ if(null == part) {
+ throw new NullPointerException();
+ }
+
+ putPartToMaps(part);
+ return mParts.add(part);
+ }
+
+ /**
+ * Inserts the specified part at the specified position.
+ *
+ * @param index index at which the specified part is to be inserted
+ * @param part part to be inserted
+ * @throws NullPointerException when part is null
+ */
+ @UnsupportedAppUsage
+ public void addPart(int index, PduPart part) {
+ if(null == part) {
+ throw new NullPointerException();
+ }
+
+ putPartToMaps(part);
+ mParts.add(index, part);
+ }
+
+ /**
+ * Removes the part at the specified position.
+ *
+ * @param index index of the part to return
+ * @return part at the specified index
+ */
+ @UnsupportedAppUsage
+ public PduPart removePart(int index) {
+ return mParts.remove(index);
+ }
+
+ /**
+ * Remove all of the parts.
+ */
+ public void removeAll() {
+ mParts.clear();
+ }
+
+ /**
+ * Get the part at the specified position.
+ *
+ * @param index index of the part to return
+ * @return part at the specified index
+ */
+ @UnsupportedAppUsage
+ public PduPart getPart(int index) {
+ return mParts.get(index);
+ }
+
+ /**
+ * Get the index of the specified part.
+ *
+ * @param part the part object
+ * @return index the index of the first occurrence of the part in this body
+ */
+ @UnsupportedAppUsage
+ public int getPartIndex(PduPart part) {
+ return mParts.indexOf(part);
+ }
+
+ /**
+ * Get the number of parts.
+ *
+ * @return the number of parts
+ */
+ @UnsupportedAppUsage
+ public int getPartsNum() {
+ return mParts.size();
+ }
+
+ /**
+ * Get pdu part by content id.
+ *
+ * @param cid the value of content id.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByContentId(String cid) {
+ return mPartMapByContentId.get(cid);
+ }
+
+ /**
+ * Get pdu part by Content-Location. Content-Location of part is
+ * the same as filename and name(param of content-type).
+ *
+ * @param fileName the value of filename.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByContentLocation(String contentLocation) {
+ return mPartMapByContentLocation.get(contentLocation);
+ }
+
+ /**
+ * Get pdu part by name.
+ *
+ * @param fileName the value of filename.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByName(String name) {
+ return mPartMapByName.get(name);
+ }
+
+ /**
+ * Get pdu part by filename.
+ *
+ * @param fileName the value of filename.
+ * @return the pdu part.
+ */
+ @UnsupportedAppUsage
+ public PduPart getPartByFileName(String filename) {
+ return mPartMapByFileName.get(filename);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduComposer.java b/telephony/java/com/google/android/mms/pdu/PduComposer.java
new file mode 100644
index 000000000000..e24bf21a11b5
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduComposer.java
@@ -0,0 +1,1229 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.text.TextUtils;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public class PduComposer {
+ /**
+ * Address type.
+ */
+ static private final int PDU_PHONE_NUMBER_ADDRESS_TYPE = 1;
+ static private final int PDU_EMAIL_ADDRESS_TYPE = 2;
+ static private final int PDU_IPV4_ADDRESS_TYPE = 3;
+ static private final int PDU_IPV6_ADDRESS_TYPE = 4;
+ static private final int PDU_UNKNOWN_ADDRESS_TYPE = 5;
+
+ /**
+ * Address regular expression string.
+ */
+ static final String REGEXP_PHONE_NUMBER_ADDRESS_TYPE = "\\+?[0-9|\\.|\\-]+";
+ static final String REGEXP_EMAIL_ADDRESS_TYPE = "[a-zA-Z| ]*\\<{0,1}[a-zA-Z| ]+@{1}" +
+ "[a-zA-Z| ]+\\.{1}[a-zA-Z| ]+\\>{0,1}";
+ static final String REGEXP_IPV6_ADDRESS_TYPE =
+ "[a-fA-F]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
+ "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" +
+ "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}";
+ static final String REGEXP_IPV4_ADDRESS_TYPE = "[0-9]{1,3}\\.{1}[0-9]{1,3}\\.{1}" +
+ "[0-9]{1,3}\\.{1}[0-9]{1,3}";
+
+ /**
+ * The postfix strings of address.
+ */
+ static final String STRING_PHONE_NUMBER_ADDRESS_TYPE = "/TYPE=PLMN";
+ static final String STRING_IPV4_ADDRESS_TYPE = "/TYPE=IPV4";
+ static final String STRING_IPV6_ADDRESS_TYPE = "/TYPE=IPV6";
+
+ /**
+ * Error values.
+ */
+ static private final int PDU_COMPOSE_SUCCESS = 0;
+ static private final int PDU_COMPOSE_CONTENT_ERROR = 1;
+ static private final int PDU_COMPOSE_FIELD_NOT_SET = 2;
+ static private final int PDU_COMPOSE_FIELD_NOT_SUPPORTED = 3;
+
+ /**
+ * WAP values defined in WSP spec.
+ */
+ static private final int QUOTED_STRING_FLAG = 34;
+ static private final int END_STRING_FLAG = 0;
+ static private final int LENGTH_QUOTE = 31;
+ static private final int TEXT_MAX = 127;
+ static private final int SHORT_INTEGER_MAX = 127;
+ static private final int LONG_INTEGER_LENGTH_MAX = 8;
+
+ /**
+ * Block size when read data from InputStream.
+ */
+ static private final int PDU_COMPOSER_BLOCK_SIZE = 1024;
+
+ /**
+ * The output message.
+ */
+ @UnsupportedAppUsage
+ protected ByteArrayOutputStream mMessage = null;
+
+ /**
+ * The PDU.
+ */
+ @UnsupportedAppUsage
+ private GenericPdu mPdu = null;
+
+ /**
+ * Current visiting position of the mMessage.
+ */
+ @UnsupportedAppUsage
+ protected int mPosition = 0;
+
+ /**
+ * Message compose buffer stack.
+ */
+ @UnsupportedAppUsage
+ private BufferStack mStack = null;
+
+ /**
+ * Content resolver.
+ */
+ @UnsupportedAppUsage
+ private final ContentResolver mResolver;
+
+ /**
+ * Header of this pdu.
+ */
+ @UnsupportedAppUsage
+ private PduHeaders mPduHeader = null;
+
+ /**
+ * Map of all content type
+ */
+ @UnsupportedAppUsage
+ private static HashMap<String, Integer> mContentTypeMap = null;
+
+ static {
+ mContentTypeMap = new HashMap<String, Integer>();
+
+ int i;
+ for (i = 0; i < PduContentTypes.contentTypes.length; i++) {
+ mContentTypeMap.put(PduContentTypes.contentTypes[i], i);
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param context the context
+ * @param pdu the pdu to be composed
+ */
+ @UnsupportedAppUsage
+ public PduComposer(Context context, GenericPdu pdu) {
+ mPdu = pdu;
+ mResolver = context.getContentResolver();
+ mPduHeader = pdu.getPduHeaders();
+ mStack = new BufferStack();
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ /**
+ * Make the message. No need to check whether mandatory fields are set,
+ * because the constructors of outgoing pdus are taking care of this.
+ *
+ * @return OutputStream of maked message. Return null if
+ * the PDU is invalid.
+ */
+ @UnsupportedAppUsage
+ public byte[] make() {
+ // Get Message-type.
+ int type = mPdu.getMessageType();
+
+ /* make the message */
+ switch (type) {
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ if (makeSendRetrievePdu(type) != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ if (makeNotifyResp() != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ if (makeAckInd() != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ if (makeReadRecInd() != PDU_COMPOSE_SUCCESS) {
+ return null;
+ }
+ break;
+ default:
+ return null;
+ }
+
+ return mMessage.toByteArray();
+ }
+
+ /**
+ * Copy buf to mMessage.
+ */
+ @UnsupportedAppUsage
+ protected void arraycopy(byte[] buf, int pos, int length) {
+ mMessage.write(buf, pos, length);
+ mPosition = mPosition + length;
+ }
+
+ /**
+ * Append a byte to mMessage.
+ */
+ protected void append(int value) {
+ mMessage.write(value);
+ mPosition ++;
+ }
+
+ /**
+ * Append short integer value to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendShortInteger(int value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Short-integer = OCTET
+ * ; Integers in range 0-127 shall be encoded as a one octet value
+ * ; with the most significant bit set to one (1xxx xxxx) and with
+ * ; the value in the remaining least significant bits.
+ * In our implementation, only low 7 bits are stored and otherwise
+ * bits are ignored.
+ */
+ append((value | 0x80) & 0xff);
+ }
+
+ /**
+ * Append an octet number between 128 and 255 into mMessage.
+ * NOTE:
+ * A value between 0 and 127 should be appended by using appendShortInteger.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendOctet(int number) {
+ append(number);
+ }
+
+ /**
+ * Append a short length into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ protected void appendShortLength(int value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Short-length = <Any octet 0-30>
+ */
+ append(value);
+ }
+
+ /**
+ * Append long integer into mMessage. it's used for really long integers.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendLongInteger(long longInt) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Long-integer = Short-length Multi-octet-integer
+ * ; The Short-length indicates the length of the Multi-octet-integer
+ * Multi-octet-integer = 1*30 OCTET
+ * ; The content octets shall be an unsigned integer value with the
+ * ; most significant octet encoded first (big-endian representation).
+ * ; The minimum number of octets must be used to encode the value.
+ */
+ int size;
+ long temp = longInt;
+
+ // Count the length of the long integer.
+ for(size = 0; (temp != 0) && (size < LONG_INTEGER_LENGTH_MAX); size++) {
+ temp = (temp >>> 8);
+ }
+
+ // Set Length.
+ appendShortLength(size);
+
+ // Count and set the long integer.
+ int i;
+ int shift = (size -1) * 8;
+
+ for (i = 0; i < size; i++) {
+ append((int)((longInt >>> shift) & 0xff));
+ shift = shift - 8;
+ }
+ }
+
+ /**
+ * Append text string into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendTextString(byte[] text) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Text-string = [Quote] *TEXT End-of-string
+ * ; If the first character in the TEXT is in the range of 128-255,
+ * ; a Quote character must precede it. Otherwise the Quote character
+ * ;must be omitted. The Quote is not part of the contents.
+ */
+ if (((text[0])&0xff) > TEXT_MAX) { // No need to check for <= 255
+ append(TEXT_MAX);
+ }
+
+ arraycopy(text, 0, text.length);
+ append(0);
+ }
+
+ /**
+ * Append text string into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendTextString(String str) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Text-string = [Quote] *TEXT End-of-string
+ * ; If the first character in the TEXT is in the range of 128-255,
+ * ; a Quote character must precede it. Otherwise the Quote character
+ * ;must be omitted. The Quote is not part of the contents.
+ */
+ appendTextString(str.getBytes());
+ }
+
+ /**
+ * Append encoded string value to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendEncodedString(EncodedStringValue enStr) {
+ /*
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C:
+ * Encoded-string-value = Text-string | Value-length Char-set Text-string
+ */
+ assert(enStr != null);
+
+ int charset = enStr.getCharacterSet();
+ byte[] textString = enStr.getTextString();
+ if (null == textString) {
+ return;
+ }
+
+ /*
+ * In the implementation of EncodedStringValue, the charset field will
+ * never be 0. It will always be composed as
+ * Encoded-string-value = Value-length Char-set Text-string
+ */
+ mStack.newbuf();
+ PositionMarker start = mStack.mark();
+
+ appendShortInteger(charset);
+ appendTextString(textString);
+
+ int len = start.getLength();
+ mStack.pop();
+ appendValueLength(len);
+ mStack.copy();
+ }
+
+ /**
+ * Append uintvar integer into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendUintvarInteger(long value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * To encode a large unsigned integer, split it into 7-bit fragments
+ * and place them in the payloads of multiple octets. The most significant
+ * bits are placed in the first octets with the least significant bits
+ * ending up in the last octet. All octets MUST set the Continue bit to 1
+ * except the last octet, which MUST set the Continue bit to 0.
+ */
+ int i;
+ long max = SHORT_INTEGER_MAX;
+
+ for (i = 0; i < 5; i++) {
+ if (value < max) {
+ break;
+ }
+
+ max = (max << 7) | 0x7fl;
+ }
+
+ while(i > 0) {
+ long temp = value >>> (i * 7);
+ temp = temp & 0x7f;
+
+ append((int)((temp | 0x80) & 0xff));
+
+ i--;
+ }
+
+ append((int)(value & 0x7f));
+ }
+
+ /**
+ * Append date value into mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ protected void appendDateValue(long date) {
+ /*
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C:
+ * Date-value = Long-integer
+ */
+ appendLongInteger(date);
+ }
+
+ /**
+ * Append value length to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendValueLength(long value) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Value-length = Short-length | (Length-quote Length)
+ * ; Value length is used to indicate the length of the value to follow
+ * Short-length = <Any octet 0-30>
+ * Length-quote = <Octet 31>
+ * Length = Uintvar-integer
+ */
+ if (value < LENGTH_QUOTE) {
+ appendShortLength((int) value);
+ return;
+ }
+
+ append(LENGTH_QUOTE);
+ appendUintvarInteger(value);
+ }
+
+ /**
+ * Append quoted string to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendQuotedString(byte[] text) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Quoted-string = <Octet 34> *TEXT End-of-string
+ * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
+ * ;quotation-marks <"> removed.
+ */
+ append(QUOTED_STRING_FLAG);
+ arraycopy(text, 0, text.length);
+ append(END_STRING_FLAG);
+ }
+
+ /**
+ * Append quoted string to mMessage.
+ * This implementation doesn't check the validity of parameter, since it
+ * assumes that the values are validated in the GenericPdu setter methods.
+ */
+ @UnsupportedAppUsage
+ protected void appendQuotedString(String str) {
+ /*
+ * From WAP-230-WSP-20010705-a:
+ * Quoted-string = <Octet 34> *TEXT End-of-string
+ * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing
+ * ;quotation-marks <"> removed.
+ */
+ appendQuotedString(str.getBytes());
+ }
+
+ private EncodedStringValue appendAddressType(EncodedStringValue address) {
+ EncodedStringValue temp = null;
+
+ try {
+ int addressType = checkAddressType(address.getString());
+ temp = EncodedStringValue.copy(address);
+ if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) {
+ // Phone number.
+ temp.appendTextString(STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes());
+ } else if (PDU_IPV4_ADDRESS_TYPE == addressType) {
+ // Ipv4 address.
+ temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes());
+ } else if (PDU_IPV6_ADDRESS_TYPE == addressType) {
+ // Ipv6 address.
+ temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes());
+ }
+ } catch (NullPointerException e) {
+ return null;
+ }
+
+ return temp;
+ }
+
+ /**
+ * Append header to mMessage.
+ */
+ @UnsupportedAppUsage
+ private int appendHeader(int field) {
+ switch (field) {
+ case PduHeaders.MMS_VERSION:
+ appendOctet(field);
+
+ int version = mPduHeader.getOctet(field);
+ if (0 == version) {
+ appendShortInteger(PduHeaders.CURRENT_MMS_VERSION);
+ } else {
+ appendShortInteger(version);
+ }
+
+ break;
+
+ case PduHeaders.MESSAGE_ID:
+ case PduHeaders.TRANSACTION_ID:
+ byte[] textString = mPduHeader.getTextString(field);
+ if (null == textString) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendTextString(textString);
+ break;
+
+ case PduHeaders.TO:
+ case PduHeaders.BCC:
+ case PduHeaders.CC:
+ EncodedStringValue[] addr = mPduHeader.getEncodedStringValues(field);
+
+ if (null == addr) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ EncodedStringValue temp;
+ for (int i = 0; i < addr.length; i++) {
+ temp = appendAddressType(addr[i]);
+ if (temp == null) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ appendOctet(field);
+ appendEncodedString(temp);
+ }
+ break;
+
+ case PduHeaders.FROM:
+ // Value-length (Address-present-token Encoded-string-value | Insert-address-token)
+ appendOctet(field);
+
+ EncodedStringValue from = mPduHeader.getEncodedStringValue(field);
+ if ((from == null)
+ || TextUtils.isEmpty(from.getString())
+ || new String(from.getTextString()).equals(
+ PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR)) {
+ // Length of from = 1
+ append(1);
+ // Insert-address-token = <Octet 129>
+ append(PduHeaders.FROM_INSERT_ADDRESS_TOKEN);
+ } else {
+ mStack.newbuf();
+ PositionMarker fstart = mStack.mark();
+
+ // Address-present-token = <Octet 128>
+ append(PduHeaders.FROM_ADDRESS_PRESENT_TOKEN);
+
+ temp = appendAddressType(from);
+ if (temp == null) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ appendEncodedString(temp);
+
+ int flen = fstart.getLength();
+ mStack.pop();
+ appendValueLength(flen);
+ mStack.copy();
+ }
+ break;
+
+ case PduHeaders.READ_STATUS:
+ case PduHeaders.STATUS:
+ case PduHeaders.REPORT_ALLOWED:
+ case PduHeaders.PRIORITY:
+ case PduHeaders.DELIVERY_REPORT:
+ case PduHeaders.READ_REPORT:
+ case PduHeaders.RETRIEVE_STATUS:
+ int octet = mPduHeader.getOctet(field);
+ if (0 == octet) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendOctet(octet);
+ break;
+
+ case PduHeaders.DATE:
+ long date = mPduHeader.getLongInteger(field);
+ if (-1 == date) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendDateValue(date);
+ break;
+
+ case PduHeaders.SUBJECT:
+ case PduHeaders.RETRIEVE_TEXT:
+ EncodedStringValue enString =
+ mPduHeader.getEncodedStringValue(field);
+ if (null == enString) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ appendEncodedString(enString);
+ break;
+
+ case PduHeaders.MESSAGE_CLASS:
+ byte[] messageClass = mPduHeader.getTextString(field);
+ if (null == messageClass) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+ if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_ADVERTISEMENT);
+ } else if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_AUTO);
+ } else if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_PERSONAL);
+ } else if (Arrays.equals(messageClass,
+ PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes())) {
+ appendOctet(PduHeaders.MESSAGE_CLASS_INFORMATIONAL);
+ } else {
+ appendTextString(messageClass);
+ }
+ break;
+
+ case PduHeaders.EXPIRY:
+ long expiry = mPduHeader.getLongInteger(field);
+ if (-1 == expiry) {
+ return PDU_COMPOSE_FIELD_NOT_SET;
+ }
+
+ appendOctet(field);
+
+ mStack.newbuf();
+ PositionMarker expiryStart = mStack.mark();
+
+ append(PduHeaders.VALUE_RELATIVE_TOKEN);
+ appendLongInteger(expiry);
+
+ int expiryLength = expiryStart.getLength();
+ mStack.pop();
+ appendValueLength(expiryLength);
+ mStack.copy();
+ break;
+
+ default:
+ return PDU_COMPOSE_FIELD_NOT_SUPPORTED;
+ }
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make ReadRec.Ind.
+ */
+ private int makeReadRecInd() {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(PduHeaders.MESSAGE_TYPE_READ_REC_IND);
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Message-ID
+ if (appendHeader(PduHeaders.MESSAGE_ID) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // To
+ if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // From
+ if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Date Optional
+ appendHeader(PduHeaders.DATE);
+
+ // X-Mms-Read-Status
+ if (appendHeader(PduHeaders.READ_STATUS) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Applic-ID Optional(not support)
+ // X-Mms-Reply-Applic-ID Optional(not support)
+ // X-Mms-Aux-Applic-Info Optional(not support)
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make NotifyResp.Ind.
+ */
+ private int makeNotifyResp() {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND);
+
+ // X-Mms-Transaction-ID
+ if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Status
+ if (appendHeader(PduHeaders.STATUS) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Report-Allowed Optional (not support)
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make Acknowledge.Ind.
+ */
+ private int makeAckInd() {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND);
+
+ // X-Mms-Transaction-ID
+ if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // X-Mms-Report-Allowed Optional
+ appendHeader(PduHeaders.REPORT_ALLOWED);
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Make Send.req.
+ */
+ private int makeSendRetrievePdu(int type) {
+ if (mMessage == null) {
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ // X-Mms-Message-Type
+ appendOctet(PduHeaders.MESSAGE_TYPE);
+ appendOctet(type);
+
+ // X-Mms-Transaction-ID
+ appendOctet(PduHeaders.TRANSACTION_ID);
+
+ byte[] trid = mPduHeader.getTextString(PduHeaders.TRANSACTION_ID);
+ if (trid == null) {
+ // Transaction-ID should be set(by Transaction) before make().
+ throw new IllegalArgumentException("Transaction-ID is null.");
+ }
+ appendTextString(trid);
+
+ // X-Mms-MMS-Version
+ if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Date Date-value Optional.
+ appendHeader(PduHeaders.DATE);
+
+ // From
+ if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ boolean recipient = false;
+
+ // To
+ if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_CONTENT_ERROR) {
+ recipient = true;
+ }
+
+ // Cc
+ if (appendHeader(PduHeaders.CC) != PDU_COMPOSE_CONTENT_ERROR) {
+ recipient = true;
+ }
+
+ // Bcc
+ if (appendHeader(PduHeaders.BCC) != PDU_COMPOSE_CONTENT_ERROR) {
+ recipient = true;
+ }
+
+ // Need at least one of "cc", "bcc" and "to".
+ if (false == recipient) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // Subject Optional
+ appendHeader(PduHeaders.SUBJECT);
+
+ // X-Mms-Message-Class Optional
+ // Message-class-value = Class-identifier | Token-text
+ appendHeader(PduHeaders.MESSAGE_CLASS);
+
+ // X-Mms-Expiry Optional
+ appendHeader(PduHeaders.EXPIRY);
+
+ // X-Mms-Priority Optional
+ appendHeader(PduHeaders.PRIORITY);
+
+ // X-Mms-Delivery-Report Optional
+ appendHeader(PduHeaders.DELIVERY_REPORT);
+
+ // X-Mms-Read-Report Optional
+ appendHeader(PduHeaders.READ_REPORT);
+
+ if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
+ // X-Mms-Retrieve-Status Optional
+ appendHeader(PduHeaders.RETRIEVE_STATUS);
+ // X-Mms-Retrieve-Text Optional
+ appendHeader(PduHeaders.RETRIEVE_TEXT);
+ }
+
+
+ // Content-Type
+ appendOctet(PduHeaders.CONTENT_TYPE);
+
+ // Message body
+ return makeMessageBody(type);
+ }
+
+ /**
+ * Make message body.
+ */
+ private int makeMessageBody(int type) {
+ // 1. add body informations
+ mStack.newbuf(); // Switching buffer because we need to
+
+ PositionMarker ctStart = mStack.mark();
+
+ // This contentTypeIdentifier should be used for type of attachment...
+ String contentType = new String(mPduHeader.getTextString(PduHeaders.CONTENT_TYPE));
+ Integer contentTypeIdentifier = mContentTypeMap.get(contentType);
+ if (contentTypeIdentifier == null) {
+ // content type is mandatory
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ appendShortInteger(contentTypeIdentifier.intValue());
+
+ // content-type parameter: start
+ PduBody body;
+ if (type == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) {
+ body = ((RetrieveConf) mPdu).getBody();
+ } else {
+ body = ((SendReq) mPdu).getBody();
+ }
+ if (null == body || body.getPartsNum() == 0) {
+ // empty message
+ appendUintvarInteger(0);
+ mStack.pop();
+ mStack.copy();
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ PduPart part;
+ try {
+ part = body.getPart(0);
+
+ byte[] start = part.getContentId();
+ if (start != null) {
+ appendOctet(PduPart.P_DEP_START);
+ if (('<' == start[0]) && ('>' == start[start.length - 1])) {
+ appendTextString(start);
+ } else {
+ appendTextString("<" + new String(start) + ">");
+ }
+ }
+
+ // content-type parameter: type
+ appendOctet(PduPart.P_CT_MR_TYPE);
+ appendTextString(part.getContentType());
+ }
+ catch (ArrayIndexOutOfBoundsException e){
+ e.printStackTrace();
+ }
+
+ int ctLength = ctStart.getLength();
+ mStack.pop();
+ appendValueLength(ctLength);
+ mStack.copy();
+
+ // 3. add content
+ int partNum = body.getPartsNum();
+ appendUintvarInteger(partNum);
+ for (int i = 0; i < partNum; i++) {
+ part = body.getPart(i);
+ mStack.newbuf(); // Leaving space for header lengh and data length
+ PositionMarker attachment = mStack.mark();
+
+ mStack.newbuf(); // Leaving space for Content-Type length
+ PositionMarker contentTypeBegin = mStack.mark();
+
+ byte[] partContentType = part.getContentType();
+
+ if (partContentType == null) {
+ // content type is mandatory
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+
+ // content-type value
+ Integer partContentTypeIdentifier =
+ mContentTypeMap.get(new String(partContentType));
+ if (partContentTypeIdentifier == null) {
+ appendTextString(partContentType);
+ } else {
+ appendShortInteger(partContentTypeIdentifier.intValue());
+ }
+
+ /* Content-type parameter : name.
+ * The value of name, filename, content-location is the same.
+ * Just one of them is enough for this PDU.
+ */
+ byte[] name = part.getName();
+
+ if (null == name) {
+ name = part.getFilename();
+
+ if (null == name) {
+ name = part.getContentLocation();
+
+ if (null == name) {
+ /* at lease one of name, filename, Content-location
+ * should be available.
+ */
+ return PDU_COMPOSE_CONTENT_ERROR;
+ }
+ }
+ }
+ appendOctet(PduPart.P_DEP_NAME);
+ appendTextString(name);
+
+ // content-type parameter : charset
+ int charset = part.getCharset();
+ if (charset != 0) {
+ appendOctet(PduPart.P_CHARSET);
+ appendShortInteger(charset);
+ }
+
+ int contentTypeLength = contentTypeBegin.getLength();
+ mStack.pop();
+ appendValueLength(contentTypeLength);
+ mStack.copy();
+
+ // content id
+ byte[] contentId = part.getContentId();
+
+ if (null != contentId) {
+ appendOctet(PduPart.P_CONTENT_ID);
+ if (('<' == contentId[0]) && ('>' == contentId[contentId.length - 1])) {
+ appendQuotedString(contentId);
+ } else {
+ appendQuotedString("<" + new String(contentId) + ">");
+ }
+ }
+
+ // content-location
+ byte[] contentLocation = part.getContentLocation();
+ if (null != contentLocation) {
+ appendOctet(PduPart.P_CONTENT_LOCATION);
+ appendTextString(contentLocation);
+ }
+
+ // content
+ int headerLength = attachment.getLength();
+
+ int dataLength = 0; // Just for safety...
+ byte[] partData = part.getData();
+
+ if (partData != null) {
+ arraycopy(partData, 0, partData.length);
+ dataLength = partData.length;
+ } else {
+ InputStream cr = null;
+ try {
+ byte[] buffer = new byte[PDU_COMPOSER_BLOCK_SIZE];
+ cr = mResolver.openInputStream(part.getDataUri());
+ int len = 0;
+ while ((len = cr.read(buffer)) != -1) {
+ mMessage.write(buffer, 0, len);
+ mPosition += len;
+ dataLength += len;
+ }
+ } catch (FileNotFoundException e) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ } catch (IOException e) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ } catch (RuntimeException e) {
+ return PDU_COMPOSE_CONTENT_ERROR;
+ } finally {
+ if (cr != null) {
+ try {
+ cr.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ if (dataLength != (attachment.getLength() - headerLength)) {
+ throw new RuntimeException("BUG: Length sanity check failed");
+ }
+
+ mStack.pop();
+ appendUintvarInteger(headerLength);
+ appendUintvarInteger(dataLength);
+ mStack.copy();
+ }
+
+ return PDU_COMPOSE_SUCCESS;
+ }
+
+ /**
+ * Record current message informations.
+ */
+ static private class LengthRecordNode {
+ ByteArrayOutputStream currentMessage = null;
+ public int currentPosition = 0;
+
+ public LengthRecordNode next = null;
+ }
+
+ /**
+ * Mark current message position and stact size.
+ */
+ private class PositionMarker {
+ private int c_pos; // Current position
+ private int currentStackSize; // Current stack size
+
+ @UnsupportedAppUsage
+ int getLength() {
+ // If these assert fails, likely that you are finding the
+ // size of buffer that is deep in BufferStack you can only
+ // find the length of the buffer that is on top
+ if (currentStackSize != mStack.stackSize) {
+ throw new RuntimeException("BUG: Invalid call to getLength()");
+ }
+
+ return mPosition - c_pos;
+ }
+ }
+
+ /**
+ * This implementation can be OPTIMIZED to use only
+ * 2 buffers. This optimization involves changing BufferStack
+ * only... Its usage (interface) will not change.
+ */
+ private class BufferStack {
+ private LengthRecordNode stack = null;
+ private LengthRecordNode toCopy = null;
+
+ int stackSize = 0;
+
+ /**
+ * Create a new message buffer and push it into the stack.
+ */
+ @UnsupportedAppUsage
+ void newbuf() {
+ // You can't create a new buff when toCopy != null
+ // That is after calling pop() and before calling copy()
+ // If you do, it is a bug
+ if (toCopy != null) {
+ throw new RuntimeException("BUG: Invalid newbuf() before copy()");
+ }
+
+ LengthRecordNode temp = new LengthRecordNode();
+
+ temp.currentMessage = mMessage;
+ temp.currentPosition = mPosition;
+
+ temp.next = stack;
+ stack = temp;
+
+ stackSize = stackSize + 1;
+
+ mMessage = new ByteArrayOutputStream();
+ mPosition = 0;
+ }
+
+ /**
+ * Pop the message before and record current message in the stack.
+ */
+ @UnsupportedAppUsage
+ void pop() {
+ ByteArrayOutputStream currentMessage = mMessage;
+ int currentPosition = mPosition;
+
+ mMessage = stack.currentMessage;
+ mPosition = stack.currentPosition;
+
+ toCopy = stack;
+ // Re using the top element of the stack to avoid memory allocation
+
+ stack = stack.next;
+ stackSize = stackSize - 1;
+
+ toCopy.currentMessage = currentMessage;
+ toCopy.currentPosition = currentPosition;
+ }
+
+ /**
+ * Append current message to the message before.
+ */
+ @UnsupportedAppUsage
+ void copy() {
+ arraycopy(toCopy.currentMessage.toByteArray(), 0,
+ toCopy.currentPosition);
+
+ toCopy = null;
+ }
+
+ /**
+ * Mark current message position
+ */
+ @UnsupportedAppUsage
+ PositionMarker mark() {
+ PositionMarker m = new PositionMarker();
+
+ m.c_pos = mPosition;
+ m.currentStackSize = stackSize;
+
+ return m;
+ }
+ }
+
+ /**
+ * Check address type.
+ *
+ * @param address address string without the postfix stinng type,
+ * such as "/TYPE=PLMN", "/TYPE=IPv6" and "/TYPE=IPv4"
+ * @return PDU_PHONE_NUMBER_ADDRESS_TYPE if it is phone number,
+ * PDU_EMAIL_ADDRESS_TYPE if it is email address,
+ * PDU_IPV4_ADDRESS_TYPE if it is ipv4 address,
+ * PDU_IPV6_ADDRESS_TYPE if it is ipv6 address,
+ * PDU_UNKNOWN_ADDRESS_TYPE if it is unknown.
+ */
+ protected static int checkAddressType(String address) {
+ /**
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf, section 8.
+ * address = ( e-mail / device-address / alphanum-shortcode / num-shortcode)
+ * e-mail = mailbox; to the definition of mailbox as described in
+ * section 3.4 of [RFC2822], but excluding the
+ * obsolete definitions as indicated by the "obs-" prefix.
+ * device-address = ( global-phone-number "/TYPE=PLMN" )
+ * / ( ipv4 "/TYPE=IPv4" ) / ( ipv6 "/TYPE=IPv6" )
+ * / ( escaped-value "/TYPE=" address-type )
+ *
+ * global-phone-number = ["+"] 1*( DIGIT / written-sep )
+ * written-sep =("-"/".")
+ *
+ * ipv4 = 1*3DIGIT 3( "." 1*3DIGIT ) ; IPv4 address value
+ *
+ * ipv6 = 4HEXDIG 7( ":" 4HEXDIG ) ; IPv6 address per RFC 2373
+ */
+
+ if (null == address) {
+ return PDU_UNKNOWN_ADDRESS_TYPE;
+ }
+
+ if (address.matches(REGEXP_IPV4_ADDRESS_TYPE)) {
+ // Ipv4 address.
+ return PDU_IPV4_ADDRESS_TYPE;
+ }else if (address.matches(REGEXP_PHONE_NUMBER_ADDRESS_TYPE)) {
+ // Phone number.
+ return PDU_PHONE_NUMBER_ADDRESS_TYPE;
+ } else if (address.matches(REGEXP_EMAIL_ADDRESS_TYPE)) {
+ // Email address.
+ return PDU_EMAIL_ADDRESS_TYPE;
+ } else if (address.matches(REGEXP_IPV6_ADDRESS_TYPE)) {
+ // Ipv6 address.
+ return PDU_IPV6_ADDRESS_TYPE;
+ } else {
+ // Unknown address.
+ return PDU_UNKNOWN_ADDRESS_TYPE;
+ }
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduContentTypes.java b/telephony/java/com/google/android/mms/pdu/PduContentTypes.java
new file mode 100644
index 000000000000..8551b2f9b693
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduContentTypes.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public class PduContentTypes {
+ /**
+ * All content types. From:
+ * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.htm
+ */
+ @UnsupportedAppUsage
+ static final String[] contentTypes = {
+ "*/*", /* 0x00 */
+ "text/*", /* 0x01 */
+ "text/html", /* 0x02 */
+ "text/plain", /* 0x03 */
+ "text/x-hdml", /* 0x04 */
+ "text/x-ttml", /* 0x05 */
+ "text/x-vCalendar", /* 0x06 */
+ "text/x-vCard", /* 0x07 */
+ "text/vnd.wap.wml", /* 0x08 */
+ "text/vnd.wap.wmlscript", /* 0x09 */
+ "text/vnd.wap.wta-event", /* 0x0A */
+ "multipart/*", /* 0x0B */
+ "multipart/mixed", /* 0x0C */
+ "multipart/form-data", /* 0x0D */
+ "multipart/byterantes", /* 0x0E */
+ "multipart/alternative", /* 0x0F */
+ "application/*", /* 0x10 */
+ "application/java-vm", /* 0x11 */
+ "application/x-www-form-urlencoded", /* 0x12 */
+ "application/x-hdmlc", /* 0x13 */
+ "application/vnd.wap.wmlc", /* 0x14 */
+ "application/vnd.wap.wmlscriptc", /* 0x15 */
+ "application/vnd.wap.wta-eventc", /* 0x16 */
+ "application/vnd.wap.uaprof", /* 0x17 */
+ "application/vnd.wap.wtls-ca-certificate", /* 0x18 */
+ "application/vnd.wap.wtls-user-certificate", /* 0x19 */
+ "application/x-x509-ca-cert", /* 0x1A */
+ "application/x-x509-user-cert", /* 0x1B */
+ "image/*", /* 0x1C */
+ "image/gif", /* 0x1D */
+ "image/jpeg", /* 0x1E */
+ "image/tiff", /* 0x1F */
+ "image/png", /* 0x20 */
+ "image/vnd.wap.wbmp", /* 0x21 */
+ "application/vnd.wap.multipart.*", /* 0x22 */
+ "application/vnd.wap.multipart.mixed", /* 0x23 */
+ "application/vnd.wap.multipart.form-data", /* 0x24 */
+ "application/vnd.wap.multipart.byteranges", /* 0x25 */
+ "application/vnd.wap.multipart.alternative", /* 0x26 */
+ "application/xml", /* 0x27 */
+ "text/xml", /* 0x28 */
+ "application/vnd.wap.wbxml", /* 0x29 */
+ "application/x-x968-cross-cert", /* 0x2A */
+ "application/x-x968-ca-cert", /* 0x2B */
+ "application/x-x968-user-cert", /* 0x2C */
+ "text/vnd.wap.si", /* 0x2D */
+ "application/vnd.wap.sic", /* 0x2E */
+ "text/vnd.wap.sl", /* 0x2F */
+ "application/vnd.wap.slc", /* 0x30 */
+ "text/vnd.wap.co", /* 0x31 */
+ "application/vnd.wap.coc", /* 0x32 */
+ "application/vnd.wap.multipart.related", /* 0x33 */
+ "application/vnd.wap.sia", /* 0x34 */
+ "text/vnd.wap.connectivity-xml", /* 0x35 */
+ "application/vnd.wap.connectivity-wbxml", /* 0x36 */
+ "application/pkcs7-mime", /* 0x37 */
+ "application/vnd.wap.hashed-certificate", /* 0x38 */
+ "application/vnd.wap.signed-certificate", /* 0x39 */
+ "application/vnd.wap.cert-response", /* 0x3A */
+ "application/xhtml+xml", /* 0x3B */
+ "application/wml+xml", /* 0x3C */
+ "text/css", /* 0x3D */
+ "application/vnd.wap.mms-message", /* 0x3E */
+ "application/vnd.wap.rollover-certificate", /* 0x3F */
+ "application/vnd.wap.locc+wbxml", /* 0x40 */
+ "application/vnd.wap.loc+xml", /* 0x41 */
+ "application/vnd.syncml.dm+wbxml", /* 0x42 */
+ "application/vnd.syncml.dm+xml", /* 0x43 */
+ "application/vnd.syncml.notification", /* 0x44 */
+ "application/vnd.wap.xhtml+xml", /* 0x45 */
+ "application/vnd.wv.csp.cir", /* 0x46 */
+ "application/vnd.oma.dd+xml", /* 0x47 */
+ "application/vnd.oma.drm.message", /* 0x48 */
+ "application/vnd.oma.drm.content", /* 0x49 */
+ "application/vnd.oma.drm.rights+xml", /* 0x4A */
+ "application/vnd.oma.drm.rights+wbxml", /* 0x4B */
+ "application/vnd.wv.csp+xml", /* 0x4C */
+ "application/vnd.wv.csp+wbxml", /* 0x4D */
+ "application/vnd.syncml.ds.notification", /* 0x4E */
+ "audio/*", /* 0x4F */
+ "video/*", /* 0x50 */
+ "application/vnd.oma.dd2+xml", /* 0x51 */
+ "application/mikey" /* 0x52 */
+ };
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduHeaders.java b/telephony/java/com/google/android/mms/pdu/PduHeaders.java
new file mode 100644
index 000000000000..b5244645fda1
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduHeaders.java
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class PduHeaders {
+ /**
+ * All pdu header fields.
+ */
+ public static final int BCC = 0x81;
+ public static final int CC = 0x82;
+ public static final int CONTENT_LOCATION = 0x83;
+ public static final int CONTENT_TYPE = 0x84;
+ public static final int DATE = 0x85;
+ public static final int DELIVERY_REPORT = 0x86;
+ public static final int DELIVERY_TIME = 0x87;
+ public static final int EXPIRY = 0x88;
+ public static final int FROM = 0x89;
+ public static final int MESSAGE_CLASS = 0x8A;
+ public static final int MESSAGE_ID = 0x8B;
+ public static final int MESSAGE_TYPE = 0x8C;
+ public static final int MMS_VERSION = 0x8D;
+ public static final int MESSAGE_SIZE = 0x8E;
+ public static final int PRIORITY = 0x8F;
+
+ public static final int READ_REPLY = 0x90;
+ public static final int READ_REPORT = 0x90;
+ public static final int REPORT_ALLOWED = 0x91;
+ public static final int RESPONSE_STATUS = 0x92;
+ public static final int RESPONSE_TEXT = 0x93;
+ public static final int SENDER_VISIBILITY = 0x94;
+ public static final int STATUS = 0x95;
+ public static final int SUBJECT = 0x96;
+ public static final int TO = 0x97;
+ public static final int TRANSACTION_ID = 0x98;
+ public static final int RETRIEVE_STATUS = 0x99;
+ public static final int RETRIEVE_TEXT = 0x9A;
+ public static final int READ_STATUS = 0x9B;
+ public static final int REPLY_CHARGING = 0x9C;
+ public static final int REPLY_CHARGING_DEADLINE = 0x9D;
+ public static final int REPLY_CHARGING_ID = 0x9E;
+ public static final int REPLY_CHARGING_SIZE = 0x9F;
+
+ public static final int PREVIOUSLY_SENT_BY = 0xA0;
+ public static final int PREVIOUSLY_SENT_DATE = 0xA1;
+ public static final int STORE = 0xA2;
+ public static final int MM_STATE = 0xA3;
+ public static final int MM_FLAGS = 0xA4;
+ public static final int STORE_STATUS = 0xA5;
+ public static final int STORE_STATUS_TEXT = 0xA6;
+ public static final int STORED = 0xA7;
+ public static final int ATTRIBUTES = 0xA8;
+ public static final int TOTALS = 0xA9;
+ public static final int MBOX_TOTALS = 0xAA;
+ public static final int QUOTAS = 0xAB;
+ public static final int MBOX_QUOTAS = 0xAC;
+ public static final int MESSAGE_COUNT = 0xAD;
+ public static final int CONTENT = 0xAE;
+ public static final int START = 0xAF;
+
+ public static final int ADDITIONAL_HEADERS = 0xB0;
+ public static final int DISTRIBUTION_INDICATOR = 0xB1;
+ public static final int ELEMENT_DESCRIPTOR = 0xB2;
+ public static final int LIMIT = 0xB3;
+ public static final int RECOMMENDED_RETRIEVAL_MODE = 0xB4;
+ public static final int RECOMMENDED_RETRIEVAL_MODE_TEXT = 0xB5;
+ public static final int STATUS_TEXT = 0xB6;
+ public static final int APPLIC_ID = 0xB7;
+ public static final int REPLY_APPLIC_ID = 0xB8;
+ public static final int AUX_APPLIC_ID = 0xB9;
+ public static final int CONTENT_CLASS = 0xBA;
+ public static final int DRM_CONTENT = 0xBB;
+ public static final int ADAPTATION_ALLOWED = 0xBC;
+ public static final int REPLACE_ID = 0xBD;
+ public static final int CANCEL_ID = 0xBE;
+ public static final int CANCEL_STATUS = 0xBF;
+
+ /**
+ * X-Mms-Message-Type field types.
+ */
+ public static final int MESSAGE_TYPE_SEND_REQ = 0x80;
+ public static final int MESSAGE_TYPE_SEND_CONF = 0x81;
+ public static final int MESSAGE_TYPE_NOTIFICATION_IND = 0x82;
+ public static final int MESSAGE_TYPE_NOTIFYRESP_IND = 0x83;
+ public static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84;
+ public static final int MESSAGE_TYPE_ACKNOWLEDGE_IND = 0x85;
+ public static final int MESSAGE_TYPE_DELIVERY_IND = 0x86;
+ public static final int MESSAGE_TYPE_READ_REC_IND = 0x87;
+ public static final int MESSAGE_TYPE_READ_ORIG_IND = 0x88;
+ public static final int MESSAGE_TYPE_FORWARD_REQ = 0x89;
+ public static final int MESSAGE_TYPE_FORWARD_CONF = 0x8A;
+ public static final int MESSAGE_TYPE_MBOX_STORE_REQ = 0x8B;
+ public static final int MESSAGE_TYPE_MBOX_STORE_CONF = 0x8C;
+ public static final int MESSAGE_TYPE_MBOX_VIEW_REQ = 0x8D;
+ public static final int MESSAGE_TYPE_MBOX_VIEW_CONF = 0x8E;
+ public static final int MESSAGE_TYPE_MBOX_UPLOAD_REQ = 0x8F;
+ public static final int MESSAGE_TYPE_MBOX_UPLOAD_CONF = 0x90;
+ public static final int MESSAGE_TYPE_MBOX_DELETE_REQ = 0x91;
+ public static final int MESSAGE_TYPE_MBOX_DELETE_CONF = 0x92;
+ public static final int MESSAGE_TYPE_MBOX_DESCR = 0x93;
+ public static final int MESSAGE_TYPE_DELETE_REQ = 0x94;
+ public static final int MESSAGE_TYPE_DELETE_CONF = 0x95;
+ public static final int MESSAGE_TYPE_CANCEL_REQ = 0x96;
+ public static final int MESSAGE_TYPE_CANCEL_CONF = 0x97;
+
+ /**
+ * X-Mms-Delivery-Report |
+ * X-Mms-Read-Report |
+ * X-Mms-Report-Allowed |
+ * X-Mms-Sender-Visibility |
+ * X-Mms-Store |
+ * X-Mms-Stored |
+ * X-Mms-Totals |
+ * X-Mms-Quotas |
+ * X-Mms-Distribution-Indicator |
+ * X-Mms-DRM-Content |
+ * X-Mms-Adaptation-Allowed |
+ * field types.
+ */
+ public static final int VALUE_YES = 0x80;
+ public static final int VALUE_NO = 0x81;
+
+ /**
+ * Delivery-Time |
+ * Expiry and Reply-Charging-Deadline |
+ * field type components.
+ */
+ public static final int VALUE_ABSOLUTE_TOKEN = 0x80;
+ public static final int VALUE_RELATIVE_TOKEN = 0x81;
+
+ /**
+ * X-Mms-MMS-Version field types.
+ */
+ public static final int MMS_VERSION_1_3 = ((1 << 4) | 3);
+ public static final int MMS_VERSION_1_2 = ((1 << 4) | 2);
+ public static final int MMS_VERSION_1_1 = ((1 << 4) | 1);
+ public static final int MMS_VERSION_1_0 = ((1 << 4) | 0);
+
+ // Current version is 1.2.
+ public static final int CURRENT_MMS_VERSION = MMS_VERSION_1_2;
+
+ /**
+ * From field type components.
+ */
+ public static final int FROM_ADDRESS_PRESENT_TOKEN = 0x80;
+ public static final int FROM_INSERT_ADDRESS_TOKEN = 0x81;
+
+ public static final String FROM_ADDRESS_PRESENT_TOKEN_STR = "address-present-token";
+ public static final String FROM_INSERT_ADDRESS_TOKEN_STR = "insert-address-token";
+
+ /**
+ * X-Mms-Status Field.
+ */
+ public static final int STATUS_EXPIRED = 0x80;
+ public static final int STATUS_RETRIEVED = 0x81;
+ public static final int STATUS_REJECTED = 0x82;
+ public static final int STATUS_DEFERRED = 0x83;
+ public static final int STATUS_UNRECOGNIZED = 0x84;
+ public static final int STATUS_INDETERMINATE = 0x85;
+ public static final int STATUS_FORWARDED = 0x86;
+ public static final int STATUS_UNREACHABLE = 0x87;
+
+ /**
+ * MM-Flags field type components.
+ */
+ public static final int MM_FLAGS_ADD_TOKEN = 0x80;
+ public static final int MM_FLAGS_REMOVE_TOKEN = 0x81;
+ public static final int MM_FLAGS_FILTER_TOKEN = 0x82;
+
+ /**
+ * X-Mms-Message-Class field types.
+ */
+ public static final int MESSAGE_CLASS_PERSONAL = 0x80;
+ public static final int MESSAGE_CLASS_ADVERTISEMENT = 0x81;
+ public static final int MESSAGE_CLASS_INFORMATIONAL = 0x82;
+ public static final int MESSAGE_CLASS_AUTO = 0x83;
+
+ public static final String MESSAGE_CLASS_PERSONAL_STR = "personal";
+ public static final String MESSAGE_CLASS_ADVERTISEMENT_STR = "advertisement";
+ public static final String MESSAGE_CLASS_INFORMATIONAL_STR = "informational";
+ public static final String MESSAGE_CLASS_AUTO_STR = "auto";
+
+ /**
+ * X-Mms-Priority field types.
+ */
+ public static final int PRIORITY_LOW = 0x80;
+ public static final int PRIORITY_NORMAL = 0x81;
+ public static final int PRIORITY_HIGH = 0x82;
+
+ /**
+ * X-Mms-Response-Status field types.
+ */
+ public static final int RESPONSE_STATUS_OK = 0x80;
+ public static final int RESPONSE_STATUS_ERROR_UNSPECIFIED = 0x81;
+ public static final int RESPONSE_STATUS_ERROR_SERVICE_DENIED = 0x82;
+
+ public static final int RESPONSE_STATUS_ERROR_MESSAGE_FORMAT_CORRUPT = 0x83;
+ public static final int RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED = 0x84;
+
+ public static final int RESPONSE_STATUS_ERROR_MESSAGE_NOT_FOUND = 0x85;
+ public static final int RESPONSE_STATUS_ERROR_NETWORK_PROBLEM = 0x86;
+ public static final int RESPONSE_STATUS_ERROR_CONTENT_NOT_ACCEPTED = 0x87;
+ public static final int RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE = 0x88;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
+
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_SENDNG_ADDRESS_UNRESOLVED = 0xC1;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC2;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC3;
+ public static final int RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS = 0xC4;
+
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_SENDING_ADDRESS_UNRESOLVED = 0xE3;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE4;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_CONTENT_NOT_ACCEPTED = 0xE5;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_LIMITATIONS_NOT_MET = 0xE6;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_REQUEST_NOT_ACCEPTED = 0xE6;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_FORWARDING_DENIED = 0xE8;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_NOT_SUPPORTED = 0xE9;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_ADDRESS_HIDING_NOT_SUPPORTED = 0xEA;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID = 0xEB;
+ public static final int RESPONSE_STATUS_ERROR_PERMANENT_END = 0xFF;
+
+ /**
+ * X-Mms-Retrieve-Status field types.
+ */
+ public static final int RETRIEVE_STATUS_OK = 0x80;
+ public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
+ public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC1;
+ public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC2;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE2;
+ public static final int RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED = 0xE3;
+ public static final int RETRIEVE_STATUS_ERROR_END = 0xFF;
+
+ /**
+ * X-Mms-Sender-Visibility field types.
+ */
+ public static final int SENDER_VISIBILITY_HIDE = 0x80;
+ public static final int SENDER_VISIBILITY_SHOW = 0x81;
+
+ /**
+ * X-Mms-Read-Status field types.
+ */
+ public static final int READ_STATUS_READ = 0x80;
+ public static final int READ_STATUS__DELETED_WITHOUT_BEING_READ = 0x81;
+
+ /**
+ * X-Mms-Cancel-Status field types.
+ */
+ public static final int CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED = 0x80;
+ public static final int CANCEL_STATUS_REQUEST_CORRUPTED = 0x81;
+
+ /**
+ * X-Mms-Reply-Charging field types.
+ */
+ public static final int REPLY_CHARGING_REQUESTED = 0x80;
+ public static final int REPLY_CHARGING_REQUESTED_TEXT_ONLY = 0x81;
+ public static final int REPLY_CHARGING_ACCEPTED = 0x82;
+ public static final int REPLY_CHARGING_ACCEPTED_TEXT_ONLY = 0x83;
+
+ /**
+ * X-Mms-MM-State field types.
+ */
+ public static final int MM_STATE_DRAFT = 0x80;
+ public static final int MM_STATE_SENT = 0x81;
+ public static final int MM_STATE_NEW = 0x82;
+ public static final int MM_STATE_RETRIEVED = 0x83;
+ public static final int MM_STATE_FORWARDED = 0x84;
+
+ /**
+ * X-Mms-Recommended-Retrieval-Mode field types.
+ */
+ public static final int RECOMMENDED_RETRIEVAL_MODE_MANUAL = 0x80;
+
+ /**
+ * X-Mms-Content-Class field types.
+ */
+ public static final int CONTENT_CLASS_TEXT = 0x80;
+ public static final int CONTENT_CLASS_IMAGE_BASIC = 0x81;
+ public static final int CONTENT_CLASS_IMAGE_RICH = 0x82;
+ public static final int CONTENT_CLASS_VIDEO_BASIC = 0x83;
+ public static final int CONTENT_CLASS_VIDEO_RICH = 0x84;
+ public static final int CONTENT_CLASS_MEGAPIXEL = 0x85;
+ public static final int CONTENT_CLASS_CONTENT_BASIC = 0x86;
+ public static final int CONTENT_CLASS_CONTENT_RICH = 0x87;
+
+ /**
+ * X-Mms-Store-Status field types.
+ */
+ public static final int STORE_STATUS_SUCCESS = 0x80;
+ public static final int STORE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0;
+ public static final int STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC1;
+ public static final int STORE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0;
+ public static final int STORE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1;
+ public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2;
+ public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE3;
+ public static final int STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL = 0xE4;
+ public static final int STORE_STATUS_ERROR_END = 0xFF;
+
+ /**
+ * The map contains the value of all headers.
+ */
+ private HashMap<Integer, Object> mHeaderMap = null;
+
+ /**
+ * Constructor of PduHeaders.
+ */
+ @UnsupportedAppUsage
+ public PduHeaders() {
+ mHeaderMap = new HashMap<Integer, Object>();
+ }
+
+ /**
+ * Get octet value by header field.
+ *
+ * @param field the field
+ * @return the octet value of the pdu header
+ * with specified header field. Return 0 if
+ * the value is not set.
+ */
+ @UnsupportedAppUsage
+ protected int getOctet(int field) {
+ Integer octet = (Integer) mHeaderMap.get(field);
+ if (null == octet) {
+ return 0;
+ }
+
+ return octet;
+ }
+
+ /**
+ * Set octet value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ protected void setOctet(int value, int field)
+ throws InvalidHeaderValueException{
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ switch (field) {
+ case REPORT_ALLOWED:
+ case ADAPTATION_ALLOWED:
+ case DELIVERY_REPORT:
+ case DRM_CONTENT:
+ case DISTRIBUTION_INDICATOR:
+ case QUOTAS:
+ case READ_REPORT:
+ case STORE:
+ case STORED:
+ case TOTALS:
+ case SENDER_VISIBILITY:
+ if ((VALUE_YES != value) && (VALUE_NO != value)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case READ_STATUS:
+ if ((READ_STATUS_READ != value) &&
+ (READ_STATUS__DELETED_WITHOUT_BEING_READ != value)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case CANCEL_STATUS:
+ if ((CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED != value) &&
+ (CANCEL_STATUS_REQUEST_CORRUPTED != value)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case PRIORITY:
+ if ((value < PRIORITY_LOW) || (value > PRIORITY_HIGH)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case STATUS:
+ if ((value < STATUS_EXPIRED) || (value > STATUS_UNREACHABLE)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case REPLY_CHARGING:
+ if ((value < REPLY_CHARGING_REQUESTED)
+ || (value > REPLY_CHARGING_ACCEPTED_TEXT_ONLY)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case MM_STATE:
+ if ((value < MM_STATE_DRAFT) || (value > MM_STATE_FORWARDED)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case RECOMMENDED_RETRIEVAL_MODE:
+ if (RECOMMENDED_RETRIEVAL_MODE_MANUAL != value) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case CONTENT_CLASS:
+ if ((value < CONTENT_CLASS_TEXT)
+ || (value > CONTENT_CLASS_CONTENT_RICH)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ case RETRIEVE_STATUS:
+ // According to oma-ts-mms-enc-v1_3, section 7.3.50, we modify the invalid value.
+ if ((value > RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) &&
+ (value < RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE)) {
+ value = RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE;
+ } else if ((value > RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED) &&
+ (value <= RETRIEVE_STATUS_ERROR_END)) {
+ value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE;
+ } else if ((value < RETRIEVE_STATUS_OK) ||
+ ((value > RETRIEVE_STATUS_OK) &&
+ (value < RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
+ (value > RETRIEVE_STATUS_ERROR_END)) {
+ value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE;
+ }
+ break;
+ case STORE_STATUS:
+ // According to oma-ts-mms-enc-v1_3, section 7.3.58, we modify the invalid value.
+ if ((value > STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) &&
+ (value < STORE_STATUS_ERROR_PERMANENT_FAILURE)) {
+ value = STORE_STATUS_ERROR_TRANSIENT_FAILURE;
+ } else if ((value > STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL) &&
+ (value <= STORE_STATUS_ERROR_END)) {
+ value = STORE_STATUS_ERROR_PERMANENT_FAILURE;
+ } else if ((value < STORE_STATUS_SUCCESS) ||
+ ((value > STORE_STATUS_SUCCESS) &&
+ (value < STORE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
+ (value > STORE_STATUS_ERROR_END)) {
+ value = STORE_STATUS_ERROR_PERMANENT_FAILURE;
+ }
+ break;
+ case RESPONSE_STATUS:
+ // According to oma-ts-mms-enc-v1_3, section 7.3.48, we modify the invalid value.
+ if ((value > RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS) &&
+ (value < RESPONSE_STATUS_ERROR_PERMANENT_FAILURE)) {
+ value = RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE;
+ } else if (((value > RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID) &&
+ (value <= RESPONSE_STATUS_ERROR_PERMANENT_END)) ||
+ (value < RESPONSE_STATUS_OK) ||
+ ((value > RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE) &&
+ (value < RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE)) ||
+ (value > RESPONSE_STATUS_ERROR_PERMANENT_END)) {
+ value = RESPONSE_STATUS_ERROR_PERMANENT_FAILURE;
+ }
+ break;
+ case MMS_VERSION:
+ if ((value < MMS_VERSION_1_0)|| (value > MMS_VERSION_1_3)) {
+ value = CURRENT_MMS_VERSION; // Current version is the default value.
+ }
+ break;
+ case MESSAGE_TYPE:
+ if ((value < MESSAGE_TYPE_SEND_REQ) || (value > MESSAGE_TYPE_CANCEL_CONF)) {
+ // Invalid value.
+ throw new InvalidHeaderValueException("Invalid Octet value!");
+ }
+ break;
+ default:
+ // This header value should not be Octect.
+ throw new RuntimeException("Invalid header field!");
+ }
+ mHeaderMap.put(field, value);
+ }
+
+ /**
+ * Get TextString value by header field.
+ *
+ * @param field the field
+ * @return the TextString value of the pdu header
+ * with specified header field
+ */
+ @UnsupportedAppUsage
+ protected byte[] getTextString(int field) {
+ return (byte[]) mHeaderMap.get(field);
+ }
+
+ /**
+ * Set TextString value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ * @return the TextString value of the pdu header
+ * with specified header field
+ * @throws NullPointerException if the value is null.
+ */
+ protected void setTextString(byte[] value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case TRANSACTION_ID:
+ case REPLY_CHARGING_ID:
+ case AUX_APPLIC_ID:
+ case APPLIC_ID:
+ case REPLY_APPLIC_ID:
+ case MESSAGE_ID:
+ case REPLACE_ID:
+ case CANCEL_ID:
+ case CONTENT_LOCATION:
+ case MESSAGE_CLASS:
+ case CONTENT_TYPE:
+ break;
+ default:
+ // This header value should not be Text-String.
+ throw new RuntimeException("Invalid header field!");
+ }
+ mHeaderMap.put(field, value);
+ }
+
+ /**
+ * Get EncodedStringValue value by header field.
+ *
+ * @param field the field
+ * @return the EncodedStringValue value of the pdu header
+ * with specified header field
+ */
+ @UnsupportedAppUsage
+ protected EncodedStringValue getEncodedStringValue(int field) {
+ return (EncodedStringValue) mHeaderMap.get(field);
+ }
+
+ /**
+ * Get TO, CC or BCC header value.
+ *
+ * @param field the field
+ * @return the EncodeStringValue array of the pdu header
+ * with specified header field
+ */
+ @UnsupportedAppUsage
+ protected EncodedStringValue[] getEncodedStringValues(int field) {
+ ArrayList<EncodedStringValue> list =
+ (ArrayList<EncodedStringValue>) mHeaderMap.get(field);
+ if (null == list) {
+ return null;
+ }
+ EncodedStringValue[] values = new EncodedStringValue[list.size()];
+ return list.toArray(values);
+ }
+
+ /**
+ * Set EncodedStringValue value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ * @return the EncodedStringValue value of the pdu header
+ * with specified header field
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ protected void setEncodedStringValue(EncodedStringValue value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case SUBJECT:
+ case RECOMMENDED_RETRIEVAL_MODE_TEXT:
+ case RETRIEVE_TEXT:
+ case STATUS_TEXT:
+ case STORE_STATUS_TEXT:
+ case RESPONSE_TEXT:
+ case FROM:
+ case PREVIOUSLY_SENT_BY:
+ case MM_FLAGS:
+ break;
+ default:
+ // This header value should not be Encoded-String-Value.
+ throw new RuntimeException("Invalid header field!");
+ }
+
+ mHeaderMap.put(field, value);
+ }
+
+ /**
+ * Set TO, CC or BCC header value.
+ *
+ * @param value the value
+ * @param field the field
+ * @return the EncodedStringValue value array of the pdu header
+ * with specified header field
+ * @throws NullPointerException if the value is null.
+ */
+ protected void setEncodedStringValues(EncodedStringValue[] value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case BCC:
+ case CC:
+ case TO:
+ break;
+ default:
+ // This header value should not be Encoded-String-Value.
+ throw new RuntimeException("Invalid header field!");
+ }
+
+ ArrayList<EncodedStringValue> list = new ArrayList<EncodedStringValue>();
+ for (int i = 0; i < value.length; i++) {
+ list.add(value[i]);
+ }
+ mHeaderMap.put(field, list);
+ }
+
+ /**
+ * Append one EncodedStringValue to another.
+ *
+ * @param value the EncodedStringValue to append
+ * @param field the field
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ protected void appendEncodedStringValue(EncodedStringValue value,
+ int field) {
+ if (null == value) {
+ throw new NullPointerException();
+ }
+
+ switch (field) {
+ case BCC:
+ case CC:
+ case TO:
+ break;
+ default:
+ throw new RuntimeException("Invalid header field!");
+ }
+
+ ArrayList<EncodedStringValue> list =
+ (ArrayList<EncodedStringValue>) mHeaderMap.get(field);
+ if (null == list) {
+ list = new ArrayList<EncodedStringValue>();
+ }
+ list.add(value);
+ mHeaderMap.put(field, list);
+ }
+
+ /**
+ * Get LongInteger value by header field.
+ *
+ * @param field the field
+ * @return the LongInteger value of the pdu header
+ * with specified header field. if return -1, the
+ * field is not existed in pdu header.
+ */
+ @UnsupportedAppUsage
+ protected long getLongInteger(int field) {
+ Long longInteger = (Long) mHeaderMap.get(field);
+ if (null == longInteger) {
+ return -1;
+ }
+
+ return longInteger.longValue();
+ }
+
+ /**
+ * Set LongInteger value to pdu header by header field.
+ *
+ * @param value the value
+ * @param field the field
+ */
+ @UnsupportedAppUsage
+ protected void setLongInteger(long value, int field) {
+ /**
+ * Check whether this field can be set for specific
+ * header and check validity of the field.
+ */
+ switch (field) {
+ case DATE:
+ case REPLY_CHARGING_SIZE:
+ case MESSAGE_SIZE:
+ case MESSAGE_COUNT:
+ case START:
+ case LIMIT:
+ case DELIVERY_TIME:
+ case EXPIRY:
+ case REPLY_CHARGING_DEADLINE:
+ case PREVIOUSLY_SENT_DATE:
+ break;
+ default:
+ // This header value should not be LongInteger.
+ throw new RuntimeException("Invalid header field!");
+ }
+ mHeaderMap.put(field, value);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduParser.java b/telephony/java/com/google/android/mms/pdu/PduParser.java
new file mode 100755
index 000000000000..f48399410723
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduParser.java
@@ -0,0 +1,2023 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.ContentType;
+import com.google.android.mms.InvalidHeaderValueException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public class PduParser {
+ /**
+ * The next are WAP values defined in WSP specification.
+ */
+ private static final int QUOTE = 127;
+ private static final int LENGTH_QUOTE = 31;
+ private static final int TEXT_MIN = 32;
+ private static final int TEXT_MAX = 127;
+ private static final int SHORT_INTEGER_MAX = 127;
+ private static final int SHORT_LENGTH_MAX = 30;
+ private static final int LONG_INTEGER_LENGTH_MAX = 8;
+ private static final int QUOTED_STRING_FLAG = 34;
+ private static final int END_STRING_FLAG = 0x00;
+ //The next two are used by the interface "parseWapString" to
+ //distinguish Text-String and Quoted-String.
+ private static final int TYPE_TEXT_STRING = 0;
+ private static final int TYPE_QUOTED_STRING = 1;
+ private static final int TYPE_TOKEN_STRING = 2;
+
+ /**
+ * Specify the part position.
+ */
+ private static final int THE_FIRST_PART = 0;
+ private static final int THE_LAST_PART = 1;
+
+ /**
+ * The pdu data.
+ */
+ private ByteArrayInputStream mPduDataStream = null;
+
+ /**
+ * Store pdu headers
+ */
+ private PduHeaders mHeaders = null;
+
+ /**
+ * Store pdu parts.
+ */
+ private PduBody mBody = null;
+
+ /**
+ * Store the "type" parameter in "Content-Type" header field.
+ */
+ private static byte[] mTypeParam = null;
+
+ /**
+ * Store the "start" parameter in "Content-Type" header field.
+ */
+ private static byte[] mStartParam = null;
+
+ /**
+ * The log tag.
+ */
+ private static final String LOG_TAG = "PduParser";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ /**
+ * Whether to parse content-disposition part header
+ */
+ private final boolean mParseContentDisposition;
+
+ /**
+ * Constructor.
+ *
+ * @param pduDataStream pdu data to be parsed
+ * @param parseContentDisposition whether to parse the Content-Disposition part header
+ */
+ @UnsupportedAppUsage
+ public PduParser(byte[] pduDataStream, boolean parseContentDisposition) {
+ mPduDataStream = new ByteArrayInputStream(pduDataStream);
+ mParseContentDisposition = parseContentDisposition;
+ }
+
+ /**
+ * Parse the pdu.
+ *
+ * @return the pdu structure if parsing successfully.
+ * null if parsing error happened or mandatory fields are not set.
+ */
+ @UnsupportedAppUsage
+ public GenericPdu parse(){
+ if (mPduDataStream == null) {
+ return null;
+ }
+
+ /* parse headers */
+ mHeaders = parseHeaders(mPduDataStream);
+ if (null == mHeaders) {
+ // Parse headers failed.
+ return null;
+ }
+
+ /* get the message type */
+ int messageType = mHeaders.getOctet(PduHeaders.MESSAGE_TYPE);
+
+ /* check mandatory header fields */
+ if (false == checkMandatoryHeader(mHeaders)) {
+ log("check mandatory headers failed!");
+ return null;
+ }
+
+ if ((PduHeaders.MESSAGE_TYPE_SEND_REQ == messageType) ||
+ (PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF == messageType)) {
+ /* need to parse the parts */
+ mBody = parseParts(mPduDataStream);
+ if (null == mBody) {
+ // Parse parts failed.
+ return null;
+ }
+ }
+
+ switch (messageType) {
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_REQ");
+ }
+ SendReq sendReq = new SendReq(mHeaders, mBody);
+ return sendReq;
+ case PduHeaders.MESSAGE_TYPE_SEND_CONF:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_SEND_CONF");
+ }
+ SendConf sendConf = new SendConf(mHeaders);
+ return sendConf;
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFICATION_IND");
+ }
+ NotificationInd notificationInd =
+ new NotificationInd(mHeaders);
+ return notificationInd;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_NOTIFYRESP_IND");
+ }
+ NotifyRespInd notifyRespInd =
+ new NotifyRespInd(mHeaders);
+ return notifyRespInd;
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_RETRIEVE_CONF");
+ }
+ RetrieveConf retrieveConf =
+ new RetrieveConf(mHeaders, mBody);
+
+ byte[] contentType = retrieveConf.getContentType();
+ if (null == contentType) {
+ return null;
+ }
+ String ctTypeStr = new String(contentType);
+ if (ctTypeStr.equals(ContentType.MULTIPART_MIXED)
+ || ctTypeStr.equals(ContentType.MULTIPART_RELATED)
+ || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
+ // The MMS content type must be "application/vnd.wap.multipart.mixed"
+ // or "application/vnd.wap.multipart.related"
+ // or "application/vnd.wap.multipart.alternative"
+ return retrieveConf;
+ } else if (ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
+ // "application/vnd.wap.multipart.alternative"
+ // should take only the first part.
+ PduPart firstPart = mBody.getPart(0);
+ mBody.removeAll();
+ mBody.addPart(0, firstPart);
+ return retrieveConf;
+ }
+ return null;
+ case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_DELIVERY_IND");
+ }
+ DeliveryInd deliveryInd =
+ new DeliveryInd(mHeaders);
+ return deliveryInd;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_ACKNOWLEDGE_IND");
+ }
+ AcknowledgeInd acknowledgeInd =
+ new AcknowledgeInd(mHeaders);
+ return acknowledgeInd;
+ case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_ORIG_IND");
+ }
+ ReadOrigInd readOrigInd =
+ new ReadOrigInd(mHeaders);
+ return readOrigInd;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parse: MESSAGE_TYPE_READ_REC_IND");
+ }
+ ReadRecInd readRecInd =
+ new ReadRecInd(mHeaders);
+ return readRecInd;
+ default:
+ log("Parser doesn't support this message type in this version!");
+ return null;
+ }
+ }
+
+ /**
+ * Parse pdu headers.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return headers in PduHeaders structure, null when parse fail
+ */
+ protected PduHeaders parseHeaders(ByteArrayInputStream pduDataStream){
+ if (pduDataStream == null) {
+ return null;
+ }
+ boolean keepParsing = true;
+ PduHeaders headers = new PduHeaders();
+
+ while (keepParsing && (pduDataStream.available() > 0)) {
+ pduDataStream.mark(1);
+ int headerField = extractByteValue(pduDataStream);
+ /* parse custom text header */
+ if ((headerField >= TEXT_MIN) && (headerField <= TEXT_MAX)) {
+ pduDataStream.reset();
+ byte [] bVal = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "TextHeader: " + new String(bVal));
+ }
+ /* we should ignore it at the moment */
+ continue;
+ }
+ switch (headerField) {
+ case PduHeaders.MESSAGE_TYPE:
+ {
+ int messageType = extractByteValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: messageType: " + messageType);
+ }
+ switch (messageType) {
+ // We don't support these kind of messages now.
+ case PduHeaders.MESSAGE_TYPE_FORWARD_REQ:
+ case PduHeaders.MESSAGE_TYPE_FORWARD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DESCR:
+ case PduHeaders.MESSAGE_TYPE_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_REQ:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_CONF:
+ return null;
+ }
+ try {
+ headers.setOctet(messageType, headerField);
+ } catch(InvalidHeaderValueException e) {
+ log("Set invalid Octet value: " + messageType +
+ " into the header filed: " + headerField);
+ return null;
+ } catch(RuntimeException e) {
+ log(headerField + "is not Octet header field!");
+ return null;
+ }
+ break;
+ }
+ /* Octect value */
+ case PduHeaders.REPORT_ALLOWED:
+ case PduHeaders.ADAPTATION_ALLOWED:
+ case PduHeaders.DELIVERY_REPORT:
+ case PduHeaders.DRM_CONTENT:
+ case PduHeaders.DISTRIBUTION_INDICATOR:
+ case PduHeaders.QUOTAS:
+ case PduHeaders.READ_REPORT:
+ case PduHeaders.STORE:
+ case PduHeaders.STORED:
+ case PduHeaders.TOTALS:
+ case PduHeaders.SENDER_VISIBILITY:
+ case PduHeaders.READ_STATUS:
+ case PduHeaders.CANCEL_STATUS:
+ case PduHeaders.PRIORITY:
+ case PduHeaders.STATUS:
+ case PduHeaders.REPLY_CHARGING:
+ case PduHeaders.MM_STATE:
+ case PduHeaders.RECOMMENDED_RETRIEVAL_MODE:
+ case PduHeaders.CONTENT_CLASS:
+ case PduHeaders.RETRIEVE_STATUS:
+ case PduHeaders.STORE_STATUS:
+ /**
+ * The following field has a different value when
+ * used in the M-Mbox-Delete.conf and M-Delete.conf PDU.
+ * For now we ignore this fact, since we do not support these PDUs
+ */
+ case PduHeaders.RESPONSE_STATUS:
+ {
+ int value = extractByteValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: byte: " + headerField + " value: " +
+ value);
+ }
+
+ try {
+ headers.setOctet(value, headerField);
+ } catch(InvalidHeaderValueException e) {
+ log("Set invalid Octet value: " + value +
+ " into the header filed: " + headerField);
+ return null;
+ } catch(RuntimeException e) {
+ log(headerField + "is not Octet header field!");
+ return null;
+ }
+ break;
+ }
+
+ /* Long-Integer */
+ case PduHeaders.DATE:
+ case PduHeaders.REPLY_CHARGING_SIZE:
+ case PduHeaders.MESSAGE_SIZE:
+ {
+ try {
+ long value = parseLongInteger(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: longint: " + headerField + " value: " +
+ value);
+ }
+ headers.setLongInteger(value, headerField);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ /* Integer-Value */
+ case PduHeaders.MESSAGE_COUNT:
+ case PduHeaders.START:
+ case PduHeaders.LIMIT:
+ {
+ try {
+ long value = parseIntegerValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: int: " + headerField + " value: " +
+ value);
+ }
+ headers.setLongInteger(value, headerField);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ /* Text-String */
+ case PduHeaders.TRANSACTION_ID:
+ case PduHeaders.REPLY_CHARGING_ID:
+ case PduHeaders.AUX_APPLIC_ID:
+ case PduHeaders.APPLIC_ID:
+ case PduHeaders.REPLY_APPLIC_ID:
+ /**
+ * The next three header fields are email addresses
+ * as defined in RFC2822,
+ * not including the characters "<" and ">"
+ */
+ case PduHeaders.MESSAGE_ID:
+ case PduHeaders.REPLACE_ID:
+ case PduHeaders.CANCEL_ID:
+ /**
+ * The following field has a different value when
+ * used in the M-Mbox-Delete.conf and M-Delete.conf PDU.
+ * For now we ignore this fact, since we do not support these PDUs
+ */
+ case PduHeaders.CONTENT_LOCATION:
+ {
+ byte[] value = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (null != value) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: string: " + headerField + " value: " +
+ new String(value));
+ }
+ headers.setTextString(value, headerField);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ /* Encoded-string-value */
+ case PduHeaders.SUBJECT:
+ case PduHeaders.RECOMMENDED_RETRIEVAL_MODE_TEXT:
+ case PduHeaders.RETRIEVE_TEXT:
+ case PduHeaders.STATUS_TEXT:
+ case PduHeaders.STORE_STATUS_TEXT:
+ /* the next one is not support
+ * M-Mbox-Delete.conf and M-Delete.conf now */
+ case PduHeaders.RESPONSE_TEXT:
+ {
+ EncodedStringValue value =
+ parseEncodedStringValue(pduDataStream);
+ if (null != value) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: encoded string: " + headerField
+ + " value: " + value.getString());
+ }
+ headers.setEncodedStringValue(value, headerField);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch (RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ /* Addressing model */
+ case PduHeaders.BCC:
+ case PduHeaders.CC:
+ case PduHeaders.TO:
+ {
+ EncodedStringValue value =
+ parseEncodedStringValue(pduDataStream);
+ if (null != value) {
+ byte[] address = value.getTextString();
+ if (null != address) {
+ String str = new String(address);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: (to/cc/bcc) address: " + headerField
+ + " value: " + str);
+ }
+ int endIndex = str.indexOf("/");
+ if (endIndex > 0) {
+ str = str.substring(0, endIndex);
+ }
+ try {
+ value.setTextString(str.getBytes());
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ return null;
+ }
+ }
+
+ try {
+ headers.appendEncodedStringValue(value, headerField);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ /* Value-length
+ * (Absolute-token Date-value | Relative-token Delta-seconds-value) */
+ case PduHeaders.DELIVERY_TIME:
+ case PduHeaders.EXPIRY:
+ case PduHeaders.REPLY_CHARGING_DEADLINE:
+ {
+ /* parse Value-length */
+ parseValueLength(pduDataStream);
+
+ /* Absolute-token or Relative-token */
+ int token = extractByteValue(pduDataStream);
+
+ /* Date-value or Delta-seconds-value */
+ long timeValue;
+ try {
+ timeValue = parseLongInteger(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ if (PduHeaders.VALUE_RELATIVE_TOKEN == token) {
+ /* need to convert the Delta-seconds-value
+ * into Date-value */
+ timeValue = System.currentTimeMillis()/1000 + timeValue;
+ }
+
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: time value: " + headerField
+ + " value: " + timeValue);
+ }
+ headers.setLongInteger(timeValue, headerField);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.FROM: {
+ /* From-value =
+ * Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ */
+ EncodedStringValue from = null;
+ parseValueLength(pduDataStream); /* parse value-length */
+
+ /* Address-present-token or Insert-address-token */
+ int fromToken = extractByteValue(pduDataStream);
+
+ /* Address-present-token or Insert-address-token */
+ if (PduHeaders.FROM_ADDRESS_PRESENT_TOKEN == fromToken) {
+ /* Encoded-string-value */
+ from = parseEncodedStringValue(pduDataStream);
+ if (null != from) {
+ byte[] address = from.getTextString();
+ if (null != address) {
+ String str = new String(address);
+ int endIndex = str.indexOf("/");
+ if (endIndex > 0) {
+ str = str.substring(0, endIndex);
+ }
+ try {
+ from.setTextString(str.getBytes());
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ return null;
+ }
+ }
+ }
+ } else {
+ try {
+ from = new EncodedStringValue(
+ PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes());
+ } catch(NullPointerException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: from address: " + headerField
+ + " value: " + from.getString());
+ }
+ headers.setEncodedStringValue(from, PduHeaders.FROM);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.MESSAGE_CLASS: {
+ /* Message-class-value = Class-identifier | Token-text */
+ pduDataStream.mark(1);
+ int messageClass = extractByteValue(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MESSAGE_CLASS: " + headerField
+ + " value: " + messageClass);
+ }
+
+ if (messageClass >= PduHeaders.MESSAGE_CLASS_PERSONAL) {
+ /* Class-identifier */
+ try {
+ if (PduHeaders.MESSAGE_CLASS_PERSONAL == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ } else if (PduHeaders.MESSAGE_CLASS_ADVERTISEMENT == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ } else if (PduHeaders.MESSAGE_CLASS_INFORMATIONAL == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ } else if (PduHeaders.MESSAGE_CLASS_AUTO == messageClass) {
+ headers.setTextString(
+ PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes(),
+ PduHeaders.MESSAGE_CLASS);
+ }
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ } else {
+ /* Token-text */
+ pduDataStream.reset();
+ byte[] messageClassString = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (null != messageClassString) {
+ try {
+ headers.setTextString(messageClassString, PduHeaders.MESSAGE_CLASS);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ }
+ }
+ break;
+ }
+
+ case PduHeaders.MMS_VERSION: {
+ int version = parseShortInteger(pduDataStream);
+
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MMS_VERSION: " + headerField
+ + " value: " + version);
+ }
+ headers.setOctet(version, PduHeaders.MMS_VERSION);
+ } catch(InvalidHeaderValueException e) {
+ log("Set invalid Octet value: " + version +
+ " into the header filed: " + headerField);
+ return null;
+ } catch(RuntimeException e) {
+ log(headerField + "is not Octet header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.PREVIOUSLY_SENT_BY: {
+ /* Previously-sent-by-value =
+ * Value-length Forwarded-count-value Encoded-string-value */
+ /* parse value-length */
+ parseValueLength(pduDataStream);
+
+ /* parse Forwarded-count-value */
+ try {
+ parseIntegerValue(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + " is not Integer-Value");
+ return null;
+ }
+
+ /* parse Encoded-string-value */
+ EncodedStringValue previouslySentBy =
+ parseEncodedStringValue(pduDataStream);
+ if (null != previouslySentBy) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_BY: " + headerField
+ + " value: " + previouslySentBy.getString());
+ }
+ headers.setEncodedStringValue(previouslySentBy,
+ PduHeaders.PREVIOUSLY_SENT_BY);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Encoded-String-Value header field!");
+ return null;
+ }
+ }
+ break;
+ }
+
+ case PduHeaders.PREVIOUSLY_SENT_DATE: {
+ /* Previously-sent-date-value =
+ * Value-length Forwarded-count-value Date-value */
+ /* parse value-length */
+ parseValueLength(pduDataStream);
+
+ /* parse Forwarded-count-value */
+ try {
+ parseIntegerValue(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + " is not Integer-Value");
+ return null;
+ }
+
+ /* Date-value */
+ try {
+ long perviouslySentDate = parseLongInteger(pduDataStream);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: PREVIOUSLY_SENT_DATE: " + headerField
+ + " value: " + perviouslySentDate);
+ }
+ headers.setLongInteger(perviouslySentDate,
+ PduHeaders.PREVIOUSLY_SENT_DATE);
+ } catch(RuntimeException e) {
+ log(headerField + "is not Long-Integer header field!");
+ return null;
+ }
+ break;
+ }
+
+ case PduHeaders.MM_FLAGS: {
+ /* MM-flags-value =
+ * Value-length
+ * ( Add-token | Remove-token | Filter-token )
+ * Encoded-string-value
+ */
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MM_FLAGS: " + headerField
+ + " NOT REALLY SUPPORTED");
+ }
+
+ /* parse Value-length */
+ parseValueLength(pduDataStream);
+
+ /* Add-token | Remove-token | Filter-token */
+ extractByteValue(pduDataStream);
+
+ /* Encoded-string-value */
+ parseEncodedStringValue(pduDataStream);
+
+ /* not store this header filed in "headers",
+ * because now PduHeaders doesn't support it */
+ break;
+ }
+
+ /* Value-length
+ * (Message-total-token | Size-total-token) Integer-Value */
+ case PduHeaders.MBOX_TOTALS:
+ case PduHeaders.MBOX_QUOTAS:
+ {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: MBOX_TOTALS: " + headerField);
+ }
+ /* Value-length */
+ parseValueLength(pduDataStream);
+
+ /* Message-total-token | Size-total-token */
+ extractByteValue(pduDataStream);
+
+ /*Integer-Value*/
+ try {
+ parseIntegerValue(pduDataStream);
+ } catch(RuntimeException e) {
+ log(headerField + " is not Integer-Value");
+ return null;
+ }
+
+ /* not store these headers filed in "headers",
+ because now PduHeaders doesn't support them */
+ break;
+ }
+
+ case PduHeaders.ELEMENT_DESCRIPTOR: {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: ELEMENT_DESCRIPTOR: " + headerField);
+ }
+ parseContentType(pduDataStream, null);
+
+ /* not store this header filed in "headers",
+ because now PduHeaders doesn't support it */
+ break;
+ }
+
+ case PduHeaders.CONTENT_TYPE: {
+ HashMap<Integer, Object> map =
+ new HashMap<Integer, Object>();
+ byte[] contentType =
+ parseContentType(pduDataStream, map);
+
+ if (null != contentType) {
+ try {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: CONTENT_TYPE: " + headerField +
+ contentType.toString());
+ }
+ headers.setTextString(contentType, PduHeaders.CONTENT_TYPE);
+ } catch(NullPointerException e) {
+ log("null pointer error!");
+ } catch(RuntimeException e) {
+ log(headerField + "is not Text-String header field!");
+ return null;
+ }
+ }
+
+ /* get start parameter */
+ mStartParam = (byte[]) map.get(PduPart.P_START);
+
+ /* get charset parameter */
+ mTypeParam= (byte[]) map.get(PduPart.P_TYPE);
+
+ keepParsing = false;
+ break;
+ }
+
+ case PduHeaders.CONTENT:
+ case PduHeaders.ADDITIONAL_HEADERS:
+ case PduHeaders.ATTRIBUTES:
+ default: {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "parseHeaders: Unknown header: " + headerField);
+ }
+ log("Unknown header");
+ }
+ }
+ }
+
+ return headers;
+ }
+
+ /**
+ * Parse pdu parts.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return parts in PduBody structure
+ */
+ protected PduBody parseParts(ByteArrayInputStream pduDataStream) {
+ if (pduDataStream == null) {
+ return null;
+ }
+
+ int count = parseUnsignedInt(pduDataStream); // get the number of parts
+ PduBody body = new PduBody();
+
+ for (int i = 0 ; i < count ; i++) {
+ int headerLength = parseUnsignedInt(pduDataStream);
+ int dataLength = parseUnsignedInt(pduDataStream);
+ PduPart part = new PduPart();
+ int startPos = pduDataStream.available();
+ if (startPos <= 0) {
+ // Invalid part.
+ return null;
+ }
+
+ /* parse part's content-type */
+ HashMap<Integer, Object> map = new HashMap<Integer, Object>();
+ byte[] contentType = parseContentType(pduDataStream, map);
+ if (null != contentType) {
+ part.setContentType(contentType);
+ } else {
+ part.setContentType((PduContentTypes.contentTypes[0]).getBytes()); //"*/*"
+ }
+
+ /* get name parameter */
+ byte[] name = (byte[]) map.get(PduPart.P_NAME);
+ if (null != name) {
+ part.setName(name);
+ }
+
+ /* get charset parameter */
+ Integer charset = (Integer) map.get(PduPart.P_CHARSET);
+ if (null != charset) {
+ part.setCharset(charset);
+ }
+
+ /* parse part's headers */
+ int endPos = pduDataStream.available();
+ int partHeaderLen = headerLength - (startPos - endPos);
+ if (partHeaderLen > 0) {
+ if (false == parsePartHeaders(pduDataStream, part, partHeaderLen)) {
+ // Parse part header faild.
+ return null;
+ }
+ } else if (partHeaderLen < 0) {
+ // Invalid length of content-type.
+ return null;
+ }
+
+ /* FIXME: check content-id, name, filename and content location,
+ * if not set anyone of them, generate a default content-location
+ */
+ if ((null == part.getContentLocation())
+ && (null == part.getName())
+ && (null == part.getFilename())
+ && (null == part.getContentId())) {
+ part.setContentLocation(Long.toOctalString(
+ System.currentTimeMillis()).getBytes());
+ }
+
+ /* get part's data */
+ if (dataLength > 0) {
+ byte[] partData = new byte[dataLength];
+ String partContentType = new String(part.getContentType());
+ pduDataStream.read(partData, 0, dataLength);
+ if (partContentType.equalsIgnoreCase(ContentType.MULTIPART_ALTERNATIVE)) {
+ // parse "multipart/vnd.wap.multipart.alternative".
+ PduBody childBody = parseParts(new ByteArrayInputStream(partData));
+ // take the first part of children.
+ part = childBody.getPart(0);
+ } else {
+ // Check Content-Transfer-Encoding.
+ byte[] partDataEncoding = part.getContentTransferEncoding();
+ if (null != partDataEncoding) {
+ String encoding = new String(partDataEncoding);
+ if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) {
+ // Decode "base64" into "binary".
+ partData = Base64.decodeBase64(partData);
+ } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) {
+ // Decode "quoted-printable" into "binary".
+ partData = QuotedPrintable.decodeQuotedPrintable(partData);
+ } else {
+ // "binary" is the default encoding.
+ }
+ }
+ if (null == partData) {
+ log("Decode part data error!");
+ return null;
+ }
+ part.setData(partData);
+ }
+ }
+
+ /* add this part to body */
+ if (THE_FIRST_PART == checkPartPosition(part)) {
+ /* this is the first part */
+ body.addPart(0, part);
+ } else {
+ /* add the part to the end */
+ body.addPart(part);
+ }
+ }
+
+ return body;
+ }
+
+ /**
+ * Log status.
+ *
+ * @param text log information
+ */
+ @UnsupportedAppUsage
+ private static void log(String text) {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, text);
+ }
+ }
+
+ /**
+ * Parse unsigned integer.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the integer, -1 when failed
+ */
+ @UnsupportedAppUsage
+ protected static int parseUnsignedInt(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * The maximum size of a uintvar is 32 bits.
+ * So it will be encoded in no more than 5 octets.
+ */
+ assert(null != pduDataStream);
+ int result = 0;
+ int temp = pduDataStream.read();
+ if (temp == -1) {
+ return temp;
+ }
+
+ while((temp & 0x80) != 0) {
+ result = result << 7;
+ result |= temp & 0x7F;
+ temp = pduDataStream.read();
+ if (temp == -1) {
+ return temp;
+ }
+ }
+
+ result = result << 7;
+ result |= temp & 0x7F;
+
+ return result;
+ }
+
+ /**
+ * Parse value length.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the integer
+ */
+ @UnsupportedAppUsage
+ protected static int parseValueLength(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Value-length = Short-length | (Length-quote Length)
+ * Short-length = <Any octet 0-30>
+ * Length-quote = <Octet 31>
+ * Length = Uintvar-integer
+ * Uintvar-integer = 1*5 OCTET
+ */
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ int first = temp & 0xFF;
+
+ if (first <= SHORT_LENGTH_MAX) {
+ return first;
+ } else if (first == LENGTH_QUOTE) {
+ return parseUnsignedInt(pduDataStream);
+ }
+
+ throw new RuntimeException ("Value length > LENGTH_QUOTE!");
+ }
+
+ /**
+ * Parse encoded string value.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the EncodedStringValue
+ */
+ protected static EncodedStringValue parseEncodedStringValue(ByteArrayInputStream pduDataStream){
+ /**
+ * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf
+ * Encoded-string-value = Text-string | Value-length Char-set Text-string
+ */
+ assert(null != pduDataStream);
+ pduDataStream.mark(1);
+ EncodedStringValue returnValue = null;
+ int charset = 0;
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ int first = temp & 0xFF;
+ if (first == 0) {
+ return new EncodedStringValue("");
+ }
+
+ pduDataStream.reset();
+ if (first < TEXT_MIN) {
+ parseValueLength(pduDataStream);
+
+ charset = parseShortInteger(pduDataStream); //get the "Charset"
+ }
+
+ byte[] textString = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+
+ try {
+ if (0 != charset) {
+ returnValue = new EncodedStringValue(charset, textString);
+ } else {
+ returnValue = new EncodedStringValue(textString);
+ }
+ } catch(Exception e) {
+ return null;
+ }
+
+ return returnValue;
+ }
+
+ /**
+ * Parse Text-String or Quoted-String.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param stringType TYPE_TEXT_STRING or TYPE_QUOTED_STRING
+ * @return the string without End-of-string in byte array
+ */
+ @UnsupportedAppUsage
+ protected static byte[] parseWapString(ByteArrayInputStream pduDataStream,
+ int stringType) {
+ assert(null != pduDataStream);
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Text-string = [Quote] *TEXT End-of-string
+ * If the first character in the TEXT is in the range of 128-255,
+ * a Quote character must precede it.
+ * Otherwise the Quote character must be omitted.
+ * The Quote is not part of the contents.
+ * Quote = <Octet 127>
+ * End-of-string = <Octet 0>
+ *
+ * Quoted-string = <Octet 34> *TEXT End-of-string
+ *
+ * Token-text = Token End-of-string
+ */
+
+ // Mark supposed beginning of Text-string
+ // We will have to mark again if first char is QUOTE or QUOTED_STRING_FLAG
+ pduDataStream.mark(1);
+
+ // Check first char
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ if ((TYPE_QUOTED_STRING == stringType) &&
+ (QUOTED_STRING_FLAG == temp)) {
+ // Mark again if QUOTED_STRING_FLAG and ignore it
+ pduDataStream.mark(1);
+ } else if ((TYPE_TEXT_STRING == stringType) &&
+ (QUOTE == temp)) {
+ // Mark again if QUOTE and ignore it
+ pduDataStream.mark(1);
+ } else {
+ // Otherwise go back to origin
+ pduDataStream.reset();
+ }
+
+ // We are now definitely at the beginning of string
+ /**
+ * Return *TOKEN or *TEXT (Text-String without QUOTE,
+ * Quoted-String without QUOTED_STRING_FLAG and without End-of-string)
+ */
+ return getWapString(pduDataStream, stringType);
+ }
+
+ /**
+ * Check TOKEN data defined in RFC2616.
+ * @param ch checking data
+ * @return true when ch is TOKEN, false when ch is not TOKEN
+ */
+ protected static boolean isTokenCharacter(int ch) {
+ /**
+ * Token = 1*<any CHAR except CTLs or separators>
+ * separators = "("(40) | ")"(41) | "<"(60) | ">"(62) | "@"(64)
+ * | ","(44) | ";"(59) | ":"(58) | "\"(92) | <">(34)
+ * | "/"(47) | "["(91) | "]"(93) | "?"(63) | "="(61)
+ * | "{"(123) | "}"(125) | SP(32) | HT(9)
+ * CHAR = <any US-ASCII character (octets 0 - 127)>
+ * CTL = <any US-ASCII control character
+ * (octets 0 - 31) and DEL (127)>
+ * SP = <US-ASCII SP, space (32)>
+ * HT = <US-ASCII HT, horizontal-tab (9)>
+ */
+ if((ch < 33) || (ch > 126)) {
+ return false;
+ }
+
+ switch(ch) {
+ case '"': /* '"' */
+ case '(': /* '(' */
+ case ')': /* ')' */
+ case ',': /* ',' */
+ case '/': /* '/' */
+ case ':': /* ':' */
+ case ';': /* ';' */
+ case '<': /* '<' */
+ case '=': /* '=' */
+ case '>': /* '>' */
+ case '?': /* '?' */
+ case '@': /* '@' */
+ case '[': /* '[' */
+ case '\\': /* '\' */
+ case ']': /* ']' */
+ case '{': /* '{' */
+ case '}': /* '}' */
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check TEXT data defined in RFC2616.
+ * @param ch checking data
+ * @return true when ch is TEXT, false when ch is not TEXT
+ */
+ protected static boolean isText(int ch) {
+ /**
+ * TEXT = <any OCTET except CTLs,
+ * but including LWS>
+ * CTL = <any US-ASCII control character
+ * (octets 0 - 31) and DEL (127)>
+ * LWS = [CRLF] 1*( SP | HT )
+ * CRLF = CR LF
+ * CR = <US-ASCII CR, carriage return (13)>
+ * LF = <US-ASCII LF, linefeed (10)>
+ */
+ if(((ch >= 32) && (ch <= 126)) || ((ch >= 128) && (ch <= 255))) {
+ return true;
+ }
+
+ switch(ch) {
+ case '\t': /* '\t' */
+ case '\n': /* '\n' */
+ case '\r': /* '\r' */
+ return true;
+ }
+
+ return false;
+ }
+
+ protected static byte[] getWapString(ByteArrayInputStream pduDataStream,
+ int stringType) {
+ assert(null != pduDataStream);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ while((-1 != temp) && ('\0' != temp)) {
+ // check each of the character
+ if (stringType == TYPE_TOKEN_STRING) {
+ if (isTokenCharacter(temp)) {
+ out.write(temp);
+ }
+ } else {
+ if (isText(temp)) {
+ out.write(temp);
+ }
+ }
+
+ temp = pduDataStream.read();
+ assert(-1 != temp);
+ }
+
+ if (out.size() > 0) {
+ return out.toByteArray();
+ }
+
+ return null;
+ }
+
+ /**
+ * Extract a byte value from the input stream.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the byte
+ */
+ protected static int extractByteValue(ByteArrayInputStream pduDataStream) {
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ return temp & 0xFF;
+ }
+
+ /**
+ * Parse Short-Integer.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return the byte
+ */
+ @UnsupportedAppUsage
+ protected static int parseShortInteger(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Short-integer = OCTET
+ * Integers in range 0-127 shall be encoded as a one
+ * octet value with the most significant bit set to one (1xxx xxxx)
+ * and with the value in the remaining least significant bits.
+ */
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ return temp & 0x7F;
+ }
+
+ /**
+ * Parse Long-Integer.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return long integer
+ */
+ protected static long parseLongInteger(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Long-integer = Short-length Multi-octet-integer
+ * The Short-length indicates the length of the Multi-octet-integer
+ * Multi-octet-integer = 1*30 OCTET
+ * The content octets shall be an unsigned integer value
+ * with the most significant octet encoded first (big-endian representation).
+ * The minimum number of octets must be used to encode the value.
+ * Short-length = <Any octet 0-30>
+ */
+ assert(null != pduDataStream);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ int count = temp & 0xFF;
+
+ if (count > LONG_INTEGER_LENGTH_MAX) {
+ throw new RuntimeException("Octet count greater than 8 and I can't represent that!");
+ }
+
+ long result = 0;
+
+ for (int i = 0 ; i < count ; i++) {
+ temp = pduDataStream.read();
+ assert(-1 != temp);
+ result <<= 8;
+ result += (temp & 0xFF);
+ }
+
+ return result;
+ }
+
+ /**
+ * Parse Integer-Value.
+ *
+ * @param pduDataStream pdu data input stream
+ * @return long integer
+ */
+ protected static long parseIntegerValue(ByteArrayInputStream pduDataStream) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Integer-Value = Short-integer | Long-integer
+ */
+ assert(null != pduDataStream);
+ pduDataStream.mark(1);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ pduDataStream.reset();
+ if (temp > SHORT_INTEGER_MAX) {
+ return parseShortInteger(pduDataStream);
+ } else {
+ return parseLongInteger(pduDataStream);
+ }
+ }
+
+ /**
+ * To skip length of the wap value.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param length area size
+ * @return the values in this area
+ */
+ protected static int skipWapValue(ByteArrayInputStream pduDataStream, int length) {
+ assert(null != pduDataStream);
+ byte[] area = new byte[length];
+ int readLen = pduDataStream.read(area, 0, length);
+ if (readLen < length) { //The actually read length is lower than the length
+ return -1;
+ } else {
+ return readLen;
+ }
+ }
+
+ /**
+ * Parse content type parameters. For now we just support
+ * four parameters used in mms: "type", "start", "name", "charset".
+ *
+ * @param pduDataStream pdu data input stream
+ * @param map to store parameters of Content-Type field
+ * @param length length of all the parameters
+ */
+ protected static void parseContentTypeParams(ByteArrayInputStream pduDataStream,
+ HashMap<Integer, Object> map, Integer length) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Parameter = Typed-parameter | Untyped-parameter
+ * Typed-parameter = Well-known-parameter-token Typed-value
+ * the actual expected type of the value is implied by the well-known parameter
+ * Well-known-parameter-token = Integer-value
+ * the code values used for parameters are specified in the Assigned Numbers appendix
+ * Typed-value = Compact-value | Text-value
+ * In addition to the expected type, there may be no value.
+ * If the value cannot be encoded using the expected type, it shall be encoded as text.
+ * Compact-value = Integer-value |
+ * Date-value | Delta-seconds-value | Q-value | Version-value |
+ * Uri-value
+ * Untyped-parameter = Token-text Untyped-value
+ * the type of the value is unknown, but it shall be encoded as an integer,
+ * if that is possible.
+ * Untyped-value = Integer-value | Text-value
+ */
+ assert(null != pduDataStream);
+ assert(length > 0);
+
+ int startPos = pduDataStream.available();
+ int tempPos = 0;
+ int lastLen = length;
+ while(0 < lastLen) {
+ int param = pduDataStream.read();
+ assert(-1 != param);
+ lastLen--;
+
+ switch (param) {
+ /**
+ * From rfc2387, chapter 3.1
+ * The type parameter must be specified and its value is the MIME media
+ * type of the "root" body part. It permits a MIME user agent to
+ * determine the content-type without reference to the enclosed body
+ * part. If the value of the type parameter and the root body part's
+ * content-type differ then the User Agent's behavior is undefined.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * type = Constrained-encoding
+ * Constrained-encoding = Extension-Media | Short-integer
+ * Extension-media = *TEXT End-of-string
+ */
+ case PduPart.P_TYPE:
+ case PduPart.P_CT_MR_TYPE:
+ pduDataStream.mark(1);
+ int first = extractByteValue(pduDataStream);
+ pduDataStream.reset();
+ if (first > TEXT_MAX) {
+ // Short-integer (well-known type)
+ int index = parseShortInteger(pduDataStream);
+
+ if (index < PduContentTypes.contentTypes.length) {
+ byte[] type = (PduContentTypes.contentTypes[index]).getBytes();
+ map.put(PduPart.P_TYPE, type);
+ } else {
+ //not support this type, ignore it.
+ }
+ } else {
+ // Text-String (extension-media)
+ byte[] type = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if ((null != type) && (null != map)) {
+ map.put(PduPart.P_TYPE, type);
+ }
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.3.
+ * Start Parameter Referring to Presentation
+ *
+ * From rfc2387, chapter 3.2
+ * The start parameter, if given, is the content-ID of the compound
+ * object's "root". If not present the "root" is the first body part in
+ * the Multipart/Related entity. The "root" is the element the
+ * applications processes first.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * start = Text-String
+ */
+ case PduPart.P_START:
+ case PduPart.P_DEP_START:
+ byte[] start = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if ((null != start) && (null != map)) {
+ map.put(PduPart.P_START, start);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf
+ * In creation, the character set SHALL be either us-ascii
+ * (IANA MIBenum 3) or utf-8 (IANA MIBenum 106)[Unicode].
+ * In retrieval, both us-ascii and utf-8 SHALL be supported.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * charset = Well-known-charset|Text-String
+ * Well-known-charset = Any-charset | Integer-value
+ * Both are encoded using values from Character Set
+ * Assignments table in Assigned Numbers
+ * Any-charset = <Octet 128>
+ * Equivalent to the special RFC2616 charset value "*"
+ */
+ case PduPart.P_CHARSET:
+ pduDataStream.mark(1);
+ int firstValue = extractByteValue(pduDataStream);
+ pduDataStream.reset();
+ //Check first char
+ if (((firstValue > TEXT_MIN) && (firstValue < TEXT_MAX)) ||
+ (END_STRING_FLAG == firstValue)) {
+ //Text-String (extension-charset)
+ byte[] charsetStr = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ try {
+ int charsetInt = CharacterSets.getMibEnumValue(
+ new String(charsetStr));
+ map.put(PduPart.P_CHARSET, charsetInt);
+ } catch (UnsupportedEncodingException e) {
+ // Not a well-known charset, use "*".
+ Log.e(LOG_TAG, Arrays.toString(charsetStr), e);
+ map.put(PduPart.P_CHARSET, CharacterSets.ANY_CHARSET);
+ }
+ } else {
+ //Well-known-charset
+ int charset = (int) parseIntegerValue(pduDataStream);
+ if (map != null) {
+ map.put(PduPart.P_CHARSET, charset);
+ }
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf
+ * A name for multipart object SHALL be encoded using name-parameter
+ * for Content-Type header in WSP multipart headers.
+ *
+ * From wap-230-wsp-20010705-a.pdf
+ * name = Text-String
+ */
+ case PduPart.P_DEP_NAME:
+ case PduPart.P_NAME:
+ byte[] name = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if ((null != name) && (null != map)) {
+ map.put(PduPart.P_NAME, name);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+ default:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "Not supported Content-Type parameter");
+ }
+ if (-1 == skipWapValue(pduDataStream, lastLen)) {
+ Log.e(LOG_TAG, "Corrupt Content-Type");
+ } else {
+ lastLen = 0;
+ }
+ break;
+ }
+ }
+
+ if (0 != lastLen) {
+ Log.e(LOG_TAG, "Corrupt Content-Type");
+ }
+ }
+
+ /**
+ * Parse content type.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param map to store parameters in Content-Type header field
+ * @return Content-Type value
+ */
+ @UnsupportedAppUsage
+ protected static byte[] parseContentType(ByteArrayInputStream pduDataStream,
+ HashMap<Integer, Object> map) {
+ /**
+ * From wap-230-wsp-20010705-a.pdf
+ * Content-type-value = Constrained-media | Content-general-form
+ * Content-general-form = Value-length Media-type
+ * Media-type = (Well-known-media | Extension-Media) *(Parameter)
+ */
+ assert(null != pduDataStream);
+
+ byte[] contentType = null;
+ pduDataStream.mark(1);
+ int temp = pduDataStream.read();
+ assert(-1 != temp);
+ pduDataStream.reset();
+
+ int cur = (temp & 0xFF);
+
+ if (cur < TEXT_MIN) {
+ int length = parseValueLength(pduDataStream);
+ int startPos = pduDataStream.available();
+ pduDataStream.mark(1);
+ temp = pduDataStream.read();
+ assert(-1 != temp);
+ pduDataStream.reset();
+ int first = (temp & 0xFF);
+
+ if ((first >= TEXT_MIN) && (first <= TEXT_MAX)) {
+ contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ } else if (first > TEXT_MAX) {
+ int index = parseShortInteger(pduDataStream);
+
+ if (index < PduContentTypes.contentTypes.length) { //well-known type
+ contentType = (PduContentTypes.contentTypes[index]).getBytes();
+ } else {
+ pduDataStream.reset();
+ contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ }
+ } else {
+ Log.e(LOG_TAG, "Corrupt content-type");
+ return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
+ }
+
+ int endPos = pduDataStream.available();
+ int parameterLen = length - (startPos - endPos);
+ if (parameterLen > 0) {//have parameters
+ parseContentTypeParams(pduDataStream, map, parameterLen);
+ }
+
+ if (parameterLen < 0) {
+ Log.e(LOG_TAG, "Corrupt MMS message");
+ return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*"
+ }
+ } else if (cur <= TEXT_MAX) {
+ contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ } else {
+ contentType =
+ (PduContentTypes.contentTypes[parseShortInteger(pduDataStream)]).getBytes();
+ }
+
+ return contentType;
+ }
+
+ /**
+ * Parse part's headers.
+ *
+ * @param pduDataStream pdu data input stream
+ * @param part to store the header informations of the part
+ * @param length length of the headers
+ * @return true if parse successfully, false otherwise
+ */
+ @UnsupportedAppUsage
+ protected boolean parsePartHeaders(ByteArrayInputStream pduDataStream,
+ PduPart part, int length) {
+ assert(null != pduDataStream);
+ assert(null != part);
+ assert(length > 0);
+
+ /**
+ * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.
+ * A name for multipart object SHALL be encoded using name-parameter
+ * for Content-Type header in WSP multipart headers.
+ * In decoding, name-parameter of Content-Type SHALL be used if available.
+ * If name-parameter of Content-Type is not available,
+ * filename parameter of Content-Disposition header SHALL be used if available.
+ * If neither name-parameter of Content-Type header nor filename parameter
+ * of Content-Disposition header is available,
+ * Content-Location header SHALL be used if available.
+ *
+ * Within SMIL part the reference to the media object parts SHALL use
+ * either Content-ID or Content-Location mechanism [RFC2557]
+ * and the corresponding WSP part headers in media object parts
+ * contain the corresponding definitions.
+ */
+ int startPos = pduDataStream.available();
+ int tempPos = 0;
+ int lastLen = length;
+ while(0 < lastLen) {
+ int header = pduDataStream.read();
+ assert(-1 != header);
+ lastLen--;
+
+ if (header > TEXT_MAX) {
+ // Number assigned headers.
+ switch (header) {
+ case PduPart.P_CONTENT_LOCATION:
+ /**
+ * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
+ * Content-location-value = Uri-value
+ */
+ byte[] contentLocation = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ if (null != contentLocation) {
+ part.setContentLocation(contentLocation);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+ case PduPart.P_CONTENT_ID:
+ /**
+ * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
+ * Content-ID-value = Quoted-string
+ */
+ byte[] contentId = parseWapString(pduDataStream, TYPE_QUOTED_STRING);
+ if (null != contentId) {
+ part.setContentId(contentId);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ break;
+ case PduPart.P_DEP_CONTENT_DISPOSITION:
+ case PduPart.P_CONTENT_DISPOSITION:
+ /**
+ * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21
+ * Content-disposition-value = Value-length Disposition *(Parameter)
+ * Disposition = Form-data | Attachment | Inline | Token-text
+ * Form-data = <Octet 128>
+ * Attachment = <Octet 129>
+ * Inline = <Octet 130>
+ */
+
+ /*
+ * some carrier mmsc servers do not support content_disposition
+ * field correctly
+ */
+ if (mParseContentDisposition) {
+ int len = parseValueLength(pduDataStream);
+ pduDataStream.mark(1);
+ int thisStartPos = pduDataStream.available();
+ int thisEndPos = 0;
+ int value = pduDataStream.read();
+
+ if (value == PduPart.P_DISPOSITION_FROM_DATA ) {
+ part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA);
+ } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) {
+ part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT);
+ } else if (value == PduPart.P_DISPOSITION_INLINE) {
+ part.setContentDisposition(PduPart.DISPOSITION_INLINE);
+ } else {
+ pduDataStream.reset();
+ /* Token-text */
+ part.setContentDisposition(parseWapString(pduDataStream
+ , TYPE_TEXT_STRING));
+ }
+
+ /* get filename parameter and skip other parameters */
+ thisEndPos = pduDataStream.available();
+ if (thisStartPos - thisEndPos < len) {
+ value = pduDataStream.read();
+ if (value == PduPart.P_FILENAME) { //filename is text-string
+ part.setFilename(parseWapString(pduDataStream
+ , TYPE_TEXT_STRING));
+ }
+
+ /* skip other parameters */
+ thisEndPos = pduDataStream.available();
+ if (thisStartPos - thisEndPos < len) {
+ int last = len - (thisStartPos - thisEndPos);
+ byte[] temp = new byte[last];
+ pduDataStream.read(temp, 0, last);
+ }
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ }
+ break;
+ default:
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "Not supported Part headers: " + header);
+ }
+ if (-1 == skipWapValue(pduDataStream, lastLen)) {
+ Log.e(LOG_TAG, "Corrupt Part headers");
+ return false;
+ }
+ lastLen = 0;
+ break;
+ }
+ } else if ((header >= TEXT_MIN) && (header <= TEXT_MAX)) {
+ // Not assigned header.
+ byte[] tempHeader = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+ byte[] tempValue = parseWapString(pduDataStream, TYPE_TEXT_STRING);
+
+ // Check the header whether it is "Content-Transfer-Encoding".
+ if (true ==
+ PduPart.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(new String(tempHeader))) {
+ part.setContentTransferEncoding(tempValue);
+ }
+
+ tempPos = pduDataStream.available();
+ lastLen = length - (startPos - tempPos);
+ } else {
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "Not supported Part headers: " + header);
+ }
+ // Skip all headers of this part.
+ if (-1 == skipWapValue(pduDataStream, lastLen)) {
+ Log.e(LOG_TAG, "Corrupt Part headers");
+ return false;
+ }
+ lastLen = 0;
+ }
+ }
+
+ if (0 != lastLen) {
+ Log.e(LOG_TAG, "Corrupt Part headers");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Check the position of a specified part.
+ *
+ * @param part the part to be checked
+ * @return part position, THE_FIRST_PART when it's the
+ * first one, THE_LAST_PART when it's the last one.
+ */
+ @UnsupportedAppUsage
+ private static int checkPartPosition(PduPart part) {
+ assert(null != part);
+ if ((null == mTypeParam) &&
+ (null == mStartParam)) {
+ return THE_LAST_PART;
+ }
+
+ /* check part's content-id */
+ if (null != mStartParam) {
+ byte[] contentId = part.getContentId();
+ if (null != contentId) {
+ if (true == Arrays.equals(mStartParam, contentId)) {
+ return THE_FIRST_PART;
+ }
+ }
+ // This is not the first part, so append to end (keeping the original order)
+ // Check b/19607294 for details of this change
+ return THE_LAST_PART;
+ }
+
+ /* check part's content-type */
+ if (null != mTypeParam) {
+ byte[] contentType = part.getContentType();
+ if (null != contentType) {
+ if (true == Arrays.equals(mTypeParam, contentType)) {
+ return THE_FIRST_PART;
+ }
+ }
+ }
+
+ return THE_LAST_PART;
+ }
+
+ /**
+ * Check mandatory headers of a pdu.
+ *
+ * @param headers pdu headers
+ * @return true if the pdu has all of the mandatory headers, false otherwise.
+ */
+ protected static boolean checkMandatoryHeader(PduHeaders headers) {
+ if (null == headers) {
+ return false;
+ }
+
+ /* get message type */
+ int messageType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
+
+ /* check Mms-Version field */
+ int mmsVersion = headers.getOctet(PduHeaders.MMS_VERSION);
+ if (0 == mmsVersion) {
+ // Every message should have Mms-Version field.
+ return false;
+ }
+
+ /* check mandatory header fields */
+ switch (messageType) {
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ // Content-Type field.
+ byte[] srContentType = headers.getTextString(PduHeaders.CONTENT_TYPE);
+ if (null == srContentType) {
+ return false;
+ }
+
+ // From field.
+ EncodedStringValue srFrom = headers.getEncodedStringValue(PduHeaders.FROM);
+ if (null == srFrom) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] srTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == srTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_CONF:
+ // Response-Status field.
+ int scResponseStatus = headers.getOctet(PduHeaders.RESPONSE_STATUS);
+ if (0 == scResponseStatus) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] scTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == scTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ // Content-Location field.
+ byte[] niContentLocation = headers.getTextString(PduHeaders.CONTENT_LOCATION);
+ if (null == niContentLocation) {
+ return false;
+ }
+
+ // Expiry field.
+ long niExpiry = headers.getLongInteger(PduHeaders.EXPIRY);
+ if (-1 == niExpiry) {
+ return false;
+ }
+
+ // Message-Class field.
+ byte[] niMessageClass = headers.getTextString(PduHeaders.MESSAGE_CLASS);
+ if (null == niMessageClass) {
+ return false;
+ }
+
+ // Message-Size field.
+ long niMessageSize = headers.getLongInteger(PduHeaders.MESSAGE_SIZE);
+ if (-1 == niMessageSize) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] niTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == niTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ // Status field.
+ int nriStatus = headers.getOctet(PduHeaders.STATUS);
+ if (0 == nriStatus) {
+ return false;
+ }
+
+ // Transaction-Id field.
+ byte[] nriTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == nriTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ // Content-Type field.
+ byte[] rcContentType = headers.getTextString(PduHeaders.CONTENT_TYPE);
+ if (null == rcContentType) {
+ return false;
+ }
+
+ // Date field.
+ long rcDate = headers.getLongInteger(PduHeaders.DATE);
+ if (-1 == rcDate) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
+ // Date field.
+ long diDate = headers.getLongInteger(PduHeaders.DATE);
+ if (-1 == diDate) {
+ return false;
+ }
+
+ // Message-Id field.
+ byte[] diMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
+ if (null == diMessageId) {
+ return false;
+ }
+
+ // Status field.
+ int diStatus = headers.getOctet(PduHeaders.STATUS);
+ if (0 == diStatus) {
+ return false;
+ }
+
+ // To field.
+ EncodedStringValue[] diTo = headers.getEncodedStringValues(PduHeaders.TO);
+ if (null == diTo) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ // Transaction-Id field.
+ byte[] aiTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID);
+ if (null == aiTransactionId) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
+ // Date field.
+ long roDate = headers.getLongInteger(PduHeaders.DATE);
+ if (-1 == roDate) {
+ return false;
+ }
+
+ // From field.
+ EncodedStringValue roFrom = headers.getEncodedStringValue(PduHeaders.FROM);
+ if (null == roFrom) {
+ return false;
+ }
+
+ // Message-Id field.
+ byte[] roMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
+ if (null == roMessageId) {
+ return false;
+ }
+
+ // Read-Status field.
+ int roReadStatus = headers.getOctet(PduHeaders.READ_STATUS);
+ if (0 == roReadStatus) {
+ return false;
+ }
+
+ // To field.
+ EncodedStringValue[] roTo = headers.getEncodedStringValues(PduHeaders.TO);
+ if (null == roTo) {
+ return false;
+ }
+
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ // From field.
+ EncodedStringValue rrFrom = headers.getEncodedStringValue(PduHeaders.FROM);
+ if (null == rrFrom) {
+ return false;
+ }
+
+ // Message-Id field.
+ byte[] rrMessageId = headers.getTextString(PduHeaders.MESSAGE_ID);
+ if (null == rrMessageId) {
+ return false;
+ }
+
+ // Read-Status field.
+ int rrReadStatus = headers.getOctet(PduHeaders.READ_STATUS);
+ if (0 == rrReadStatus) {
+ return false;
+ }
+
+ // To field.
+ EncodedStringValue[] rrTo = headers.getEncodedStringValues(PduHeaders.TO);
+ if (null == rrTo) {
+ return false;
+ }
+
+ break;
+ default:
+ // Parser doesn't support this message type in this version.
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/PduPart.java b/telephony/java/com/google/android/mms/pdu/PduPart.java
new file mode 100644
index 000000000000..09b775118dc3
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduPart.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.net.Uri;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The pdu part.
+ */
+public class PduPart {
+ /**
+ * Well-Known Parameters.
+ */
+ public static final int P_Q = 0x80;
+ public static final int P_CHARSET = 0x81;
+ public static final int P_LEVEL = 0x82;
+ public static final int P_TYPE = 0x83;
+ public static final int P_DEP_NAME = 0x85;
+ public static final int P_DEP_FILENAME = 0x86;
+ public static final int P_DIFFERENCES = 0x87;
+ public static final int P_PADDING = 0x88;
+ // This value of "TYPE" s used with Content-Type: multipart/related
+ public static final int P_CT_MR_TYPE = 0x89;
+ public static final int P_DEP_START = 0x8A;
+ public static final int P_DEP_START_INFO = 0x8B;
+ public static final int P_DEP_COMMENT = 0x8C;
+ public static final int P_DEP_DOMAIN = 0x8D;
+ public static final int P_MAX_AGE = 0x8E;
+ public static final int P_DEP_PATH = 0x8F;
+ public static final int P_SECURE = 0x90;
+ public static final int P_SEC = 0x91;
+ public static final int P_MAC = 0x92;
+ public static final int P_CREATION_DATE = 0x93;
+ public static final int P_MODIFICATION_DATE = 0x94;
+ public static final int P_READ_DATE = 0x95;
+ public static final int P_SIZE = 0x96;
+ public static final int P_NAME = 0x97;
+ public static final int P_FILENAME = 0x98;
+ public static final int P_START = 0x99;
+ public static final int P_START_INFO = 0x9A;
+ public static final int P_COMMENT = 0x9B;
+ public static final int P_DOMAIN = 0x9C;
+ public static final int P_PATH = 0x9D;
+
+ /**
+ * Header field names.
+ */
+ public static final int P_CONTENT_TYPE = 0x91;
+ public static final int P_CONTENT_LOCATION = 0x8E;
+ public static final int P_CONTENT_ID = 0xC0;
+ public static final int P_DEP_CONTENT_DISPOSITION = 0xAE;
+ public static final int P_CONTENT_DISPOSITION = 0xC5;
+ // The next header is unassigned header, use reserved header(0x48) value.
+ public static final int P_CONTENT_TRANSFER_ENCODING = 0xC8;
+
+ /**
+ * Content=Transfer-Encoding string.
+ */
+ public static final String CONTENT_TRANSFER_ENCODING =
+ "Content-Transfer-Encoding";
+
+ /**
+ * Value of Content-Transfer-Encoding.
+ */
+ public static final String P_BINARY = "binary";
+ public static final String P_7BIT = "7bit";
+ public static final String P_8BIT = "8bit";
+ public static final String P_BASE64 = "base64";
+ public static final String P_QUOTED_PRINTABLE = "quoted-printable";
+
+ /**
+ * Value of disposition can be set to PduPart when the value is octet in
+ * the PDU.
+ * "from-data" instead of Form-data<Octet 128>.
+ * "attachment" instead of Attachment<Octet 129>.
+ * "inline" instead of Inline<Octet 130>.
+ */
+ static final byte[] DISPOSITION_FROM_DATA = "from-data".getBytes();
+ static final byte[] DISPOSITION_ATTACHMENT = "attachment".getBytes();
+ static final byte[] DISPOSITION_INLINE = "inline".getBytes();
+
+ /**
+ * Content-Disposition value.
+ */
+ public static final int P_DISPOSITION_FROM_DATA = 0x80;
+ public static final int P_DISPOSITION_ATTACHMENT = 0x81;
+ public static final int P_DISPOSITION_INLINE = 0x82;
+
+ /**
+ * Header of part.
+ */
+ private Map<Integer, Object> mPartHeader = null;
+
+ /**
+ * Data uri.
+ */
+ private Uri mUri = null;
+
+ /**
+ * Part data.
+ */
+ private byte[] mPartData = null;
+
+ private static final String TAG = "PduPart";
+
+ /**
+ * Empty Constructor.
+ */
+ @UnsupportedAppUsage
+ public PduPart() {
+ mPartHeader = new HashMap<Integer, Object>();
+ }
+
+ /**
+ * Set part data. The data are stored as byte array.
+ *
+ * @param data the data
+ */
+ @UnsupportedAppUsage
+ public void setData(byte[] data) {
+ if(data == null) {
+ return;
+ }
+
+ mPartData = new byte[data.length];
+ System.arraycopy(data, 0, mPartData, 0, data.length);
+ }
+
+ /**
+ * @return A copy of the part data or null if the data wasn't set or
+ * the data is stored as Uri.
+ * @see #getDataUri
+ */
+ @UnsupportedAppUsage
+ public byte[] getData() {
+ if(mPartData == null) {
+ return null;
+ }
+
+ byte[] byteArray = new byte[mPartData.length];
+ System.arraycopy(mPartData, 0, byteArray, 0, mPartData.length);
+ return byteArray;
+ }
+
+ /**
+ * @return The length of the data, if this object have data, else 0.
+ */
+ @UnsupportedAppUsage
+ public int getDataLength() {
+ if(mPartData != null){
+ return mPartData.length;
+ } else {
+ return 0;
+ }
+ }
+
+
+ /**
+ * Set data uri. The data are stored as Uri.
+ *
+ * @param uri the uri
+ */
+ @UnsupportedAppUsage
+ public void setDataUri(Uri uri) {
+ mUri = uri;
+ }
+
+ /**
+ * @return The Uri of the part data or null if the data wasn't set or
+ * the data is stored as byte array.
+ * @see #getData
+ */
+ @UnsupportedAppUsage
+ public Uri getDataUri() {
+ return mUri;
+ }
+
+ /**
+ * Set Content-id value
+ *
+ * @param contentId the content-id value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentId(byte[] contentId) {
+ if((contentId == null) || (contentId.length == 0)) {
+ throw new IllegalArgumentException(
+ "Content-Id may not be null or empty.");
+ }
+
+ if ((contentId.length > 1)
+ && ((char) contentId[0] == '<')
+ && ((char) contentId[contentId.length - 1] == '>')) {
+ mPartHeader.put(P_CONTENT_ID, contentId);
+ return;
+ }
+
+ // Insert beginning '<' and trailing '>' for Content-Id.
+ byte[] buffer = new byte[contentId.length + 2];
+ buffer[0] = (byte) (0xff & '<');
+ buffer[buffer.length - 1] = (byte) (0xff & '>');
+ System.arraycopy(contentId, 0, buffer, 1, contentId.length);
+ mPartHeader.put(P_CONTENT_ID, buffer);
+ }
+
+ /**
+ * Get Content-id value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentId() {
+ return (byte[]) mPartHeader.get(P_CONTENT_ID);
+ }
+
+ /**
+ * Set Char-set value.
+ *
+ * @param charset the value
+ */
+ @UnsupportedAppUsage
+ public void setCharset(int charset) {
+ mPartHeader.put(P_CHARSET, charset);
+ }
+
+ /**
+ * Get Char-set value
+ *
+ * @return the charset value. Return 0 if charset was not set.
+ */
+ @UnsupportedAppUsage
+ public int getCharset() {
+ Integer charset = (Integer) mPartHeader.get(P_CHARSET);
+ if(charset == null) {
+ return 0;
+ } else {
+ return charset.intValue();
+ }
+ }
+
+ /**
+ * Set Content-Location value.
+ *
+ * @param contentLocation the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentLocation(byte[] contentLocation) {
+ if(contentLocation == null) {
+ throw new NullPointerException("null content-location");
+ }
+
+ mPartHeader.put(P_CONTENT_LOCATION, contentLocation);
+ }
+
+ /**
+ * Get Content-Location value.
+ *
+ * @return the value
+ * return PduPart.disposition[0] instead of <Octet 128> (Form-data).
+ * return PduPart.disposition[1] instead of <Octet 129> (Attachment).
+ * return PduPart.disposition[2] instead of <Octet 130> (Inline).
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentLocation() {
+ return (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
+ }
+
+ /**
+ * Set Content-Disposition value.
+ * Use PduPart.disposition[0] instead of <Octet 128> (Form-data).
+ * Use PduPart.disposition[1] instead of <Octet 129> (Attachment).
+ * Use PduPart.disposition[2] instead of <Octet 130> (Inline).
+ *
+ * @param contentDisposition the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentDisposition(byte[] contentDisposition) {
+ if(contentDisposition == null) {
+ throw new NullPointerException("null content-disposition");
+ }
+
+ mPartHeader.put(P_CONTENT_DISPOSITION, contentDisposition);
+ }
+
+ /**
+ * Get Content-Disposition value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentDisposition() {
+ return (byte[]) mPartHeader.get(P_CONTENT_DISPOSITION);
+ }
+
+ /**
+ * Set Content-Type value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentType(byte[] contentType) {
+ if(contentType == null) {
+ throw new NullPointerException("null content-type");
+ }
+
+ mPartHeader.put(P_CONTENT_TYPE, contentType);
+ }
+
+ /**
+ * Get Content-Type value of part.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentType() {
+ return (byte[]) mPartHeader.get(P_CONTENT_TYPE);
+ }
+
+ /**
+ * Set Content-Transfer-Encoding value
+ *
+ * @param contentId the content-id value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentTransferEncoding(byte[] contentTransferEncoding) {
+ if(contentTransferEncoding == null) {
+ throw new NullPointerException("null content-transfer-encoding");
+ }
+
+ mPartHeader.put(P_CONTENT_TRANSFER_ENCODING, contentTransferEncoding);
+ }
+
+ /**
+ * Get Content-Transfer-Encoding value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentTransferEncoding() {
+ return (byte[]) mPartHeader.get(P_CONTENT_TRANSFER_ENCODING);
+ }
+
+ /**
+ * Set Content-type parameter: name.
+ *
+ * @param name the name value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setName(byte[] name) {
+ if(null == name) {
+ throw new NullPointerException("null content-id");
+ }
+
+ mPartHeader.put(P_NAME, name);
+ }
+
+ /**
+ * Get content-type parameter: name.
+ *
+ * @return the name
+ */
+ @UnsupportedAppUsage
+ public byte[] getName() {
+ return (byte[]) mPartHeader.get(P_NAME);
+ }
+
+ /**
+ * Get Content-disposition parameter: filename
+ *
+ * @param fileName the filename value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setFilename(byte[] fileName) {
+ if(null == fileName) {
+ throw new NullPointerException("null content-id");
+ }
+
+ mPartHeader.put(P_FILENAME, fileName);
+ }
+
+ /**
+ * Set Content-disposition parameter: filename
+ *
+ * @return the filename
+ */
+ @UnsupportedAppUsage
+ public byte[] getFilename() {
+ return (byte[]) mPartHeader.get(P_FILENAME);
+ }
+
+ @UnsupportedAppUsage
+ public String generateLocation() {
+ // Assumption: At least one of the content-location / name / filename
+ // or content-id should be set. This is guaranteed by the PduParser
+ // for incoming messages and by MM composer for outgoing messages.
+ byte[] location = (byte[]) mPartHeader.get(P_NAME);
+ if(null == location) {
+ location = (byte[]) mPartHeader.get(P_FILENAME);
+
+ if (null == location) {
+ location = (byte[]) mPartHeader.get(P_CONTENT_LOCATION);
+ }
+ }
+
+ if (null == location) {
+ byte[] contentId = (byte[]) mPartHeader.get(P_CONTENT_ID);
+ return "cid:" + new String(contentId);
+ } else {
+ return new String(location);
+ }
+ }
+}
+
diff --git a/telephony/java/com/google/android/mms/pdu/PduPersister.java b/telephony/java/com/google/android/mms/pdu/PduPersister.java
new file mode 100755
index 000000000000..93f30657bf1b
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/PduPersister.java
@@ -0,0 +1,1573 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.drm.DrmManagerClient;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.Telephony;
+import android.provider.Telephony.Mms;
+import android.provider.Telephony.Mms.Addr;
+import android.provider.Telephony.Mms.Part;
+import android.provider.Telephony.MmsSms;
+import android.provider.Telephony.MmsSms.PendingMessages;
+import android.provider.Telephony.Threads;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.ContentType;
+import com.google.android.mms.InvalidHeaderValueException;
+import com.google.android.mms.MmsException;
+import com.google.android.mms.util.DownloadDrmHelper;
+import com.google.android.mms.util.DrmConvertSession;
+import com.google.android.mms.util.PduCache;
+import com.google.android.mms.util.PduCacheEntry;
+import com.google.android.mms.util.SqliteWrapper;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * This class is the high-level manager of PDU storage.
+ */
+public class PduPersister {
+ private static final String TAG = "PduPersister";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ private static final long DUMMY_THREAD_ID = Long.MAX_VALUE;
+
+ /**
+ * The uri of temporary drm objects.
+ */
+ public static final String TEMPORARY_DRM_OBJECT_URI =
+ "content://mms/" + Long.MAX_VALUE + "/part";
+ /**
+ * Indicate that we transiently failed to process a MM.
+ */
+ public static final int PROC_STATUS_TRANSIENT_FAILURE = 1;
+ /**
+ * Indicate that we permanently failed to process a MM.
+ */
+ public static final int PROC_STATUS_PERMANENTLY_FAILURE = 2;
+ /**
+ * Indicate that we have successfully processed a MM.
+ */
+ public static final int PROC_STATUS_COMPLETED = 3;
+
+ private static PduPersister sPersister;
+ @UnsupportedAppUsage
+ private static final PduCache PDU_CACHE_INSTANCE;
+
+ @UnsupportedAppUsage
+ private static final int[] ADDRESS_FIELDS = new int[] {
+ PduHeaders.BCC,
+ PduHeaders.CC,
+ PduHeaders.FROM,
+ PduHeaders.TO
+ };
+
+ private static final String[] PDU_PROJECTION = new String[] {
+ Mms._ID,
+ Mms.MESSAGE_BOX,
+ Mms.THREAD_ID,
+ Mms.RETRIEVE_TEXT,
+ Mms.SUBJECT,
+ Mms.CONTENT_LOCATION,
+ Mms.CONTENT_TYPE,
+ Mms.MESSAGE_CLASS,
+ Mms.MESSAGE_ID,
+ Mms.RESPONSE_TEXT,
+ Mms.TRANSACTION_ID,
+ Mms.CONTENT_CLASS,
+ Mms.DELIVERY_REPORT,
+ Mms.MESSAGE_TYPE,
+ Mms.MMS_VERSION,
+ Mms.PRIORITY,
+ Mms.READ_REPORT,
+ Mms.READ_STATUS,
+ Mms.REPORT_ALLOWED,
+ Mms.RETRIEVE_STATUS,
+ Mms.STATUS,
+ Mms.DATE,
+ Mms.DELIVERY_TIME,
+ Mms.EXPIRY,
+ Mms.MESSAGE_SIZE,
+ Mms.SUBJECT_CHARSET,
+ Mms.RETRIEVE_TEXT_CHARSET,
+ };
+
+ private static final int PDU_COLUMN_ID = 0;
+ private static final int PDU_COLUMN_MESSAGE_BOX = 1;
+ private static final int PDU_COLUMN_THREAD_ID = 2;
+ private static final int PDU_COLUMN_RETRIEVE_TEXT = 3;
+ private static final int PDU_COLUMN_SUBJECT = 4;
+ private static final int PDU_COLUMN_CONTENT_LOCATION = 5;
+ private static final int PDU_COLUMN_CONTENT_TYPE = 6;
+ private static final int PDU_COLUMN_MESSAGE_CLASS = 7;
+ private static final int PDU_COLUMN_MESSAGE_ID = 8;
+ private static final int PDU_COLUMN_RESPONSE_TEXT = 9;
+ private static final int PDU_COLUMN_TRANSACTION_ID = 10;
+ private static final int PDU_COLUMN_CONTENT_CLASS = 11;
+ private static final int PDU_COLUMN_DELIVERY_REPORT = 12;
+ private static final int PDU_COLUMN_MESSAGE_TYPE = 13;
+ private static final int PDU_COLUMN_MMS_VERSION = 14;
+ private static final int PDU_COLUMN_PRIORITY = 15;
+ private static final int PDU_COLUMN_READ_REPORT = 16;
+ private static final int PDU_COLUMN_READ_STATUS = 17;
+ private static final int PDU_COLUMN_REPORT_ALLOWED = 18;
+ private static final int PDU_COLUMN_RETRIEVE_STATUS = 19;
+ private static final int PDU_COLUMN_STATUS = 20;
+ private static final int PDU_COLUMN_DATE = 21;
+ private static final int PDU_COLUMN_DELIVERY_TIME = 22;
+ private static final int PDU_COLUMN_EXPIRY = 23;
+ private static final int PDU_COLUMN_MESSAGE_SIZE = 24;
+ private static final int PDU_COLUMN_SUBJECT_CHARSET = 25;
+ private static final int PDU_COLUMN_RETRIEVE_TEXT_CHARSET = 26;
+
+ @UnsupportedAppUsage
+ private static final String[] PART_PROJECTION = new String[] {
+ Part._ID,
+ Part.CHARSET,
+ Part.CONTENT_DISPOSITION,
+ Part.CONTENT_ID,
+ Part.CONTENT_LOCATION,
+ Part.CONTENT_TYPE,
+ Part.FILENAME,
+ Part.NAME,
+ Part.TEXT
+ };
+
+ private static final int PART_COLUMN_ID = 0;
+ private static final int PART_COLUMN_CHARSET = 1;
+ private static final int PART_COLUMN_CONTENT_DISPOSITION = 2;
+ private static final int PART_COLUMN_CONTENT_ID = 3;
+ private static final int PART_COLUMN_CONTENT_LOCATION = 4;
+ private static final int PART_COLUMN_CONTENT_TYPE = 5;
+ private static final int PART_COLUMN_FILENAME = 6;
+ private static final int PART_COLUMN_NAME = 7;
+ private static final int PART_COLUMN_TEXT = 8;
+
+ @UnsupportedAppUsage
+ private static final HashMap<Uri, Integer> MESSAGE_BOX_MAP;
+ // These map are used for convenience in persist() and load().
+ private static final HashMap<Integer, Integer> CHARSET_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> ENCODED_STRING_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> TEXT_STRING_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> OCTET_COLUMN_INDEX_MAP;
+ private static final HashMap<Integer, Integer> LONG_COLUMN_INDEX_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> CHARSET_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> ENCODED_STRING_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> TEXT_STRING_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> OCTET_COLUMN_NAME_MAP;
+ @UnsupportedAppUsage
+ private static final HashMap<Integer, String> LONG_COLUMN_NAME_MAP;
+
+ static {
+ MESSAGE_BOX_MAP = new HashMap<Uri, Integer>();
+ MESSAGE_BOX_MAP.put(Mms.Inbox.CONTENT_URI, Mms.MESSAGE_BOX_INBOX);
+ MESSAGE_BOX_MAP.put(Mms.Sent.CONTENT_URI, Mms.MESSAGE_BOX_SENT);
+ MESSAGE_BOX_MAP.put(Mms.Draft.CONTENT_URI, Mms.MESSAGE_BOX_DRAFTS);
+ MESSAGE_BOX_MAP.put(Mms.Outbox.CONTENT_URI, Mms.MESSAGE_BOX_OUTBOX);
+
+ CHARSET_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT_CHARSET);
+ CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT_CHARSET);
+
+ CHARSET_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ CHARSET_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT_CHARSET);
+ CHARSET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT_CHARSET);
+
+ // Encoded string field code -> column index/name map.
+ ENCODED_STRING_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT);
+ ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT);
+
+ ENCODED_STRING_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT);
+ ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT);
+
+ // Text string field code -> column index/name map.
+ TEXT_STRING_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_LOCATION, PDU_COLUMN_CONTENT_LOCATION);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_TYPE, PDU_COLUMN_CONTENT_TYPE);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_CLASS, PDU_COLUMN_MESSAGE_CLASS);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_ID, PDU_COLUMN_MESSAGE_ID);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RESPONSE_TEXT, PDU_COLUMN_RESPONSE_TEXT);
+ TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.TRANSACTION_ID, PDU_COLUMN_TRANSACTION_ID);
+
+ TEXT_STRING_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_LOCATION, Mms.CONTENT_LOCATION);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_TYPE, Mms.CONTENT_TYPE);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_CLASS, Mms.MESSAGE_CLASS);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_ID, Mms.MESSAGE_ID);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.RESPONSE_TEXT, Mms.RESPONSE_TEXT);
+ TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.TRANSACTION_ID, Mms.TRANSACTION_ID);
+
+ // Octet field code -> column index/name map.
+ OCTET_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_CLASS, PDU_COLUMN_CONTENT_CLASS);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_REPORT, PDU_COLUMN_DELIVERY_REPORT);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_TYPE, PDU_COLUMN_MESSAGE_TYPE);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MMS_VERSION, PDU_COLUMN_MMS_VERSION);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.PRIORITY, PDU_COLUMN_PRIORITY);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_REPORT, PDU_COLUMN_READ_REPORT);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_STATUS, PDU_COLUMN_READ_STATUS);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.REPORT_ALLOWED, PDU_COLUMN_REPORT_ALLOWED);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_STATUS, PDU_COLUMN_RETRIEVE_STATUS);
+ OCTET_COLUMN_INDEX_MAP.put(PduHeaders.STATUS, PDU_COLUMN_STATUS);
+
+ OCTET_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_CLASS, Mms.CONTENT_CLASS);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_REPORT, Mms.DELIVERY_REPORT);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_TYPE, Mms.MESSAGE_TYPE);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.MMS_VERSION, Mms.MMS_VERSION);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.PRIORITY, Mms.PRIORITY);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_REPORT, Mms.READ_REPORT);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_STATUS, Mms.READ_STATUS);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.REPORT_ALLOWED, Mms.REPORT_ALLOWED);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_STATUS, Mms.RETRIEVE_STATUS);
+ OCTET_COLUMN_NAME_MAP.put(PduHeaders.STATUS, Mms.STATUS);
+
+ // Long field code -> column index/name map.
+ LONG_COLUMN_INDEX_MAP = new HashMap<Integer, Integer>();
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.DATE, PDU_COLUMN_DATE);
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_TIME, PDU_COLUMN_DELIVERY_TIME);
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.EXPIRY, PDU_COLUMN_EXPIRY);
+ LONG_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_SIZE, PDU_COLUMN_MESSAGE_SIZE);
+
+ LONG_COLUMN_NAME_MAP = new HashMap<Integer, String>();
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.DATE, Mms.DATE);
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_TIME, Mms.DELIVERY_TIME);
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.EXPIRY, Mms.EXPIRY);
+ LONG_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_SIZE, Mms.MESSAGE_SIZE);
+
+ PDU_CACHE_INSTANCE = PduCache.getInstance();
+ }
+
+ @UnsupportedAppUsage
+ private final Context mContext;
+ @UnsupportedAppUsage
+ private final ContentResolver mContentResolver;
+ private final DrmManagerClient mDrmManagerClient;
+ @UnsupportedAppUsage
+ private final TelephonyManager mTelephonyManager;
+
+ private PduPersister(Context context) {
+ mContext = context;
+ mContentResolver = context.getContentResolver();
+ mDrmManagerClient = new DrmManagerClient(context);
+ mTelephonyManager = (TelephonyManager)context
+ .getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ /** Get(or create if not exist) an instance of PduPersister */
+ @UnsupportedAppUsage
+ public static PduPersister getPduPersister(Context context) {
+ if ((sPersister == null)) {
+ sPersister = new PduPersister(context);
+ } else if (!context.equals(sPersister.mContext)) {
+ sPersister.release();
+ sPersister = new PduPersister(context);
+ }
+
+ return sPersister;
+ }
+
+ private void setEncodedStringValueToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) {
+ String s = c.getString(columnIndex);
+ if ((s != null) && (s.length() > 0)) {
+ int charsetColumnIndex = CHARSET_COLUMN_INDEX_MAP.get(mapColumn);
+ int charset = c.getInt(charsetColumnIndex);
+ EncodedStringValue value = new EncodedStringValue(
+ charset, getBytes(s));
+ headers.setEncodedStringValue(value, mapColumn);
+ }
+ }
+
+ private void setTextStringToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) {
+ String s = c.getString(columnIndex);
+ if (s != null) {
+ headers.setTextString(getBytes(s), mapColumn);
+ }
+ }
+
+ private void setOctetToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) throws InvalidHeaderValueException {
+ if (!c.isNull(columnIndex)) {
+ int b = c.getInt(columnIndex);
+ headers.setOctet(b, mapColumn);
+ }
+ }
+
+ private void setLongToHeaders(
+ Cursor c, int columnIndex,
+ PduHeaders headers, int mapColumn) {
+ if (!c.isNull(columnIndex)) {
+ long l = c.getLong(columnIndex);
+ headers.setLongInteger(l, mapColumn);
+ }
+ }
+
+ @UnsupportedAppUsage
+ private Integer getIntegerFromPartColumn(Cursor c, int columnIndex) {
+ if (!c.isNull(columnIndex)) {
+ return c.getInt(columnIndex);
+ }
+ return null;
+ }
+
+ @UnsupportedAppUsage
+ private byte[] getByteArrayFromPartColumn(Cursor c, int columnIndex) {
+ if (!c.isNull(columnIndex)) {
+ return getBytes(c.getString(columnIndex));
+ }
+ return null;
+ }
+
+ private PduPart[] loadParts(long msgId) throws MmsException {
+ Cursor c = SqliteWrapper.query(mContext, mContentResolver,
+ Uri.parse("content://mms/" + msgId + "/part"),
+ PART_PROJECTION, null, null, null);
+
+ PduPart[] parts = null;
+
+ try {
+ if ((c == null) || (c.getCount() == 0)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "loadParts(" + msgId + "): no part to load.");
+ }
+ return null;
+ }
+
+ int partCount = c.getCount();
+ int partIdx = 0;
+ parts = new PduPart[partCount];
+ while (c.moveToNext()) {
+ PduPart part = new PduPart();
+ Integer charset = getIntegerFromPartColumn(
+ c, PART_COLUMN_CHARSET);
+ if (charset != null) {
+ part.setCharset(charset);
+ }
+
+ byte[] contentDisposition = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_DISPOSITION);
+ if (contentDisposition != null) {
+ part.setContentDisposition(contentDisposition);
+ }
+
+ byte[] contentId = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_ID);
+ if (contentId != null) {
+ part.setContentId(contentId);
+ }
+
+ byte[] contentLocation = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_LOCATION);
+ if (contentLocation != null) {
+ part.setContentLocation(contentLocation);
+ }
+
+ byte[] contentType = getByteArrayFromPartColumn(
+ c, PART_COLUMN_CONTENT_TYPE);
+ if (contentType != null) {
+ part.setContentType(contentType);
+ } else {
+ throw new MmsException("Content-Type must be set.");
+ }
+
+ byte[] fileName = getByteArrayFromPartColumn(
+ c, PART_COLUMN_FILENAME);
+ if (fileName != null) {
+ part.setFilename(fileName);
+ }
+
+ byte[] name = getByteArrayFromPartColumn(
+ c, PART_COLUMN_NAME);
+ if (name != null) {
+ part.setName(name);
+ }
+
+ // Construct a Uri for this part.
+ long partId = c.getLong(PART_COLUMN_ID);
+ Uri partURI = Uri.parse("content://mms/part/" + partId);
+ part.setDataUri(partURI);
+
+ // For images/audio/video, we won't keep their data in Part
+ // because their renderer accept Uri as source.
+ String type = toIsoString(contentType);
+ if (!ContentType.isImageType(type)
+ && !ContentType.isAudioType(type)
+ && !ContentType.isVideoType(type)) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ InputStream is = null;
+
+ // Store simple string values directly in the database instead of an
+ // external file. This makes the text searchable and retrieval slightly
+ // faster.
+ if (ContentType.TEXT_PLAIN.equals(type) || ContentType.APP_SMIL.equals(type)
+ || ContentType.TEXT_HTML.equals(type)) {
+ String text = c.getString(PART_COLUMN_TEXT);
+ byte [] blob = new EncodedStringValue(text != null ? text : "")
+ .getTextString();
+ baos.write(blob, 0, blob.length);
+ } else {
+
+ try {
+ is = mContentResolver.openInputStream(partURI);
+
+ byte[] buffer = new byte[256];
+ int len = is.read(buffer);
+ while (len >= 0) {
+ baos.write(buffer, 0, len);
+ len = is.read(buffer);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to load part data", e);
+ c.close();
+ throw new MmsException(e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close stream", e);
+ } // Ignore
+ }
+ }
+ }
+ part.setData(baos.toByteArray());
+ }
+ parts[partIdx++] = part;
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ return parts;
+ }
+
+ private void loadAddress(long msgId, PduHeaders headers) {
+ Cursor c = SqliteWrapper.query(mContext, mContentResolver,
+ Uri.parse("content://mms/" + msgId + "/addr"),
+ new String[] { Addr.ADDRESS, Addr.CHARSET, Addr.TYPE },
+ null, null, null);
+
+ if (c != null) {
+ try {
+ while (c.moveToNext()) {
+ String addr = c.getString(0);
+ if (!TextUtils.isEmpty(addr)) {
+ int addrType = c.getInt(2);
+ switch (addrType) {
+ case PduHeaders.FROM:
+ headers.setEncodedStringValue(
+ new EncodedStringValue(c.getInt(1), getBytes(addr)),
+ addrType);
+ break;
+ case PduHeaders.TO:
+ case PduHeaders.CC:
+ case PduHeaders.BCC:
+ headers.appendEncodedStringValue(
+ new EncodedStringValue(c.getInt(1), getBytes(addr)),
+ addrType);
+ break;
+ default:
+ Log.e(TAG, "Unknown address type: " + addrType);
+ break;
+ }
+ }
+ }
+ } finally {
+ c.close();
+ }
+ }
+ }
+
+ /**
+ * Load a PDU from storage by given Uri.
+ *
+ * @param uri The Uri of the PDU to be loaded.
+ * @return A generic PDU object, it may be cast to dedicated PDU.
+ * @throws MmsException Failed to load some fields of a PDU.
+ */
+ @UnsupportedAppUsage
+ public GenericPdu load(Uri uri) throws MmsException {
+ GenericPdu pdu = null;
+ PduCacheEntry cacheEntry = null;
+ int msgBox = 0;
+ long threadId = -1;
+ try {
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "load: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "load: ", e);
+ }
+ cacheEntry = PDU_CACHE_INSTANCE.get(uri);
+ if (cacheEntry != null) {
+ return cacheEntry.getPdu();
+ }
+ }
+ // Tell the cache to indicate to other callers that this item
+ // is currently being updated.
+ PDU_CACHE_INSTANCE.setUpdating(uri, true);
+ }
+
+ Cursor c = SqliteWrapper.query(mContext, mContentResolver, uri,
+ PDU_PROJECTION, null, null, null);
+ PduHeaders headers = new PduHeaders();
+ Set<Entry<Integer, Integer>> set;
+ long msgId = ContentUris.parseId(uri);
+
+ try {
+ if ((c == null) || (c.getCount() != 1) || !c.moveToFirst()) {
+ throw new MmsException("Bad uri: " + uri);
+ }
+
+ msgBox = c.getInt(PDU_COLUMN_MESSAGE_BOX);
+ threadId = c.getLong(PDU_COLUMN_THREAD_ID);
+
+ set = ENCODED_STRING_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setEncodedStringValueToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = TEXT_STRING_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setTextStringToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = OCTET_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setOctetToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+
+ set = LONG_COLUMN_INDEX_MAP.entrySet();
+ for (Entry<Integer, Integer> e : set) {
+ setLongToHeaders(
+ c, e.getValue(), headers, e.getKey());
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ // Check whether 'msgId' has been assigned a valid value.
+ if (msgId == -1L) {
+ throw new MmsException("Error! ID of the message: -1.");
+ }
+
+ // Load address information of the MM.
+ loadAddress(msgId, headers);
+
+ int msgType = headers.getOctet(PduHeaders.MESSAGE_TYPE);
+ PduBody body = new PduBody();
+
+ // For PDU which type is M_retrieve.conf or Send.req, we should
+ // load multiparts and put them into the body of the PDU.
+ if ((msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
+ || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
+ PduPart[] parts = loadParts(msgId);
+ if (parts != null) {
+ int partsNum = parts.length;
+ for (int i = 0; i < partsNum; i++) {
+ body.addPart(parts[i]);
+ }
+ }
+ }
+
+ switch (msgType) {
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ pdu = new NotificationInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
+ pdu = new DeliveryInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND:
+ pdu = new ReadOrigInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ pdu = new RetrieveConf(headers, body);
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ pdu = new SendReq(headers, body);
+ break;
+ case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND:
+ pdu = new AcknowledgeInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND:
+ pdu = new NotifyRespInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_READ_REC_IND:
+ pdu = new ReadRecInd(headers);
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_CONF:
+ case PduHeaders.MESSAGE_TYPE_FORWARD_REQ:
+ case PduHeaders.MESSAGE_TYPE_FORWARD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_MBOX_DESCR:
+ case PduHeaders.MESSAGE_TYPE_DELETE_REQ:
+ case PduHeaders.MESSAGE_TYPE_DELETE_CONF:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_REQ:
+ case PduHeaders.MESSAGE_TYPE_CANCEL_CONF:
+ throw new MmsException(
+ "Unsupported PDU type: " + Integer.toHexString(msgType));
+
+ default:
+ throw new MmsException(
+ "Unrecognized PDU type: " + Integer.toHexString(msgType));
+ }
+ } finally {
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (pdu != null) {
+ assert(PDU_CACHE_INSTANCE.get(uri) == null);
+ // Update the cache entry with the real info
+ cacheEntry = new PduCacheEntry(pdu, msgBox, threadId);
+ PDU_CACHE_INSTANCE.put(uri, cacheEntry);
+ }
+ PDU_CACHE_INSTANCE.setUpdating(uri, false);
+ PDU_CACHE_INSTANCE.notifyAll(); // tell anybody waiting on this entry to go ahead
+ }
+ }
+ return pdu;
+ }
+
+ @UnsupportedAppUsage
+ private void persistAddress(
+ long msgId, int type, EncodedStringValue[] array) {
+ ContentValues values = new ContentValues(3);
+
+ for (EncodedStringValue addr : array) {
+ values.clear(); // Clear all values first.
+ values.put(Addr.ADDRESS, toIsoString(addr.getTextString()));
+ values.put(Addr.CHARSET, addr.getCharacterSet());
+ values.put(Addr.TYPE, type);
+
+ Uri uri = Uri.parse("content://mms/" + msgId + "/addr");
+ SqliteWrapper.insert(mContext, mContentResolver, uri, values);
+ }
+ }
+
+ @UnsupportedAppUsage
+ private static String getPartContentType(PduPart part) {
+ return part.getContentType() == null ? null : toIsoString(part.getContentType());
+ }
+
+ @UnsupportedAppUsage
+ public Uri persistPart(PduPart part, long msgId, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ Uri uri = Uri.parse("content://mms/" + msgId + "/part");
+ ContentValues values = new ContentValues(8);
+
+ int charset = part.getCharset();
+ if (charset != 0 ) {
+ values.put(Part.CHARSET, charset);
+ }
+
+ String contentType = getPartContentType(part);
+ if (contentType != null) {
+ // There is no "image/jpg" in Android (and it's an invalid mimetype).
+ // Change it to "image/jpeg"
+ if (ContentType.IMAGE_JPG.equals(contentType)) {
+ contentType = ContentType.IMAGE_JPEG;
+ }
+
+ values.put(Part.CONTENT_TYPE, contentType);
+ // To ensure the SMIL part is always the first part.
+ if (ContentType.APP_SMIL.equals(contentType)) {
+ values.put(Part.SEQ, -1);
+ }
+ } else {
+ throw new MmsException("MIME type of the part must be set.");
+ }
+
+ if (part.getFilename() != null) {
+ String fileName = new String(part.getFilename());
+ values.put(Part.FILENAME, fileName);
+ }
+
+ if (part.getName() != null) {
+ String name = new String(part.getName());
+ values.put(Part.NAME, name);
+ }
+
+ Object value = null;
+ if (part.getContentDisposition() != null) {
+ value = toIsoString(part.getContentDisposition());
+ values.put(Part.CONTENT_DISPOSITION, (String) value);
+ }
+
+ if (part.getContentId() != null) {
+ value = toIsoString(part.getContentId());
+ values.put(Part.CONTENT_ID, (String) value);
+ }
+
+ if (part.getContentLocation() != null) {
+ value = toIsoString(part.getContentLocation());
+ values.put(Part.CONTENT_LOCATION, (String) value);
+ }
+
+ Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
+ if (res == null) {
+ throw new MmsException("Failed to persist part, return null.");
+ }
+
+ persistData(part, res, contentType, preOpenedFiles);
+ // After successfully store the data, we should update
+ // the dataUri of the part.
+ part.setDataUri(res);
+
+ return res;
+ }
+
+ /**
+ * Save data of the part into storage. The source data may be given
+ * by a byte[] or a Uri. If it's a byte[], directly save it
+ * into storage, otherwise load source data from the dataUri and then
+ * save it. If the data is an image, we may scale down it according
+ * to user preference.
+ *
+ * @param part The PDU part which contains data to be saved.
+ * @param uri The URI of the part.
+ * @param contentType The MIME type of the part.
+ * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
+ * @throws MmsException Cannot find source data or error occurred
+ * while saving the data.
+ */
+ private void persistData(PduPart part, Uri uri,
+ String contentType, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ OutputStream os = null;
+ InputStream is = null;
+ DrmConvertSession drmConvertSession = null;
+ Uri dataUri = null;
+ String path = null;
+
+ try {
+ byte[] data = part.getData();
+ if (ContentType.TEXT_PLAIN.equals(contentType)
+ || ContentType.APP_SMIL.equals(contentType)
+ || ContentType.TEXT_HTML.equals(contentType)) {
+ ContentValues cv = new ContentValues();
+ if (data == null) {
+ data = new String("").getBytes(CharacterSets.DEFAULT_CHARSET_NAME);
+ }
+ cv.put(Telephony.Mms.Part.TEXT, new EncodedStringValue(data).getString());
+ if (mContentResolver.update(uri, cv, null, null) != 1) {
+ throw new MmsException("unable to update " + uri.toString());
+ }
+ } else {
+ boolean isDrm = DownloadDrmHelper.isDrmConvertNeeded(contentType);
+ if (isDrm) {
+ if (uri != null) {
+ try (ParcelFileDescriptor pfd =
+ mContentResolver.openFileDescriptor(uri, "r")) {
+ if (pfd.getStatSize() > 0) {
+ // we're not going to re-persist and re-encrypt an already
+ // converted drm file
+ return;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Can't get file info for: " + part.getDataUri(), e);
+ }
+ }
+ // We haven't converted the file yet, start the conversion
+ drmConvertSession = DrmConvertSession.open(mContext, contentType);
+ if (drmConvertSession == null) {
+ throw new MmsException("Mimetype " + contentType +
+ " can not be converted.");
+ }
+ }
+ // uri can look like:
+ // content://mms/part/98
+ os = mContentResolver.openOutputStream(uri);
+ if (data == null) {
+ dataUri = part.getDataUri();
+ if ((dataUri == null) || (dataUri.equals(uri))) {
+ Log.w(TAG, "Can't find data for this part.");
+ return;
+ }
+ // dataUri can look like:
+ // content://com.google.android.gallery3d.provider/picasa/item/5720646660183715586
+ if (preOpenedFiles != null && preOpenedFiles.containsKey(dataUri)) {
+ is = preOpenedFiles.get(dataUri);
+ }
+ if (is == null) {
+ is = mContentResolver.openInputStream(dataUri);
+ }
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Saving data to: " + uri);
+ }
+
+ byte[] buffer = new byte[8192];
+ for (int len = 0; (len = is.read(buffer)) != -1; ) {
+ if (!isDrm) {
+ os.write(buffer, 0, len);
+ } else {
+ byte[] convertedData = drmConvertSession.convert(buffer, len);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
+ }
+ } else {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Saving data to: " + uri);
+ }
+ if (!isDrm) {
+ os.write(data);
+ } else {
+ dataUri = uri;
+ byte[] convertedData = drmConvertSession.convert(data, data.length);
+ if (convertedData != null) {
+ os.write(convertedData, 0, convertedData.length);
+ } else {
+ throw new MmsException("Error converting drm data.");
+ }
+ }
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Failed to open Input/Output stream.", e);
+ throw new MmsException(e);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to read/write data.", e);
+ throw new MmsException(e);
+ } finally {
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while closing: " + os, e);
+ } // Ignore
+ }
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while closing: " + is, e);
+ } // Ignore
+ }
+ if (drmConvertSession != null) {
+ drmConvertSession.close(path);
+
+ // Reset the permissions on the encrypted part file so everyone has only read
+ // permission.
+ File f = new File(path);
+ ContentValues values = new ContentValues(0);
+ SqliteWrapper.update(mContext, mContentResolver,
+ Uri.parse("content://mms/resetFilePerm/" + f.getName()),
+ values, null, null);
+ }
+ }
+ }
+
+ @UnsupportedAppUsage
+ private void updateAddress(
+ long msgId, int type, EncodedStringValue[] array) {
+ // Delete old address information and then insert new ones.
+ SqliteWrapper.delete(mContext, mContentResolver,
+ Uri.parse("content://mms/" + msgId + "/addr"),
+ Addr.TYPE + "=" + type, null);
+
+ persistAddress(msgId, type, array);
+ }
+
+ /**
+ * Update headers of a SendReq.
+ *
+ * @param uri The PDU which need to be updated.
+ * @param pdu New headers.
+ * @throws MmsException Bad URI or updating failed.
+ */
+ @UnsupportedAppUsage
+ public void updateHeaders(Uri uri, SendReq sendReq) {
+ synchronized(PDU_CACHE_INSTANCE) {
+ // If the cache item is getting updated, wait until it's done updating before
+ // purging it.
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "updateHeaders: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "updateHeaders: ", e);
+ }
+ }
+ }
+ PDU_CACHE_INSTANCE.purge(uri);
+
+ ContentValues values = new ContentValues(10);
+ byte[] contentType = sendReq.getContentType();
+ if (contentType != null) {
+ values.put(Mms.CONTENT_TYPE, toIsoString(contentType));
+ }
+
+ long date = sendReq.getDate();
+ if (date != -1) {
+ values.put(Mms.DATE, date);
+ }
+
+ int deliveryReport = sendReq.getDeliveryReport();
+ if (deliveryReport != 0) {
+ values.put(Mms.DELIVERY_REPORT, deliveryReport);
+ }
+
+ long expiry = sendReq.getExpiry();
+ if (expiry != -1) {
+ values.put(Mms.EXPIRY, expiry);
+ }
+
+ byte[] msgClass = sendReq.getMessageClass();
+ if (msgClass != null) {
+ values.put(Mms.MESSAGE_CLASS, toIsoString(msgClass));
+ }
+
+ int priority = sendReq.getPriority();
+ if (priority != 0) {
+ values.put(Mms.PRIORITY, priority);
+ }
+
+ int readReport = sendReq.getReadReport();
+ if (readReport != 0) {
+ values.put(Mms.READ_REPORT, readReport);
+ }
+
+ byte[] transId = sendReq.getTransactionId();
+ if (transId != null) {
+ values.put(Mms.TRANSACTION_ID, toIsoString(transId));
+ }
+
+ EncodedStringValue subject = sendReq.getSubject();
+ if (subject != null) {
+ values.put(Mms.SUBJECT, toIsoString(subject.getTextString()));
+ values.put(Mms.SUBJECT_CHARSET, subject.getCharacterSet());
+ } else {
+ values.put(Mms.SUBJECT, "");
+ }
+
+ long messageSize = sendReq.getMessageSize();
+ if (messageSize > 0) {
+ values.put(Mms.MESSAGE_SIZE, messageSize);
+ }
+
+ PduHeaders headers = sendReq.getPduHeaders();
+ HashSet<String> recipients = new HashSet<String>();
+ for (int addrType : ADDRESS_FIELDS) {
+ EncodedStringValue[] array = null;
+ if (addrType == PduHeaders.FROM) {
+ EncodedStringValue v = headers.getEncodedStringValue(addrType);
+ if (v != null) {
+ array = new EncodedStringValue[1];
+ array[0] = v;
+ }
+ } else {
+ array = headers.getEncodedStringValues(addrType);
+ }
+
+ if (array != null) {
+ long msgId = ContentUris.parseId(uri);
+ updateAddress(msgId, addrType, array);
+ if (addrType == PduHeaders.TO) {
+ for (EncodedStringValue v : array) {
+ if (v != null) {
+ recipients.add(v.getString());
+ }
+ }
+ }
+ }
+ }
+ if (!recipients.isEmpty()) {
+ long threadId = Threads.getOrCreateThreadId(mContext, recipients);
+ values.put(Mms.THREAD_ID, threadId);
+ }
+
+ SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
+ }
+
+ private void updatePart(Uri uri, PduPart part, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ ContentValues values = new ContentValues(7);
+
+ int charset = part.getCharset();
+ if (charset != 0 ) {
+ values.put(Part.CHARSET, charset);
+ }
+
+ String contentType = null;
+ if (part.getContentType() != null) {
+ contentType = toIsoString(part.getContentType());
+ values.put(Part.CONTENT_TYPE, contentType);
+ } else {
+ throw new MmsException("MIME type of the part must be set.");
+ }
+
+ if (part.getFilename() != null) {
+ String fileName = new String(part.getFilename());
+ values.put(Part.FILENAME, fileName);
+ }
+
+ if (part.getName() != null) {
+ String name = new String(part.getName());
+ values.put(Part.NAME, name);
+ }
+
+ Object value = null;
+ if (part.getContentDisposition() != null) {
+ value = toIsoString(part.getContentDisposition());
+ values.put(Part.CONTENT_DISPOSITION, (String) value);
+ }
+
+ if (part.getContentId() != null) {
+ value = toIsoString(part.getContentId());
+ values.put(Part.CONTENT_ID, (String) value);
+ }
+
+ if (part.getContentLocation() != null) {
+ value = toIsoString(part.getContentLocation());
+ values.put(Part.CONTENT_LOCATION, (String) value);
+ }
+
+ SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
+
+ // Only update the data when:
+ // 1. New binary data supplied or
+ // 2. The Uri of the part is different from the current one.
+ if ((part.getData() != null)
+ || (!uri.equals(part.getDataUri()))) {
+ persistData(part, uri, contentType, preOpenedFiles);
+ }
+ }
+
+ /**
+ * Update all parts of a PDU.
+ *
+ * @param uri The PDU which need to be updated.
+ * @param body New message body of the PDU.
+ * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
+ * @throws MmsException Bad URI or updating failed.
+ */
+ @UnsupportedAppUsage
+ public void updateParts(Uri uri, PduBody body, HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ try {
+ PduCacheEntry cacheEntry;
+ synchronized(PDU_CACHE_INSTANCE) {
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "updateParts: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "updateParts: ", e);
+ }
+ cacheEntry = PDU_CACHE_INSTANCE.get(uri);
+ if (cacheEntry != null) {
+ ((MultimediaMessagePdu) cacheEntry.getPdu()).setBody(body);
+ }
+ }
+ // Tell the cache to indicate to other callers that this item
+ // is currently being updated.
+ PDU_CACHE_INSTANCE.setUpdating(uri, true);
+ }
+
+ ArrayList<PduPart> toBeCreated = new ArrayList<PduPart>();
+ HashMap<Uri, PduPart> toBeUpdated = new HashMap<Uri, PduPart>();
+
+ int partsNum = body.getPartsNum();
+ StringBuilder filter = new StringBuilder().append('(');
+ for (int i = 0; i < partsNum; i++) {
+ PduPart part = body.getPart(i);
+ Uri partUri = part.getDataUri();
+ if ((partUri == null) || TextUtils.isEmpty(partUri.getAuthority())
+ || !partUri.getAuthority().startsWith("mms")) {
+ toBeCreated.add(part);
+ } else {
+ toBeUpdated.put(partUri, part);
+
+ // Don't use 'i > 0' to determine whether we should append
+ // 'AND' since 'i = 0' may be skipped in another branch.
+ if (filter.length() > 1) {
+ filter.append(" AND ");
+ }
+
+ filter.append(Part._ID);
+ filter.append("!=");
+ DatabaseUtils.appendEscapedSQLString(filter, partUri.getLastPathSegment());
+ }
+ }
+ filter.append(')');
+
+ long msgId = ContentUris.parseId(uri);
+
+ // Remove the parts which doesn't exist anymore.
+ SqliteWrapper.delete(mContext, mContentResolver,
+ Uri.parse(Mms.CONTENT_URI + "/" + msgId + "/part"),
+ filter.length() > 2 ? filter.toString() : null, null);
+
+ // Create new parts which didn't exist before.
+ for (PduPart part : toBeCreated) {
+ persistPart(part, msgId, preOpenedFiles);
+ }
+
+ // Update the modified parts.
+ for (Map.Entry<Uri, PduPart> e : toBeUpdated.entrySet()) {
+ updatePart(e.getKey(), e.getValue(), preOpenedFiles);
+ }
+ } finally {
+ synchronized(PDU_CACHE_INSTANCE) {
+ PDU_CACHE_INSTANCE.setUpdating(uri, false);
+ PDU_CACHE_INSTANCE.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * Persist a PDU object to specific location in the storage.
+ *
+ * @param pdu The PDU object to be stored.
+ * @param uri Where to store the given PDU object.
+ * @param createThreadId if true, this function may create a thread id for the recipients
+ * @param groupMmsEnabled if true, all of the recipients addressed in the PDU will be used
+ * to create the associated thread. When false, only the sender will be used in finding or
+ * creating the appropriate thread or conversation.
+ * @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
+ * @return A Uri which can be used to access the stored PDU.
+ */
+
+ @UnsupportedAppUsage
+ public Uri persist(GenericPdu pdu, Uri uri, boolean createThreadId, boolean groupMmsEnabled,
+ HashMap<Uri, InputStream> preOpenedFiles)
+ throws MmsException {
+ if (uri == null) {
+ throw new MmsException("Uri may not be null.");
+ }
+ long msgId = -1;
+ try {
+ msgId = ContentUris.parseId(uri);
+ } catch (NumberFormatException e) {
+ // the uri ends with "inbox" or something else like that
+ }
+ boolean existingUri = msgId != -1;
+
+ if (!existingUri && MESSAGE_BOX_MAP.get(uri) == null) {
+ throw new MmsException(
+ "Bad destination, must be one of "
+ + "content://mms/inbox, content://mms/sent, "
+ + "content://mms/drafts, content://mms/outbox, "
+ + "content://mms/temp.");
+ }
+ synchronized(PDU_CACHE_INSTANCE) {
+ // If the cache item is getting updated, wait until it's done updating before
+ // purging it.
+ if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "persist: " + uri + " blocked by isUpdating()");
+ }
+ try {
+ PDU_CACHE_INSTANCE.wait();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "persist1: ", e);
+ }
+ }
+ }
+ PDU_CACHE_INSTANCE.purge(uri);
+
+ PduHeaders header = pdu.getPduHeaders();
+ PduBody body = null;
+ ContentValues values = new ContentValues();
+ Set<Entry<Integer, String>> set;
+
+ set = ENCODED_STRING_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set) {
+ int field = e.getKey();
+ EncodedStringValue encodedString = header.getEncodedStringValue(field);
+ if (encodedString != null) {
+ String charsetColumn = CHARSET_COLUMN_NAME_MAP.get(field);
+ values.put(e.getValue(), toIsoString(encodedString.getTextString()));
+ values.put(charsetColumn, encodedString.getCharacterSet());
+ }
+ }
+
+ set = TEXT_STRING_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set){
+ byte[] text = header.getTextString(e.getKey());
+ if (text != null) {
+ values.put(e.getValue(), toIsoString(text));
+ }
+ }
+
+ set = OCTET_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set){
+ int b = header.getOctet(e.getKey());
+ if (b != 0) {
+ values.put(e.getValue(), b);
+ }
+ }
+
+ set = LONG_COLUMN_NAME_MAP.entrySet();
+ for (Entry<Integer, String> e : set){
+ long l = header.getLongInteger(e.getKey());
+ if (l != -1L) {
+ values.put(e.getValue(), l);
+ }
+ }
+
+ HashMap<Integer, EncodedStringValue[]> addressMap =
+ new HashMap<Integer, EncodedStringValue[]>(ADDRESS_FIELDS.length);
+ // Save address information.
+ for (int addrType : ADDRESS_FIELDS) {
+ EncodedStringValue[] array = null;
+ if (addrType == PduHeaders.FROM) {
+ EncodedStringValue v = header.getEncodedStringValue(addrType);
+ if (v != null) {
+ array = new EncodedStringValue[1];
+ array[0] = v;
+ }
+ } else {
+ array = header.getEncodedStringValues(addrType);
+ }
+ addressMap.put(addrType, array);
+ }
+
+ HashSet<String> recipients = new HashSet<String>();
+ int msgType = pdu.getMessageType();
+ // Here we only allocate thread ID for M-Notification.ind,
+ // M-Retrieve.conf and M-Send.req.
+ // Some of other PDU types may be allocated a thread ID outside
+ // this scope.
+ if ((msgType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND)
+ || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)
+ || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) {
+ switch (msgType) {
+ case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND:
+ case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF:
+ loadRecipients(PduHeaders.FROM, recipients, addressMap, false);
+
+ // For received messages when group MMS is enabled, we want to associate this
+ // message with the thread composed of all the recipients -- all but our own
+ // number, that is. This includes the person who sent the
+ // message or the FROM field (above) in addition to the other people the message
+ // was addressed to or the TO field. Our own number is in that TO field and
+ // we have to ignore it in loadRecipients.
+ if (groupMmsEnabled) {
+ loadRecipients(PduHeaders.TO, recipients, addressMap, true);
+
+ // Also load any numbers in the CC field to address group messaging
+ // compatibility issues with devices that place numbers in this field
+ // for group messages.
+ loadRecipients(PduHeaders.CC, recipients, addressMap, true);
+ }
+ break;
+ case PduHeaders.MESSAGE_TYPE_SEND_REQ:
+ loadRecipients(PduHeaders.TO, recipients, addressMap, false);
+ break;
+ }
+ long threadId = 0;
+ if (createThreadId && !recipients.isEmpty()) {
+ // Given all the recipients associated with this message, find (or create) the
+ // correct thread.
+ threadId = Threads.getOrCreateThreadId(mContext, recipients);
+ }
+ values.put(Mms.THREAD_ID, threadId);
+ }
+
+ // Save parts first to avoid inconsistent message is loaded
+ // while saving the parts.
+ long dummyId = System.currentTimeMillis(); // Dummy ID of the msg.
+
+ // Figure out if this PDU is a text-only message
+ boolean textOnly = true;
+
+ // Sum up the total message size
+ int messageSize = 0;
+
+ // Get body if the PDU is a RetrieveConf or SendReq.
+ if (pdu instanceof MultimediaMessagePdu) {
+ body = ((MultimediaMessagePdu) pdu).getBody();
+ // Start saving parts if necessary.
+ if (body != null) {
+ int partsNum = body.getPartsNum();
+ if (partsNum > 2) {
+ // For a text-only message there will be two parts: 1-the SMIL, 2-the text.
+ // Down a few lines below we're checking to make sure we've only got SMIL or
+ // text. We also have to check then we don't have more than two parts.
+ // Otherwise, a slideshow with two text slides would be marked as textOnly.
+ textOnly = false;
+ }
+ for (int i = 0; i < partsNum; i++) {
+ PduPart part = body.getPart(i);
+ messageSize += part.getDataLength();
+ persistPart(part, dummyId, preOpenedFiles);
+
+ // If we've got anything besides text/plain or SMIL part, then we've got
+ // an mms message with some other type of attachment.
+ String contentType = getPartContentType(part);
+ if (contentType != null && !ContentType.APP_SMIL.equals(contentType)
+ && !ContentType.TEXT_PLAIN.equals(contentType)) {
+ textOnly = false;
+ }
+ }
+ }
+ }
+ // Record whether this mms message is a simple plain text or not. This is a hint for the
+ // UI.
+ values.put(Mms.TEXT_ONLY, textOnly ? 1 : 0);
+ // The message-size might already have been inserted when parsing the
+ // PDU header. If not, then we insert the message size as well.
+ if (values.getAsInteger(Mms.MESSAGE_SIZE) == null) {
+ values.put(Mms.MESSAGE_SIZE, messageSize);
+ }
+
+ Uri res = null;
+ if (existingUri) {
+ res = uri;
+ SqliteWrapper.update(mContext, mContentResolver, res, values, null, null);
+ } else {
+ res = SqliteWrapper.insert(mContext, mContentResolver, uri, values);
+ if (res == null) {
+ throw new MmsException("persist() failed: return null.");
+ }
+ // Get the real ID of the PDU and update all parts which were
+ // saved with the dummy ID.
+ msgId = ContentUris.parseId(res);
+ }
+
+ values = new ContentValues(1);
+ values.put(Part.MSG_ID, msgId);
+ SqliteWrapper.update(mContext, mContentResolver,
+ Uri.parse("content://mms/" + dummyId + "/part"),
+ values, null, null);
+ // We should return the longest URI of the persisted PDU, for
+ // example, if input URI is "content://mms/inbox" and the _ID of
+ // persisted PDU is '8', we should return "content://mms/inbox/8"
+ // instead of "content://mms/8".
+ // FIXME: Should the MmsProvider be responsible for this???
+ if (!existingUri) {
+ res = Uri.parse(uri + "/" + msgId);
+ }
+
+ // Save address information.
+ for (int addrType : ADDRESS_FIELDS) {
+ EncodedStringValue[] array = addressMap.get(addrType);
+ if (array != null) {
+ persistAddress(msgId, addrType, array);
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * For a given address type, extract the recipients from the headers.
+ *
+ * @param addressType can be PduHeaders.FROM, PduHeaders.TO or PduHeaders.CC
+ * @param recipients a HashSet that is loaded with the recipients from the FROM, TO or CC headers
+ * @param addressMap a HashMap of the addresses from the ADDRESS_FIELDS header
+ * @param excludeMyNumber if true, the number of this phone will be excluded from recipients
+ */
+ @UnsupportedAppUsage
+ private void loadRecipients(int addressType, HashSet<String> recipients,
+ HashMap<Integer, EncodedStringValue[]> addressMap, boolean excludeMyNumber) {
+ EncodedStringValue[] array = addressMap.get(addressType);
+ if (array == null) {
+ return;
+ }
+ // If the TO recipients is only a single address, then we can skip loadRecipients when
+ // we're excluding our own number because we know that address is our own.
+ if (excludeMyNumber && array.length == 1) {
+ return;
+ }
+ final SubscriptionManager subscriptionManager = SubscriptionManager.from(mContext);
+ final Set<String> myPhoneNumbers = new HashSet<String>();
+ if (excludeMyNumber) {
+ // Build a list of my phone numbers from the various sims.
+ for (int subid : subscriptionManager.getActiveSubscriptionIdList()) {
+ final String myNumber = mTelephonyManager.getLine1Number(subid);
+ if (myNumber != null) {
+ myPhoneNumbers.add(myNumber);
+ }
+ }
+ }
+
+ for (EncodedStringValue v : array) {
+ if (v != null) {
+ final String number = v.getString();
+ if (excludeMyNumber) {
+ for (final String myNumber : myPhoneNumbers) {
+ if (!PhoneNumberUtils.compare(number, myNumber)
+ && !recipients.contains(number)) {
+ // Only add numbers which aren't my own number.
+ recipients.add(number);
+ break;
+ }
+ }
+ } else if (!recipients.contains(number)){
+ recipients.add(number);
+ }
+ }
+ }
+ }
+
+ /**
+ * Move a PDU object from one location to another.
+ *
+ * @param from Specify the PDU object to be moved.
+ * @param to The destination location, should be one of the following:
+ * "content://mms/inbox", "content://mms/sent",
+ * "content://mms/drafts", "content://mms/outbox",
+ * "content://mms/trash".
+ * @return New Uri of the moved PDU.
+ * @throws MmsException Error occurred while moving the message.
+ */
+ @UnsupportedAppUsage
+ public Uri move(Uri from, Uri to) throws MmsException {
+ // Check whether the 'msgId' has been assigned a valid value.
+ long msgId = ContentUris.parseId(from);
+ if (msgId == -1L) {
+ throw new MmsException("Error! ID of the message: -1.");
+ }
+
+ // Get corresponding int value of destination box.
+ Integer msgBox = MESSAGE_BOX_MAP.get(to);
+ if (msgBox == null) {
+ throw new MmsException(
+ "Bad destination, must be one of "
+ + "content://mms/inbox, content://mms/sent, "
+ + "content://mms/drafts, content://mms/outbox, "
+ + "content://mms/temp.");
+ }
+
+ ContentValues values = new ContentValues(1);
+ values.put(Mms.MESSAGE_BOX, msgBox);
+ SqliteWrapper.update(mContext, mContentResolver, from, values, null, null);
+ return ContentUris.withAppendedId(to, msgId);
+ }
+
+ /**
+ * Wrap a byte[] into a String.
+ */
+ @UnsupportedAppUsage
+ public static String toIsoString(byte[] bytes) {
+ try {
+ return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1);
+ } catch (UnsupportedEncodingException e) {
+ // Impossible to reach here!
+ Log.e(TAG, "ISO_8859_1 must be supported!", e);
+ return "";
+ }
+ }
+
+ /**
+ * Unpack a given String into a byte[].
+ */
+ @UnsupportedAppUsage
+ public static byte[] getBytes(String data) {
+ try {
+ return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1);
+ } catch (UnsupportedEncodingException e) {
+ // Impossible to reach here!
+ Log.e(TAG, "ISO_8859_1 must be supported!", e);
+ return new byte[0];
+ }
+ }
+
+ /**
+ * Remove all objects in the temporary path.
+ */
+ public void release() {
+ Uri uri = Uri.parse(TEMPORARY_DRM_OBJECT_URI);
+ SqliteWrapper.delete(mContext, mContentResolver, uri, null, null);
+ }
+
+ /**
+ * Find all messages to be sent or downloaded before certain time.
+ */
+ @UnsupportedAppUsage
+ public Cursor getPendingMessages(long dueTime) {
+ Uri.Builder uriBuilder = PendingMessages.CONTENT_URI.buildUpon();
+ uriBuilder.appendQueryParameter("protocol", "mms");
+
+ String selection = PendingMessages.ERROR_TYPE + " < ?"
+ + " AND " + PendingMessages.DUE_TIME + " <= ?";
+
+ String[] selectionArgs = new String[] {
+ String.valueOf(MmsSms.ERR_TYPE_GENERIC_PERMANENT),
+ String.valueOf(dueTime)
+ };
+
+ return SqliteWrapper.query(mContext, mContentResolver,
+ uriBuilder.build(), null, selection, selectionArgs,
+ PendingMessages.DUE_TIME);
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/QuotedPrintable.java b/telephony/java/com/google/android/mms/pdu/QuotedPrintable.java
new file mode 100644
index 000000000000..9d6535c72e90
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/QuotedPrintable.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.ByteArrayOutputStream;
+
+public class QuotedPrintable {
+ private static byte ESCAPE_CHAR = '=';
+
+ /**
+ * Decodes an array quoted-printable characters into an array of original bytes.
+ * Escaped characters are converted back to their original representation.
+ *
+ * <p>
+ * This function implements a subset of
+ * quoted-printable encoding specification (rule #1 and rule #2)
+ * as defined in RFC 1521.
+ * </p>
+ *
+ * @param bytes array of quoted-printable characters
+ * @return array of original bytes,
+ * null if quoted-printable decoding is unsuccessful.
+ */
+ @UnsupportedAppUsage
+ public static final byte[] decodeQuotedPrintable(byte[] bytes) {
+ if (bytes == null) {
+ return null;
+ }
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ for (int i = 0; i < bytes.length; i++) {
+ int b = bytes[i];
+ if (b == ESCAPE_CHAR) {
+ try {
+ if('\r' == (char)bytes[i + 1] &&
+ '\n' == (char)bytes[i + 2]) {
+ i += 2;
+ continue;
+ }
+ int u = Character.digit((char) bytes[++i], 16);
+ int l = Character.digit((char) bytes[++i], 16);
+ if (u == -1 || l == -1) {
+ return null;
+ }
+ buffer.write((char) ((u << 4) + l));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ } else {
+ buffer.write(b);
+ }
+ }
+ return buffer.toByteArray();
+ }
+}
diff --git a/telephony/java/com/google/android/mms/pdu/ReadOrigInd.java b/telephony/java/com/google/android/mms/pdu/ReadOrigInd.java
new file mode 100644
index 000000000000..e38c62dde622
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/ReadOrigInd.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class ReadOrigInd extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ public ReadOrigInd() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_READ_ORIG_IND);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ ReadOrigInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value.
+ *
+ * @param value the value
+ */
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get X-MMS-Read-status value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getReadStatus() {
+ return mPduHeaders.getOctet(PduHeaders.READ_STATUS);
+ }
+
+ /**
+ * Set X-MMS-Read-status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setReadStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_STATUS);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * Set To value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/ReadRecInd.java b/telephony/java/com/google/android/mms/pdu/ReadRecInd.java
new file mode 100644
index 000000000000..9696bc259d00
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/ReadRecInd.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class ReadRecInd extends GenericPdu {
+ /**
+ * Constructor, used when composing a M-ReadRec.ind pdu.
+ *
+ * @param from the from value
+ * @param messageId the message ID value
+ * @param mmsVersion current viersion of mms
+ * @param readStatus the read status value
+ * @param to the to value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if messageId or to is null.
+ */
+ @UnsupportedAppUsage
+ public ReadRecInd(EncodedStringValue from,
+ byte[] messageId,
+ int mmsVersion,
+ int readStatus,
+ EncodedStringValue[] to) throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_READ_REC_IND);
+ setFrom(from);
+ setMessageId(messageId);
+ setMmsVersion(mmsVersion);
+ setTo(to);
+ setReadStatus(readStatus);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ ReadRecInd(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Date value.
+ *
+ * @return the value
+ */
+ public long getDate() {
+ return mPduHeaders.getLongInteger(PduHeaders.DATE);
+ }
+
+ /**
+ * Set Date value.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setDate(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.DATE);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get To value.
+ *
+ * @return the value
+ */
+ public EncodedStringValue[] getTo() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.TO);
+ }
+
+ /**
+ * Set To value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /**
+ * Get X-MMS-Read-status value.
+ *
+ * @return the value
+ */
+ public int getReadStatus() {
+ return mPduHeaders.getOctet(PduHeaders.READ_STATUS);
+ }
+
+ /**
+ * Set X-MMS-Read-status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setReadStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_STATUS);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/RetrieveConf.java b/telephony/java/com/google/android/mms/pdu/RetrieveConf.java
new file mode 100644
index 000000000000..03755af4189c
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/RetrieveConf.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+/**
+ * M-Retrieve.conf Pdu.
+ */
+public class RetrieveConf extends MultimediaMessagePdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ @UnsupportedAppUsage
+ public RetrieveConf() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ RetrieveConf(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Constructor with given headers and body
+ *
+ * @param headers Headers for this PDU.
+ * @param body Body of this PDu.
+ */
+ @UnsupportedAppUsage
+ RetrieveConf(PduHeaders headers, PduBody body) {
+ super(headers, body);
+ }
+
+ /**
+ * Get CC value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getCc() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.CC);
+ }
+
+ /**
+ * Add a "CC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addCc(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC);
+ }
+
+ /**
+ * Get Content-type value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentType() {
+ return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Set Content-type value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentType(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Get X-Mms-Delivery-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getDeliveryReport() {
+ return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Delivery-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setDeliveryReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Get From value.
+ * From-value = Value-length
+ * (Address-present-token Encoded-string-value | Insert-address-token)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getFrom() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.FROM);
+ }
+
+ /**
+ * Set From value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setFrom(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM);
+ }
+
+ /**
+ * Get X-Mms-Message-Class value.
+ * Message-class-value = Class-identifier | Token-text
+ * Class-identifier = Personal | Advertisement | Informational | Auto
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageClass() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Message-Class value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setMessageClass(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get X-Mms-Read-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getReadReport() {
+ return mPduHeaders.getOctet(PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Read-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setReadReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Get X-Mms-Retrieve-Status value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getRetrieveStatus() {
+ return mPduHeaders.getOctet(PduHeaders.RETRIEVE_STATUS);
+ }
+
+ /**
+ * Set X-Mms-Retrieve-Status value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setRetrieveStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.RETRIEVE_STATUS);
+ }
+
+ /**
+ * Get X-Mms-Retrieve-Text value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue getRetrieveText() {
+ return mPduHeaders.getEncodedStringValue(PduHeaders.RETRIEVE_TEXT);
+ }
+
+ /**
+ * Set X-Mms-Retrieve-Text value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setRetrieveText(EncodedStringValue value) {
+ mPduHeaders.setEncodedStringValue(value, PduHeaders.RETRIEVE_TEXT);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte getContentClass() {return 0x00;}
+ * public void setApplicId(byte value) {}
+ *
+ * public byte getDrmContent() {return 0x00;}
+ * public void setDrmContent(byte value) {}
+ *
+ * public byte getDistributionIndicator() {return 0x00;}
+ * public void setDistributionIndicator(byte value) {}
+ *
+ * public PreviouslySentByValue getPreviouslySentBy() {return null;}
+ * public void setPreviouslySentBy(PreviouslySentByValue value) {}
+ *
+ * public PreviouslySentDateValue getPreviouslySentDate() {}
+ * public void setPreviouslySentDate(PreviouslySentDateValue value) {}
+ *
+ * public MmFlagsValue getMmFlags() {return null;}
+ * public void setMmFlags(MmFlagsValue value) {}
+ *
+ * public MmStateValue getMmState() {return null;}
+ * public void getMmState(MmStateValue value) {}
+ *
+ * public byte[] getReplaceId() {return 0x00;}
+ * public void setReplaceId(byte[] value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getReplyCharging() {return 0x00;}
+ * public void setReplyCharging(byte value) {}
+ *
+ * public byte getReplyChargingDeadline() {return 0x00;}
+ * public void setReplyChargingDeadline(byte value) {}
+ *
+ * public byte[] getReplyChargingId() {return 0x00;}
+ * public void setReplyChargingId(byte[] value) {}
+ *
+ * public long getReplyChargingSize() {return 0;}
+ * public void setReplyChargingSize(long value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/SendConf.java b/telephony/java/com/google/android/mms/pdu/SendConf.java
new file mode 100644
index 000000000000..b85982791ada
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/SendConf.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2007 Esmertec AG.
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class SendConf extends GenericPdu {
+ /**
+ * Empty constructor.
+ * Since the Pdu corresponding to this class is constructed
+ * by the Proxy-Relay server, this class is only instantiated
+ * by the Pdu Parser.
+ *
+ * @throws InvalidHeaderValueException if error occurs.
+ */
+ @UnsupportedAppUsage
+ public SendConf() throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_SEND_CONF);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ @UnsupportedAppUsage
+ SendConf(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Get Message-ID value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageId() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Set Message-ID value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setMessageId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID);
+ }
+
+ /**
+ * Get X-Mms-Response-Status.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getResponseStatus() {
+ return mPduHeaders.getOctet(PduHeaders.RESPONSE_STATUS);
+ }
+
+ /**
+ * Set X-Mms-Response-Status.
+ *
+ * @param value the values
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ public void setResponseStatus(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.RESPONSE_STATUS);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte[] getContentLocation() {return null;}
+ * public void setContentLocation(byte[] value) {}
+ *
+ * public EncodedStringValue getResponseText() {return null;}
+ * public void setResponseText(EncodedStringValue value) {}
+ *
+ * public byte getStoreStatus() {return 0x00;}
+ * public void setStoreStatus(byte value) {}
+ *
+ * public byte[] getStoreStatusText() {return null;}
+ * public void setStoreStatusText(byte[] value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/SendReq.java b/telephony/java/com/google/android/mms/pdu/SendReq.java
new file mode 100644
index 000000000000..c1b7f934c0f7
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/SendReq.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2007-2008 Esmertec AG.
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.pdu;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.InvalidHeaderValueException;
+
+public class SendReq extends MultimediaMessagePdu {
+ private static final String TAG = "SendReq";
+
+ @UnsupportedAppUsage
+ public SendReq() {
+ super();
+
+ try {
+ setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ);
+ setMmsVersion(PduHeaders.CURRENT_MMS_VERSION);
+ // FIXME: Content-type must be decided according to whether
+ // SMIL part present.
+ setContentType("application/vnd.wap.multipart.related".getBytes());
+ setFrom(new EncodedStringValue(PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes()));
+ setTransactionId(generateTransactionId());
+ } catch (InvalidHeaderValueException e) {
+ // Impossible to reach here since all headers we set above are valid.
+ Log.e(TAG, "Unexpected InvalidHeaderValueException.", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private byte[] generateTransactionId() {
+ String transactionId = "T" + Long.toHexString(System.currentTimeMillis());
+ return transactionId.getBytes();
+ }
+
+ /**
+ * Constructor, used when composing a M-Send.req pdu.
+ *
+ * @param contentType the content type value
+ * @param from the from value
+ * @param mmsVersion current viersion of mms
+ * @param transactionId the transaction-id value
+ * @throws InvalidHeaderValueException if parameters are invalid.
+ * NullPointerException if contentType, form or transactionId is null.
+ */
+ public SendReq(byte[] contentType,
+ EncodedStringValue from,
+ int mmsVersion,
+ byte[] transactionId) throws InvalidHeaderValueException {
+ super();
+ setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ);
+ setContentType(contentType);
+ setFrom(from);
+ setMmsVersion(mmsVersion);
+ setTransactionId(transactionId);
+ }
+
+ /**
+ * Constructor with given headers.
+ *
+ * @param headers Headers for this PDU.
+ */
+ SendReq(PduHeaders headers) {
+ super(headers);
+ }
+
+ /**
+ * Constructor with given headers and body
+ *
+ * @param headers Headers for this PDU.
+ * @param body Body of this PDu.
+ */
+ @UnsupportedAppUsage
+ SendReq(PduHeaders headers, PduBody body) {
+ super(headers, body);
+ }
+
+ /**
+ * Get Bcc value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getBcc() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.BCC);
+ }
+
+ /**
+ * Add a "BCC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addBcc(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.BCC);
+ }
+
+ /**
+ * Set "BCC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setBcc(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.BCC);
+ }
+
+ /**
+ * Get CC value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public EncodedStringValue[] getCc() {
+ return mPduHeaders.getEncodedStringValues(PduHeaders.CC);
+ }
+
+ /**
+ * Add a "CC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void addCc(EncodedStringValue value) {
+ mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC);
+ }
+
+ /**
+ * Set "CC" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setCc(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.CC);
+ }
+
+ /**
+ * Get Content-type value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getContentType() {
+ return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Set Content-type value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setContentType(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE);
+ }
+
+ /**
+ * Get X-Mms-Delivery-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getDeliveryReport() {
+ return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Delivery-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setDeliveryReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT);
+ }
+
+ /**
+ * Get X-Mms-Expiry value.
+ *
+ * Expiry-value = Value-length
+ * (Absolute-token Date-value | Relative-token Delta-seconds-value)
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getExpiry() {
+ return mPduHeaders.getLongInteger(PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Set X-Mms-Expiry value.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setExpiry(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY);
+ }
+
+ /**
+ * Get X-Mms-MessageSize value.
+ *
+ * Expiry-value = size of message
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public long getMessageSize() {
+ return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Set X-Mms-MessageSize value.
+ *
+ * @param value the value
+ */
+ @UnsupportedAppUsage
+ public void setMessageSize(long value) {
+ mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE);
+ }
+
+ /**
+ * Get X-Mms-Message-Class value.
+ * Message-class-value = Class-identifier | Token-text
+ * Class-identifier = Personal | Advertisement | Informational | Auto
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public byte[] getMessageClass() {
+ return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Set X-Mms-Message-Class value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setMessageClass(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS);
+ }
+
+ /**
+ * Get X-Mms-Read-Report value.
+ *
+ * @return the value
+ */
+ @UnsupportedAppUsage
+ public int getReadReport() {
+ return mPduHeaders.getOctet(PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Set X-Mms-Read-Report value.
+ *
+ * @param value the value
+ * @throws InvalidHeaderValueException if the value is invalid.
+ */
+ @UnsupportedAppUsage
+ public void setReadReport(int value) throws InvalidHeaderValueException {
+ mPduHeaders.setOctet(value, PduHeaders.READ_REPORT);
+ }
+
+ /**
+ * Set "To" value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTo(EncodedStringValue[] value) {
+ mPduHeaders.setEncodedStringValues(value, PduHeaders.TO);
+ }
+
+ /**
+ * Get X-Mms-Transaction-Id field value.
+ *
+ * @return the X-Mms-Report-Allowed value
+ */
+ @UnsupportedAppUsage
+ public byte[] getTransactionId() {
+ return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID);
+ }
+
+ /**
+ * Set X-Mms-Transaction-Id field value.
+ *
+ * @param value the value
+ * @throws NullPointerException if the value is null.
+ */
+ @UnsupportedAppUsage
+ public void setTransactionId(byte[] value) {
+ mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID);
+ }
+
+ /*
+ * Optional, not supported header fields:
+ *
+ * public byte getAdaptationAllowed() {return 0};
+ * public void setAdaptationAllowed(btye value) {};
+ *
+ * public byte[] getApplicId() {return null;}
+ * public void setApplicId(byte[] value) {}
+ *
+ * public byte[] getAuxApplicId() {return null;}
+ * public void getAuxApplicId(byte[] value) {}
+ *
+ * public byte getContentClass() {return 0x00;}
+ * public void setApplicId(byte value) {}
+ *
+ * public long getDeliveryTime() {return 0};
+ * public void setDeliveryTime(long value) {};
+ *
+ * public byte getDrmContent() {return 0x00;}
+ * public void setDrmContent(byte value) {}
+ *
+ * public MmFlagsValue getMmFlags() {return null;}
+ * public void setMmFlags(MmFlagsValue value) {}
+ *
+ * public MmStateValue getMmState() {return null;}
+ * public void getMmState(MmStateValue value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getReplyCharging() {return 0x00;}
+ * public void setReplyCharging(byte value) {}
+ *
+ * public byte getReplyChargingDeadline() {return 0x00;}
+ * public void setReplyChargingDeadline(byte value) {}
+ *
+ * public byte[] getReplyChargingId() {return 0x00;}
+ * public void setReplyChargingId(byte[] value) {}
+ *
+ * public long getReplyChargingSize() {return 0;}
+ * public void setReplyChargingSize(long value) {}
+ *
+ * public byte[] getReplyApplicId() {return 0x00;}
+ * public void setReplyApplicId(byte[] value) {}
+ *
+ * public byte getStore() {return 0x00;}
+ * public void setStore(byte value) {}
+ */
+}
diff --git a/telephony/java/com/google/android/mms/pdu/package.html b/telephony/java/com/google/android/mms/pdu/package.html
new file mode 100755
index 000000000000..c9f96a66ab3b
--- /dev/null
+++ b/telephony/java/com/google/android/mms/pdu/package.html
@@ -0,0 +1,5 @@
+<body>
+
+{@hide}
+
+</body>
diff --git a/telephony/java/com/google/android/mms/util/AbstractCache.java b/telephony/java/com/google/android/mms/util/AbstractCache.java
new file mode 100644
index 000000000000..ab5d48a4ce3d
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/AbstractCache.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.util;
+
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+
+public abstract class AbstractCache<K, V> {
+ private static final String TAG = "AbstractCache";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ private static final int MAX_CACHED_ITEMS = 500;
+
+ private final HashMap<K, CacheEntry<V>> mCacheMap;
+
+ @UnsupportedAppUsage
+ protected AbstractCache() {
+ mCacheMap = new HashMap<K, CacheEntry<V>>();
+ }
+
+ @UnsupportedAppUsage
+ public boolean put(K key, V value) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Trying to put " + key + " into cache.");
+ }
+
+ if (mCacheMap.size() >= MAX_CACHED_ITEMS) {
+ // TODO Should remove the oldest or least hit cached entry
+ // and then cache the new one.
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Failed! size limitation reached.");
+ }
+ return false;
+ }
+
+ if (key != null) {
+ CacheEntry<V> cacheEntry = new CacheEntry<V>();
+ cacheEntry.value = value;
+ mCacheMap.put(key, cacheEntry);
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, key + " cached, " + mCacheMap.size() + " items total.");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @UnsupportedAppUsage
+ public V get(K key) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Trying to get " + key + " from cache.");
+ }
+
+ if (key != null) {
+ CacheEntry<V> cacheEntry = mCacheMap.get(key);
+ if (cacheEntry != null) {
+ cacheEntry.hit++;
+ if (LOCAL_LOGV) {
+ Log.v(TAG, key + " hit " + cacheEntry.hit + " times.");
+ }
+ return cacheEntry.value;
+ }
+ }
+ return null;
+ }
+
+ @UnsupportedAppUsage
+ public V purge(K key) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Trying to purge " + key);
+ }
+
+ CacheEntry<V> v = mCacheMap.remove(key);
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, mCacheMap.size() + " items cached.");
+ }
+
+ return v != null ? v.value : null;
+ }
+
+ @UnsupportedAppUsage
+ public void purgeAll() {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Purging cache, " + mCacheMap.size()
+ + " items dropped.");
+ }
+ mCacheMap.clear();
+ }
+
+ public int size() {
+ return mCacheMap.size();
+ }
+
+ private static class CacheEntry<V> {
+ int hit;
+ V value;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/DownloadDrmHelper.java b/telephony/java/com/google/android/mms/util/DownloadDrmHelper.java
new file mode 100644
index 000000000000..118de465a518
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/DownloadDrmHelper.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmManagerClient;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public class DownloadDrmHelper {
+ private static final String TAG = "DownloadDrmHelper";
+
+ /** The MIME type of special DRM files */
+ public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
+
+ /** The extensions of special DRM files */
+ public static final String EXTENSION_DRM_MESSAGE = ".dm";
+
+ public static final String EXTENSION_INTERNAL_FWDL = ".fl";
+
+ /**
+ * Checks if the Media Type is a DRM Media Type
+ *
+ * @param drmManagerClient A DrmManagerClient
+ * @param mimetype Media Type to check
+ * @return True if the Media Type is DRM else false
+ */
+ public static boolean isDrmMimeType(Context context, String mimetype) {
+ boolean result = false;
+ if (context != null) {
+ try {
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ if (drmClient != null && mimetype != null && mimetype.length() > 0) {
+ result = drmClient.canHandle("", mimetype);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Checks if the Media Type needs to be DRM converted
+ *
+ * @param mimetype Media type of the content
+ * @return True if convert is needed else false
+ */
+ @UnsupportedAppUsage
+ public static boolean isDrmConvertNeeded(String mimetype) {
+ return MIMETYPE_DRM_MESSAGE.equals(mimetype);
+ }
+
+ /**
+ * Modifies the file extension for a DRM Forward Lock file NOTE: This
+ * function shouldn't be called if the file shouldn't be DRM converted
+ */
+ @UnsupportedAppUsage
+ public static String modifyDrmFwLockFileExtension(String filename) {
+ if (filename != null) {
+ int extensionIndex;
+ extensionIndex = filename.lastIndexOf(".");
+ if (extensionIndex != -1) {
+ filename = filename.substring(0, extensionIndex);
+ }
+ filename = filename.concat(EXTENSION_INTERNAL_FWDL);
+ }
+ return filename;
+ }
+
+ /**
+ * Gets the original mime type of DRM protected content.
+ *
+ * @param context The context
+ * @param path Path to the file
+ * @param containingMime The current mime type of the file i.e. the
+ * containing mime type
+ * @return The original mime type of the file if DRM protected else the
+ * currentMime
+ */
+ public static String getOriginalMimeType(Context context, String path, String containingMime) {
+ String result = containingMime;
+ DrmManagerClient drmClient = new DrmManagerClient(context);
+ try {
+ if (drmClient.canHandle(path, null)) {
+ result = drmClient.getOriginalMimeType(path);
+ }
+ } catch (IllegalArgumentException ex) {
+ Log.w(TAG,
+ "Can't get original mime type since path is null or empty string.");
+ } catch (IllegalStateException ex) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ return result;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/DrmConvertSession.java b/telephony/java/com/google/android/mms/util/DrmConvertSession.java
new file mode 100644
index 000000000000..0e8ec91f4ef6
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/DrmConvertSession.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.google.android.mms.util;
+
+import android.content.Context;
+import android.drm.DrmConvertedStatus;
+import android.drm.DrmManagerClient;
+import android.provider.Downloads;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+
+public class DrmConvertSession {
+ private DrmManagerClient mDrmClient;
+ private int mConvertSessionId;
+ private static final String TAG = "DrmConvertSession";
+
+ private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
+ mDrmClient = drmClient;
+ mConvertSessionId = convertSessionId;
+ }
+
+ /**
+ * Start of converting a file.
+ *
+ * @param context The context of the application running the convert session.
+ * @param mimeType Mimetype of content that shall be converted.
+ * @return A convert session or null in case an error occurs.
+ */
+ @UnsupportedAppUsage
+ public static DrmConvertSession open(Context context, String mimeType) {
+ DrmManagerClient drmClient = null;
+ int convertSessionId = -1;
+ if (context != null && mimeType != null && !mimeType.equals("")) {
+ try {
+ drmClient = new DrmManagerClient(context);
+ try {
+ convertSessionId = drmClient.openConvertSession(mimeType);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Conversion of Mimetype: " + mimeType
+ + " is not supported.", e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not access Open DrmFramework.", e);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG,
+ "DrmManagerClient instance could not be created, context is Illegal.");
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "DrmManagerClient didn't initialize properly.");
+ }
+ }
+
+ if (drmClient == null || convertSessionId < 0) {
+ return null;
+ } else {
+ return new DrmConvertSession(drmClient, convertSessionId);
+ }
+ }
+ /**
+ * Convert a buffer of data to protected format.
+ *
+ * @param buffer Buffer filled with data to convert.
+ * @param size The number of bytes that shall be converted.
+ * @return A Buffer filled with converted data, if execution is ok, in all
+ * other case null.
+ */
+ @UnsupportedAppUsage
+ public byte [] convert(byte[] inBuffer, int size) {
+ byte[] result = null;
+ if (inBuffer != null) {
+ DrmConvertedStatus convertedStatus = null;
+ try {
+ if (size != inBuffer.length) {
+ byte[] buf = new byte[size];
+ System.arraycopy(inBuffer, 0, buf, 0, size);
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, buf);
+ } else {
+ convertedStatus = mDrmClient.convertData(mConvertSessionId, inBuffer);
+ }
+
+ if (convertedStatus != null &&
+ convertedStatus.statusCode == DrmConvertedStatus.STATUS_OK &&
+ convertedStatus.convertedData != null) {
+ result = convertedStatus.convertedData;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "Buffer with data to convert is illegal. Convertsession: "
+ + mConvertSessionId, e);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not convert data. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ } else {
+ throw new IllegalArgumentException("Parameter inBuffer is null");
+ }
+ return result;
+ }
+
+ /**
+ * Ends a conversion session of a file.
+ *
+ * @param fileName The filename of the converted file.
+ * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
+ * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
+ * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
+ * occurs when accessing drm framework.
+ * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
+ */
+ @UnsupportedAppUsage
+ public int close(String filename) {
+ DrmConvertedStatus convertedStatus = null;
+ int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+ if (mDrmClient != null && mConvertSessionId >= 0) {
+ try {
+ convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
+ if (convertedStatus == null ||
+ convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
+ convertedStatus.convertedData == null) {
+ result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
+ } else {
+ RandomAccessFile rndAccessFile = null;
+ try {
+ rndAccessFile = new RandomAccessFile(filename, "rw");
+ rndAccessFile.seek(convertedStatus.offset);
+ rndAccessFile.write(convertedStatus.convertedData);
+ result = Downloads.Impl.STATUS_SUCCESS;
+ } catch (FileNotFoundException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "File: " + filename + " could not be found.", e);
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not access File: " + filename + " .", e);
+ } catch (IllegalArgumentException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Could not open file in mode: rw", e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Access to File: " + filename +
+ " was denied denied by SecurityManager.", e);
+ } finally {
+ if (rndAccessFile != null) {
+ try {
+ rndAccessFile.close();
+ } catch (IOException e) {
+ result = Downloads.Impl.STATUS_FILE_ERROR;
+ Log.w(TAG, "Failed to close File:" + filename
+ + ".", e);
+ }
+ }
+ }
+ }
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not close convertsession. Convertsession: " +
+ mConvertSessionId, e);
+ }
+ }
+ return result;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/PduCache.java b/telephony/java/com/google/android/mms/util/PduCache.java
new file mode 100644
index 000000000000..94e38946f632
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/PduCache.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.util;
+
+import android.content.ContentUris;
+import android.content.UriMatcher;
+import android.net.Uri;
+import android.provider.Telephony.Mms;
+import android.util.Log;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+public final class PduCache extends AbstractCache<Uri, PduCacheEntry> {
+ private static final String TAG = "PduCache";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = false;
+
+ private static final int MMS_ALL = 0;
+ private static final int MMS_ALL_ID = 1;
+ private static final int MMS_INBOX = 2;
+ private static final int MMS_INBOX_ID = 3;
+ private static final int MMS_SENT = 4;
+ private static final int MMS_SENT_ID = 5;
+ private static final int MMS_DRAFTS = 6;
+ private static final int MMS_DRAFTS_ID = 7;
+ private static final int MMS_OUTBOX = 8;
+ private static final int MMS_OUTBOX_ID = 9;
+ private static final int MMS_CONVERSATION = 10;
+ private static final int MMS_CONVERSATION_ID = 11;
+
+ private static final UriMatcher URI_MATCHER;
+ private static final HashMap<Integer, Integer> MATCH_TO_MSGBOX_ID_MAP;
+
+ private static PduCache sInstance;
+
+ static {
+ URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
+ URI_MATCHER.addURI("mms", null, MMS_ALL);
+ URI_MATCHER.addURI("mms", "#", MMS_ALL_ID);
+ URI_MATCHER.addURI("mms", "inbox", MMS_INBOX);
+ URI_MATCHER.addURI("mms", "inbox/#", MMS_INBOX_ID);
+ URI_MATCHER.addURI("mms", "sent", MMS_SENT);
+ URI_MATCHER.addURI("mms", "sent/#", MMS_SENT_ID);
+ URI_MATCHER.addURI("mms", "drafts", MMS_DRAFTS);
+ URI_MATCHER.addURI("mms", "drafts/#", MMS_DRAFTS_ID);
+ URI_MATCHER.addURI("mms", "outbox", MMS_OUTBOX);
+ URI_MATCHER.addURI("mms", "outbox/#", MMS_OUTBOX_ID);
+ URI_MATCHER.addURI("mms-sms", "conversations", MMS_CONVERSATION);
+ URI_MATCHER.addURI("mms-sms", "conversations/#", MMS_CONVERSATION_ID);
+
+ MATCH_TO_MSGBOX_ID_MAP = new HashMap<Integer, Integer>();
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_INBOX, Mms.MESSAGE_BOX_INBOX);
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_SENT, Mms.MESSAGE_BOX_SENT);
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_DRAFTS, Mms.MESSAGE_BOX_DRAFTS);
+ MATCH_TO_MSGBOX_ID_MAP.put(MMS_OUTBOX, Mms.MESSAGE_BOX_OUTBOX);
+ }
+
+ private final HashMap<Integer, HashSet<Uri>> mMessageBoxes;
+ private final HashMap<Long, HashSet<Uri>> mThreads;
+ private final HashSet<Uri> mUpdating;
+
+ @UnsupportedAppUsage
+ private PduCache() {
+ mMessageBoxes = new HashMap<Integer, HashSet<Uri>>();
+ mThreads = new HashMap<Long, HashSet<Uri>>();
+ mUpdating = new HashSet<Uri>();
+ }
+
+ @UnsupportedAppUsage
+ synchronized public static final PduCache getInstance() {
+ if (sInstance == null) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Constructing new PduCache instance.");
+ }
+ sInstance = new PduCache();
+ }
+ return sInstance;
+ }
+
+ @Override
+ synchronized public boolean put(Uri uri, PduCacheEntry entry) {
+ int msgBoxId = entry.getMessageBox();
+ HashSet<Uri> msgBox = mMessageBoxes.get(msgBoxId);
+ if (msgBox == null) {
+ msgBox = new HashSet<Uri>();
+ mMessageBoxes.put(msgBoxId, msgBox);
+ }
+
+ long threadId = entry.getThreadId();
+ HashSet<Uri> thread = mThreads.get(threadId);
+ if (thread == null) {
+ thread = new HashSet<Uri>();
+ mThreads.put(threadId, thread);
+ }
+
+ Uri finalKey = normalizeKey(uri);
+ boolean result = super.put(finalKey, entry);
+ if (result) {
+ msgBox.add(finalKey);
+ thread.add(finalKey);
+ }
+ setUpdating(uri, false);
+ return result;
+ }
+
+ synchronized public void setUpdating(Uri uri, boolean updating) {
+ if (updating) {
+ mUpdating.add(uri);
+ } else {
+ mUpdating.remove(uri);
+ }
+ }
+
+ @UnsupportedAppUsage
+ synchronized public boolean isUpdating(Uri uri) {
+ return mUpdating.contains(uri);
+ }
+
+ @Override
+ @UnsupportedAppUsage
+ synchronized public PduCacheEntry purge(Uri uri) {
+ int match = URI_MATCHER.match(uri);
+ switch (match) {
+ case MMS_ALL_ID:
+ return purgeSingleEntry(uri);
+ case MMS_INBOX_ID:
+ case MMS_SENT_ID:
+ case MMS_DRAFTS_ID:
+ case MMS_OUTBOX_ID:
+ String msgId = uri.getLastPathSegment();
+ return purgeSingleEntry(Uri.withAppendedPath(Mms.CONTENT_URI, msgId));
+ // Implicit batch of purge, return null.
+ case MMS_ALL:
+ case MMS_CONVERSATION:
+ purgeAll();
+ return null;
+ case MMS_INBOX:
+ case MMS_SENT:
+ case MMS_DRAFTS:
+ case MMS_OUTBOX:
+ purgeByMessageBox(MATCH_TO_MSGBOX_ID_MAP.get(match));
+ return null;
+ case MMS_CONVERSATION_ID:
+ purgeByThreadId(ContentUris.parseId(uri));
+ return null;
+ default:
+ return null;
+ }
+ }
+
+ private PduCacheEntry purgeSingleEntry(Uri key) {
+ mUpdating.remove(key);
+ PduCacheEntry entry = super.purge(key);
+ if (entry != null) {
+ removeFromThreads(key, entry);
+ removeFromMessageBoxes(key, entry);
+ return entry;
+ }
+ return null;
+ }
+
+ @UnsupportedAppUsage
+ @Override
+ synchronized public void purgeAll() {
+ super.purgeAll();
+
+ mMessageBoxes.clear();
+ mThreads.clear();
+ mUpdating.clear();
+ }
+
+ /**
+ * @param uri The Uri to be normalized.
+ * @return Uri The normalized key of cached entry.
+ */
+ private Uri normalizeKey(Uri uri) {
+ int match = URI_MATCHER.match(uri);
+ Uri normalizedKey = null;
+
+ switch (match) {
+ case MMS_ALL_ID:
+ normalizedKey = uri;
+ break;
+ case MMS_INBOX_ID:
+ case MMS_SENT_ID:
+ case MMS_DRAFTS_ID:
+ case MMS_OUTBOX_ID:
+ String msgId = uri.getLastPathSegment();
+ normalizedKey = Uri.withAppendedPath(Mms.CONTENT_URI, msgId);
+ break;
+ default:
+ return null;
+ }
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, uri + " -> " + normalizedKey);
+ }
+ return normalizedKey;
+ }
+
+ private void purgeByMessageBox(Integer msgBoxId) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Purge cache in message box: " + msgBoxId);
+ }
+
+ if (msgBoxId != null) {
+ HashSet<Uri> msgBox = mMessageBoxes.remove(msgBoxId);
+ if (msgBox != null) {
+ for (Uri key : msgBox) {
+ mUpdating.remove(key);
+ PduCacheEntry entry = super.purge(key);
+ if (entry != null) {
+ removeFromThreads(key, entry);
+ }
+ }
+ }
+ }
+ }
+
+ private void removeFromThreads(Uri key, PduCacheEntry entry) {
+ HashSet<Uri> thread = mThreads.get(entry.getThreadId());
+ if (thread != null) {
+ thread.remove(key);
+ }
+ }
+
+ private void purgeByThreadId(long threadId) {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Purge cache in thread: " + threadId);
+ }
+
+ HashSet<Uri> thread = mThreads.remove(threadId);
+ if (thread != null) {
+ for (Uri key : thread) {
+ mUpdating.remove(key);
+ PduCacheEntry entry = super.purge(key);
+ if (entry != null) {
+ removeFromMessageBoxes(key, entry);
+ }
+ }
+ }
+ }
+
+ private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) {
+ HashSet<Uri> msgBox = mThreads.get(Long.valueOf(entry.getMessageBox()));
+ if (msgBox != null) {
+ msgBox.remove(key);
+ }
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/PduCacheEntry.java b/telephony/java/com/google/android/mms/util/PduCacheEntry.java
new file mode 100644
index 000000000000..1ecd1bf93e7f
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/PduCacheEntry.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.util;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import com.google.android.mms.pdu.GenericPdu;
+
+public final class PduCacheEntry {
+ private final GenericPdu mPdu;
+ private final int mMessageBox;
+ private final long mThreadId;
+
+ @UnsupportedAppUsage
+ public PduCacheEntry(GenericPdu pdu, int msgBox, long threadId) {
+ mPdu = pdu;
+ mMessageBox = msgBox;
+ mThreadId = threadId;
+ }
+
+ @UnsupportedAppUsage
+ public GenericPdu getPdu() {
+ return mPdu;
+ }
+
+ @UnsupportedAppUsage
+ public int getMessageBox() {
+ return mMessageBox;
+ }
+
+ @UnsupportedAppUsage
+ public long getThreadId() {
+ return mThreadId;
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/SqliteWrapper.java b/telephony/java/com/google/android/mms/util/SqliteWrapper.java
new file mode 100644
index 000000000000..2dd1dc11c2a9
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/SqliteWrapper.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008 Esmertec AG.
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.mms.util;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.Toast;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+public final class SqliteWrapper {
+ private static final String TAG = "SqliteWrapper";
+ private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE
+ = "unable to open database file";
+
+ private SqliteWrapper() {
+ // Forbidden being instantiated.
+ }
+
+ // FIXME: It looks like outInfo.lowMemory does not work well as we expected.
+ // after run command: adb shell fillup -p 100, outInfo.lowMemory is still false.
+ private static boolean isLowMemory(Context context) {
+ if (null == context) {
+ return false;
+ }
+
+ ActivityManager am = (ActivityManager)
+ context.getSystemService(Context.ACTIVITY_SERVICE);
+ ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
+ am.getMemoryInfo(outInfo);
+
+ return outInfo.lowMemory;
+ }
+
+ // FIXME: need to optimize this method.
+ private static boolean isLowMemory(SQLiteException e) {
+ return e.getMessage().equals(SQLITE_EXCEPTION_DETAIL_MESSAGE);
+ }
+
+ @UnsupportedAppUsage
+ public static void checkSQLiteException(Context context, SQLiteException e) {
+ if (isLowMemory(e)) {
+ Toast.makeText(context, com.android.internal.R.string.low_memory,
+ Toast.LENGTH_SHORT).show();
+ } else {
+ throw e;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static Cursor query(Context context, ContentResolver resolver, Uri uri,
+ String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ try {
+ return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when query: ", e);
+ checkSQLiteException(context, e);
+ return null;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static boolean requery(Context context, Cursor cursor) {
+ try {
+ return cursor.requery();
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when requery: ", e);
+ checkSQLiteException(context, e);
+ return false;
+ }
+ }
+ @UnsupportedAppUsage
+ public static int update(Context context, ContentResolver resolver, Uri uri,
+ ContentValues values, String where, String[] selectionArgs) {
+ try {
+ return resolver.update(uri, values, where, selectionArgs);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when update: ", e);
+ checkSQLiteException(context, e);
+ return -1;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static int delete(Context context, ContentResolver resolver, Uri uri,
+ String where, String[] selectionArgs) {
+ try {
+ return resolver.delete(uri, where, selectionArgs);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when delete: ", e);
+ checkSQLiteException(context, e);
+ return -1;
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static Uri insert(Context context, ContentResolver resolver,
+ Uri uri, ContentValues values) {
+ try {
+ return resolver.insert(uri, values);
+ } catch (SQLiteException e) {
+ Log.e(TAG, "Catch a SQLiteException when insert: ", e);
+ checkSQLiteException(context, e);
+ return null;
+ }
+ }
+}
diff --git a/telephony/java/com/google/android/mms/util/package.html b/telephony/java/com/google/android/mms/util/package.html
new file mode 100755
index 000000000000..c9f96a66ab3b
--- /dev/null
+++ b/telephony/java/com/google/android/mms/util/package.html
@@ -0,0 +1,5 @@
+<body>
+
+{@hide}
+
+</body>
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 34ac3dcc824f..adc9e2251c0b 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -21,7 +21,7 @@ java_sdk_library {
srcs: [
"src/**/*.java",
- ":framework-srcs",
+ ":framework-all-sources",
],
api_packages: [
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 7ffa5ffc09fe..137fbd671865 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -246,6 +246,36 @@ class ValueBodyPrinter : public ConstValueVisitor {
Printer* printer_;
};
+std::string OverlayablePoliciesToString(OverlayableItem::PolicyFlags policies) {
+ static const std::map<OverlayableItem::PolicyFlags, std::string> kFlagToString = {
+ {OverlayableItem::kPublic, "public"},
+ {OverlayableItem::kSystem, "system"},
+ {OverlayableItem::kVendor, "vendor"},
+ {OverlayableItem::kProduct, "product"},
+ {OverlayableItem::kSignature, "signature"},
+ {OverlayableItem::kOdm, "odm"},
+ {OverlayableItem::kOem, "oem"},
+ };
+ std::string str;
+ for (auto const& policy : kFlagToString) {
+ if ((policies & policy.first) != policy.first) {
+ continue;
+ }
+ if (!str.empty()) {
+ str.append("|");
+ }
+ str.append(policy.second);
+ policies &= ~policy.first;
+ }
+ if (policies != 0) {
+ if (!str.empty()) {
+ str.append("|");
+ }
+ str.append(StringPrintf("0x%08x", policies));
+ }
+ return !str.empty() ? str : "none";
+}
+
} // namespace
void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& options,
@@ -312,6 +342,10 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions&
break;
}
+ if (entry->overlayable_item) {
+ printer->Print(" OVERLAYABLE");
+ }
+
printer->Println();
if (options.show_values) {
@@ -525,4 +559,62 @@ void Debug::DumpXml(const xml::XmlResource& doc, Printer* printer) {
doc.root->Accept(&xml_visitor);
}
+struct DumpOverlayableEntry {
+ std::string overlayable_section;
+ std::string policy_subsection;
+ std::string resource_name;
+};
+
+void Debug::DumpOverlayable(const ResourceTable& table, text::Printer* printer) {
+ std::vector<DumpOverlayableEntry> items;
+ for (const auto& package : table.packages) {
+ for (const auto& type : package->types) {
+ for (const auto& entry : type->entries) {
+ if (entry->overlayable_item) {
+ const auto& overlayable_item = entry->overlayable_item.value();
+ const auto overlayable_section = StringPrintf(R"(name="%s" actor="%s")",
+ overlayable_item.overlayable->name.c_str(),
+ overlayable_item.overlayable->actor.c_str());
+ const auto policy_subsection = StringPrintf(R"(policies="%s")",
+ OverlayablePoliciesToString(overlayable_item.policies).c_str());
+ const auto value =
+ StringPrintf("%s/%s", to_string(type->type).data(), entry->name.c_str());
+ items.push_back(DumpOverlayableEntry{overlayable_section, policy_subsection, value});
+ }
+ }
+ }
+ }
+
+ std::sort(items.begin(), items.end(),
+ [](const DumpOverlayableEntry& a, const DumpOverlayableEntry& b) {
+ if (a.overlayable_section != b.overlayable_section) {
+ return a.overlayable_section < b.overlayable_section;
+ }
+ if (a.policy_subsection != b.policy_subsection) {
+ return a.policy_subsection < b.policy_subsection;
+ }
+ return a.resource_name < b.resource_name;
+ });
+
+ std::string last_overlayable_section;
+ std::string last_policy_subsection;
+ for (const auto& item : items) {
+ if (last_overlayable_section != item.overlayable_section) {
+ printer->Println(item.overlayable_section);
+ last_overlayable_section = item.overlayable_section;
+ }
+ if (last_policy_subsection != item.policy_subsection) {
+ printer->Indent();
+ printer->Println(item.policy_subsection);
+ last_policy_subsection = item.policy_subsection;
+ printer->Undent();
+ }
+ printer->Indent();
+ printer->Indent();
+ printer->Println(item.resource_name);
+ printer->Undent();
+ printer->Undent();
+ }
+}
+
} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index a43197cacf7b..9443d606d7e5 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -39,6 +39,7 @@ struct Debug {
static void DumpHex(const void* data, size_t len);
static void DumpXml(const xml::XmlResource& doc, text::Printer* printer);
static void DumpResStringPool(const android::ResStringPool* pool, text::Printer* printer);
+ static void DumpOverlayable(const ResourceTable& table, text::Printer* printer);
};
} // namespace aapt
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
index 213bdd2372ec..7966ba27ebd8 100644
--- a/tools/aapt2/Main.cpp
+++ b/tools/aapt2/Main.cpp
@@ -177,6 +177,11 @@ int MainImpl(int argc, char** argv) {
return main_command->Execute(args, &std::cerr);
}
+// TODO(b/141312058) stop leaks
+extern "C" const char *__asan_default_options() {
+ return "detect_leaks=0";
+}
+
int main(int argc, char** argv) {
#ifdef _WIN32
LPWSTR* wide_argv = CommandLineToArgvW(GetCommandLineW(), &argc);
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 429aff1ff594..3982d12f6036 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -394,6 +394,17 @@ int DumpXmlTreeCommand::Dump(LoadedApk* apk) {
return 0;
}
+int DumpOverlayableCommand::Dump(LoadedApk* apk) {
+ ResourceTable* table = apk->GetResourceTable();
+ if (!table) {
+ GetDiagnostics()->Error(DiagMessage() << "Failed to retrieve resource table");
+ return 1;
+ }
+
+ Debug::DumpOverlayable(*table, GetPrinter());
+ return 0;
+}
+
const char DumpBadgerCommand::kBadgerData[2925] = {
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index 7ded9bcf8470..cd51f7a7718c 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -240,6 +240,16 @@ class DumpXmlTreeCommand : public DumpApkCommand {
std::vector<std::string> files_;
};
+class DumpOverlayableCommand : public DumpApkCommand {
+ public:
+ explicit DumpOverlayableCommand(text::Printer* printer, IDiagnostics* diag)
+ : DumpApkCommand("overlayable", printer, diag) {
+ SetDescription("Print the <overlayable> resources of an APK.");
+ }
+
+ int Dump(LoadedApk* apk) override;
+};
+
/** The default dump command. Performs no action because a subcommand is required. */
class DumpCommand : public Command {
public:
@@ -255,8 +265,8 @@ class DumpCommand : public Command {
AddOptionalSubcommand(util::make_unique<DumpTableCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlTreeCommand>(printer, diag_));
+ AddOptionalSubcommand(util::make_unique<DumpOverlayableCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpBadgerCommand>(printer), /* hidden */ true);
- // TODO(b/120609160): Add aapt2 overlayable dump command
}
int Action(const std::vector<std::string>& args) override {
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 66dc992d6dc7..075531ce158e 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -750,7 +750,10 @@ public class WifiScanner {
/**
* Enable/Disable wifi scanning.
- *
+ * Note: WifiService calls this after any client interface mode changes (i.e. a new interface
+ * set up or an existing interface torn down)
+ * If there are >= 1 active client interface, invoke setScanningEnabled(true)
+ * If there are 0 active client interface, invoke setScanningEnabled(false)
* {@hide}
*/
@RequiresPermission(Manifest.permission.NETWORK_STACK)